Files
buzz/crates
ss-dev-00 5e2879a72c broker: make the contract's strictness hold at every layer it claims to
Four bug-pass findings on #6742, all one shape: a rule the contract states in
prose but does not enforce at some layer, so the layer above it decides.

**status/code contradictions.** `Indeterminate` carrying a known pre-dispatch
code passed validation, so `execute` returned Ok for a response contradicting
itself: `unauthenticated` means the host refused before running anything — a
known fate — while `indeterminate` means the fate is unknown. A caller reading
the status would reconcile side effects that provably never happened. We already
rejected the mirror case, `outcome_unknown` under `failed`.

Rather than add the reverse one-off check, the pairing is now a closed table.
`BrokerErrorCode::may_be_failed` and `may_be_indeterminate` are exhaustive
matches with no wildcard, so a new code cannot inherit an answer — it forces a
decision at both ends. `indeterminate` admits only `outcome_unknown` and
`internal`; a host fault mid-execution is the one legitimate "I don't know", and
every other code names a fate the host knows. The rationale lives once, in the
`BrokerErrorCode` docs, and `BrokerResponse::validate` consults the predicates
instead of restating them.

**A read page bounded only by the protocol cap.** `ActionOutcome::validate`
never sees the request, so it could only check `MAX_PAGE_LIMIT`: a host could
answer a one-message read with five hundred, within the cap and still an overrun
of what was asked. `validate_for` now applies the request's own number, the one
place both halves are in scope. An absent `limit` is held to the new
`DEFAULT_PAGE_LIMIT` (100) rather than treated as consent to an unbounded page —
the cap is what a host may ever send, the default is what it may send unasked,
so the two constants are deliberately different numbers.

**A response envelope that was silently non-strict.** `#[serde(flatten)]`
disables `deny_unknown_fields` — serde cannot combine them — and the note saying
so concluded strictness was enforced elsewhere. It was not: the envelope accepted
and discarded an unknown top-level key, and accepted an `error` beside a
succeeded outcome or an `outcome` beside a failure, dropping whichever half it
could not represent. Deserialization now routes through a private strict wire
form with an exact key set per status; serialization is untouched, so the wire
format does not change. The request envelope has the same `flatten` and was
checked for the same hole: it does not have one, because `ActionArgs` is
adjacently tagged and contributes exactly `action` and `args`, leaving
`deny_unknown_fields` in force. That is now pinned by test rather than assumed.

**Events could smuggle members past the schema.** `nostr`'s `Event` deserializer
accepts and discards unknown members, so a genuinely signed event could carry an
extra `secretKey` and parse clean — the no-secret rule stopping at the envelope
instead of reaching inside it. `BrokerMessage` now deserializes through a
`deny_unknown_fields` intermediary with exactly the seven canonical NIP-01
members, and the module docs state that the invariant extends inside event
objects.

The exact-key-set table now covers both envelopes and the error payload, not
just args and outcomes: a key set nobody pins is a key set a field can be added
to. A new test parses raw bytes through a transport, because the strict-envelope
and strict-event guards live in `Deserialize` and the typed test double could
never reach them — it hands back a value that was never on a wire.

Each guard was falsified separately against 5116ca2b3, since one passing guard is
no evidence for the others: reverting the limit check, the strict envelope, the
strict event, collapsing `DEFAULT_PAGE_LIMIT` onto the cap, dropping
`deny_unknown_fields` from the request envelope, and adding a field to the
response envelope each fail a different named test.

Reported by ss-bugs-02 on #6742. Addresses the interface half of #6467.

Signed-off-by: ss-dev-00 <a02c4e0850e5e612b4ddf95dbe2f5c56467cf27c6552203bc833ff438fb31971@buzz.block.builderlab.xyz>
Co-authored-by: Bradley Axen <baxen@squareup.com>
Signed-off-by: Bradley Axen <baxen@squareup.com>
2026-08-24 21:16:02 -07:00
..