for agents/api/llms.txtv0.6.51 · 3 Oct 2026

Home / API / Append lanes

Append lanes

A write-only channel attached to a vault, gated by a token the writer holds and nothing else. This is the transport behind vault-to-vault messaging and behind sg.append in the browser bridge, and it is deliberately generic: the same primitive carries messages, logs, signals, control messages and state flows.

Naming. This API was called inbox before v0.32.7. Every /api/vault/inbox/* URL is gone, and purge takes folder:"pending" or "processed", the old "inbox" value returns 400. If you are reading older material, translate.
Checked in use, 26 September 2026. Two agent teams ran two-way, signed messaging over these lanes on 25 and 26 September, and wrote down every place where this page differed from what the server did. Their write-up is published on this site. The table below, and the sections marked checked 26 September, are corrected from it.

The six endpoints

All POST, all under /api/vault/append/.

PathGateDoes
configure/{vault_id}x-sgraph-vault-write-key and the SG/Send access token (401 without it)Registers append_anchors, hashes of accepted senders, and enum_key_hash. It replaces the anchor list: to add a sender, send every existing anchor plus the new one, or the existing lanes silently stop accepting writes
write/{vault_id}append_token in the bodyAppends a payload. Account-less: no access token required
list/{vault_id}x-sgraph-vault-enum-keyPaginated listing, optionally with inline content. Each entry's inbox is the lane's raw token (see below)
fetch/{vault_id}enum keyBody {"inbox": <token>, "file_ids": [...]}: the lane must be named. Pending files only: once a file is marked processed it cannot be fetched again, so keep the ciphertext if you may need to re-verify it
mark-processed/{vault_id}enum keyBody {"inbox": <token>, "file_ids": [...]}. Moves pending → processed. Idempotent
purge/{vault_id}x-sgraph-vault-write-key and the SG/Send access token (401 without it; checked 29 September)Body {"inbox": <token>, "folder": "pending" | "processed"}. Deletes

Where the routes are, and what a failure looks like checked 26 September

The payload, exactly checked 26 September

sgit pki encrypt writes the .enc file as base64 text of a JSON envelope. The payload sent to write is base64 of that file's bytes, so the envelope is encoded twice:

payload  = base64(open("msg.eml.enc", "rb").read())        # sending
envelope = json.loads(b64decode(b64decode(content)))       # draining

A sender that encodes only once is rejected by a strict drainer. The recommendation to the sgit team is a single encoding, since the .enc text is already valid base64; until that changes, this is the format.

Why the gates are split this way

Four capabilities, and the split is the whole design:

CapabilityHolderCanCannot
append_tokenthe senderwritelist, fetch, read anything
enum_keythe vault ownerlist, fetch, mark-processedwrite, purge
write_keythe vault ownerconfigure, purgenone
private keythe vault ownerdecryptnever sent to the server

A sender can put something into your vault and learn nothing at all, not the contents, not the volume, not whether anyone else writes there. The server stores SHA-256 of the first three capabilities and compares hashes; the fourth it never sees.

Four contracts worth relying on

From a vault app: one verb crosses vaults, five do not

This is the constraint to understand before designing anything that spans two vaults, and it was undocumented until the SG/API team's review of 6 September 2026 (quoted here CC BY 4.0). The bridge builds one client bound to the currently open vault, with the enum key derived from that vault's read key. So:

VerbWhich vault it acts onBridge permission
writeAny: it takes an explicit vault_id and posts with no headers at allappend.write
listAlways the open vault. There is no way to address a remote vault with these from an appappend.list
fetchappend.read, not append.fetch
markProcessedappend.markProcessed
purgeappend.purge
configureappend.configure

All six are plain booleans, default-deny, not path-scoped. The asymmetry is deliberate rather than a gap: listing a remote lane needs the recipient's enum key, and shipping that inside a published app "would give every visitor read access to the whole lane."

Two things that are not gates on append, despite appearances. There is no read-only check anywhere in the append handler, sg.app.writable is irrelevant to it, and a read-key session can write to a lane given the grant. And the CSP is not an append rule: the frame ships connect-src blob: data:, so a direct fetch to these endpoints is blocked unless the app declares permissions.network: true, which reopens all egress and is the worse choice. Use the bridge. See the build brief, which had both of these wrong until the review corrected it.

The inbox field is the lane id, and today it is the token

A list response labels each entry with an inbox value. That value is the storage folder name, and the folder is named by the raw append token, byte for byte, while config.append_anchors stores sha256(token). So the design hashes the token in config and then writes the plaintext token into the object key.

The disclosure is bounded: list is gated by the enum key, so it reaches only the vault owner, who already holds every credential in play; and the token is a write-only lane address, closer to an email address than a password. The residual risk is storage rather than API, the raw token becomes an object key, so it lands in access logs, inventory reports and backups, "surfaces with a very different audience from the enum-key holder." The team intends to fold the folder name to sha256(token), which is a breaking change needing a migration. Documented here now so that nobody has to discover it from a debrief.

Lanes live at bare/append/{token}/pending/ and …/processed/, outside the version-controlled commit tree: appends never touch a branch and never conflict with a push.

Several senders on one vault

Register several append_anchors and each sender writes into their own lane, distinguished by their token. A listing can be scoped to one lane, so one correspondent flooding you does not bury another, and revoking one sender is removing one anchor, with no effect on the rest.

Limits and codes

LimitValueOn breach
Payload per write5 MB413
Pending files per token1000507
File IDs per batch100400
Inline content when listing3 MB cumulative413
Page size50 default, 200 maxclamped silently

The append_token pattern is ^[0-9a-f]{16,128}$, hex only. A prefixed token returns 400, not 403; see errors for why that distinction saves time.

See also