Files
buzz/crates
ss-dev-00 9563c27e99 broker: define the agent-to-broker action contract
An agent that holds no secret key still has to work: read its channels, answer
a mention, react, keep its profile, address its memory, and create the agents it
needs. This is the contract for asking a host to do those things — the interface
half of #6467, with no host, no transport, and no signing.

The open question in that RFC is per-operation versus a single "sign this"
primitive. This answers per-operation. A sign-anything primitive tells the broker
who is asking but not what for, so the only policies it can express are "all" and
"none" — it is a signing oracle with a nicer name. Named operations let a host
serve channel.read while refusing agents.create, and give a later policy or IFC
layer a per-operation surface to attach to. The cost is that a new operation
needs a new variant, which is the point: adding one becomes a reviewable change
to the contract instead of another use of an existing blank cheque. For the same
reason signing, publishing, and NIP-42/44/98 are not actions. They are mechanisms
internal to whoever holds the key, and this contract never names them.

Identity is a pubkey and nothing else. `PubkeyHex` has no counterpart here for
the corresponding secret, every args type is `deny_unknown_fields`, and no
outcome can structurally hold key material — agents.create returns a pubkey and
a handle, never the minted nsec. A test scans the module's own source for
secret-key tokens, because a deserialization test can only reject fields someone
thought to add, while the scanner fails the moment such a type appears in a
signature at all. Both halves were checked against deliberate mutations rather
than assumed.

The envelope carries no requester, owner, or scope. Those come from the session
credential the host authenticated, so authority is never something a body can
assert: a request that could name its own subject would let any caller act as
anyone. Ownership of a created agent follows the same rule — the owner is the
requester, implicitly, which is what makes an agent able to own the agents it
creates while the chain still ends at a human. Bounding that chain's depth needs
resources and policy this contract cannot see, so it stays a host concern.

Reads are actions too, because #6467 asks for a deployment where the host is the
only route to the relay. One `channel.read` covers channel, thread, and
mention-feed scopes: they differ by filter, not by permission, and three names
for one decision would be three policy knobs pretending to be independent. Its
outcome is a projection rather than a signed event, since a caller with no relay
cannot verify signatures anyway — trust here is trust in the host, stated rather
than implied.

`presence.set` and `typing.set` are left out of v1. They are housekeeping a host
may decline regardless, and a closed enum makes adding them purely additive.

Carried over from #6543: the envelope, requestId validation, the three-way
Succeeded/Failed/Indeterminate result, the error codes, the retry-is-identical-
bytes contract, and the agents.* argument shapes. Changed deliberately: pubkeys
are a validated `PubkeyHex` instead of `String`, `capability` is renamed
`action`, `CapabilityFailed` becomes `ActionFailed`, and `Unsupported` is new so
a host can decline a best-effort operation without it reading as a fault. #6543
also lands in this module path; whichever merges second needs a deliberate
reconcile, which I own.

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 18:01:27 -07:00
..