mirror of
https://github.com/block/buzz.git
synced 2026-10-30 14:01:37 +00:00
docs: correct six facts in the broker spec against the contract code
Six members in the request envelope, not five; requestId is never trimmed and admits no whitespace; cursors admit no spaces; reaction and updatedFields are described as intent plus what is actually checked; the three response examples now use values that parse. Co-authored-by: Bradley Axen <baxen@squareup.com> Signed-off-by: Bradley Axen <baxen@squareup.com> Signed-off-by: ss-core-02 <cca2c695c61d409321106cc399009c2cc674d72cc8900b2999a68fe54577a411@buzz.block.builderlab.xyz>
This commit is contained in:
@@ -121,7 +121,7 @@ enforce. And nothing prevents a host from *holding* keys — that is its job.
|
||||
The invariant is that it cannot *hand them over* through this interface.
|
||||
|
||||
**(I2) Authority comes from the session, never the body.** Axiom B2, restated
|
||||
as a property of the wire: the request envelope has exactly five members
|
||||
as a property of the wire: the request envelope has exactly six members
|
||||
(§Request) and none of them is a requester, owner, scope, relay, or
|
||||
credential. The credential travels in a transport header, opaque to the
|
||||
contract. Ownership of a created agent is implicitly the requester, which is
|
||||
@@ -205,7 +205,9 @@ load-bearing alone.
|
||||
|
||||
### Request
|
||||
|
||||
A request is a JSON object with exactly these members, and no others:
|
||||
A request is a JSON object with exactly these six members, and no others.
|
||||
Throughout this document `…` in an example marks a placeholder for a real
|
||||
value; the three full envelope examples in §Results use values that parse.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -246,11 +248,11 @@ cost of a closed set is that adding an operation is a change to this document
|
||||
— which is the point; it makes a new capability a reviewable change rather
|
||||
than a new use of an existing blank cheque.
|
||||
|
||||
Throughout, string fields other than message content and cursors are trimmed
|
||||
of surrounding whitespace before being frozen into the request. Message
|
||||
content publishes exactly as written. A cursor is opaque and nothing may alter
|
||||
it. Length limits on names, prompts, and similar fields count **characters**;
|
||||
limits on content, cursors, request ids, and storage slugs count **bytes**.
|
||||
Throughout, string fields are trimmed of surrounding whitespace before being
|
||||
frozen into the request, with three exceptions: message content publishes
|
||||
exactly as written; a cursor is opaque and nothing may alter it; and a
|
||||
`requestId` is never trimmed because it may not contain whitespace at all. Length limits on names, prompts, and similar fields count **characters**;
|
||||
limits on content, cursors, and request ids count **bytes**.
|
||||
|
||||
**`channel.read`** — the one read operation. It covers a whole channel, a
|
||||
single thread, or the requester's mention feed, because those differ only by
|
||||
@@ -278,8 +280,8 @@ outcome is a page:
|
||||
`messages` are signed events in the host's declared order (I4). `nextCursor`
|
||||
is present when there is more to read and omitted when there is not; a caller
|
||||
learns to stop from its absence, never by comparing a page length against a
|
||||
limit it may not have set. A cursor is 1–256 bytes of printable ASCII, issued
|
||||
by `H`, validated for shape only, and **never parsed, compared, or
|
||||
limit it may not have set. A cursor is 1–256 bytes of printable ASCII with no
|
||||
spaces, issued by `H`, validated for shape only, and **never parsed, compared, or
|
||||
synthesized** by `A`. This is deliberate: a timestamp cursor cannot page
|
||||
safely when more events than `limit` share one second, and it would commit
|
||||
every future host to one ordering strategy. `H` owns ordering and cursor
|
||||
@@ -305,8 +307,9 @@ job, derived from the parent's own tags.
|
||||
{ "channelId": "…", "targetEventId": "…", "reaction": "👍" }
|
||||
```
|
||||
|
||||
`reaction` is an emoji or a `:shortcode:`, non-empty, at most 66 characters.
|
||||
This is the one **best-effort** operation in version 1: a host may answer it
|
||||
`reaction` is intended to be an emoji or a `:shortcode:`; the contract checks
|
||||
only that it is non-empty and at most 66 characters, and what counts as a
|
||||
reaction beyond that is `H`'s to decide. This is the one **best-effort** operation in version 1: a host may answer it
|
||||
`unsupported` and the agent carries on unharmed (§Results). #6467 asks that
|
||||
non-essential signed housekeeping be skippable so an agent can run where it is
|
||||
unavailable; reactions are that housekeeping.
|
||||
@@ -331,7 +334,8 @@ through the interface rather than compute locally.
|
||||
{ "slug": "mem/slice-c" }
|
||||
```
|
||||
|
||||
`slug` is `core` or a `mem/…` path in the NIP-AE grammar, at most 255 bytes.
|
||||
`slug` is `core` or a `mem/…` path in the NIP-AE grammar, at most 255
|
||||
characters (the grammar is ASCII-only, so bytes and characters coincide).
|
||||
The outcome is addressing material only:
|
||||
|
||||
```json
|
||||
@@ -374,8 +378,9 @@ place to be (I1).
|
||||
`target` names the agent by **exactly one** of `pubkey` or `name`, so a host
|
||||
never has to decide which of two selectors wins. At least one other member
|
||||
must be present. The outcome reports the agent's pubkey, its name after the
|
||||
update, and `updatedFields`: the sorted wire names of the members `H`
|
||||
actually changed.
|
||||
update, and `updatedFields`, in which `H` lists the wire names of the members
|
||||
it actually changed, sorted. The client holds `H` to that only by convention —
|
||||
it checks the name's shape and nothing about the list.
|
||||
|
||||
**`agents.delete`** — remove a managed agent the requester owns. Arguments
|
||||
are the `target` selector alone; the outcome is the removed agent's pubkey and
|
||||
@@ -400,19 +405,20 @@ A response is a JSON object with one of three shapes, discriminated by
|
||||
`status`:
|
||||
|
||||
```json
|
||||
{ "type": "broker_result", "protocolVersion": 1, "requestId": "…",
|
||||
{ "type": "broker_result", "protocolVersion": 1, "requestId": "a9f3c2e1-0b5d-4e8a-9c71-3f2b6d8e4a10",
|
||||
"status": "succeeded", "action": "message.reply",
|
||||
"outcome": { "eventId": "…", "kind": 9, "createdAt": 1787675471 } }
|
||||
"outcome": { "eventId": "5ff391bf2c6d4e8a9b7c1d3e5f7a9b1c3d5e7f9a1b3c5d7e9f1a3b5c7d9e1f3a",
|
||||
"kind": 9, "createdAt": 1787675471 } }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "type": "broker_result", "protocolVersion": 1, "requestId": "…",
|
||||
{ "type": "broker_result", "protocolVersion": 1, "requestId": "a9f3c2e1-0b5d-4e8a-9c71-3f2b6d8e4a10",
|
||||
"status": "failed",
|
||||
"error": { "code": "unauthorized", "message": "agents.create is not permitted for this session" } }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "type": "broker_result", "protocolVersion": 1, "requestId": "…",
|
||||
{ "type": "broker_result", "protocolVersion": 1, "requestId": "a9f3c2e1-0b5d-4e8a-9c71-3f2b6d8e4a10",
|
||||
"status": "indeterminate",
|
||||
"error": { "code": "outcome_unknown", "message": "relay connection dropped after publish was attempted" } }
|
||||
```
|
||||
@@ -535,7 +541,7 @@ to verdicts.
|
||||
|
||||
### [C2] Host conformance — any broker host
|
||||
|
||||
- Accepts only the request envelope in §Request with exactly its five
|
||||
- Accepts only the request envelope in §Request with exactly its six
|
||||
members; rejects an unknown member anywhere, a `null` anywhere, a repeated
|
||||
key, an unknown `protocolVersion`, an unknown action, or an unsupported
|
||||
`actionVersion` — each as `failed` with the matching code.
|
||||
|
||||
Reference in New Issue
Block a user