You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: upload an attachment, behind its own non-signing command
ADR-0009 decided this tool never pins, and that was wrong. A Kleros v2
maintainer reversed it. ADR-0012 records what replaces it; ADR-0009 is
marked superseded in part rather than deleted, because its research and
its endpoint ranking still stand and this change follows them.
Why the old reasoning failed: "the party that authored the content is
the party that should keep it available" assumes a caller who already
holds a CID. The primary consumer here is an autonomous agent, and for
an agent handed a PDF that assumption is false. Evidence with an
attachment was not filable end to end. The threat ADR-0009 actually
guarded against was narrower than the rule it wrote — a credential and
an HTTP client in the signing path — and that is still removed.
`upload-file` is a separate command, not a flag on submit-evidence.
The decisive reason is that a dry run would have to publish or lie:
fold the upload in, and a run without --broadcast either uploads anyway
— an irreversible public side effect on the path whose whole promise is
that it has none — or simulates a payload whose fileURI is not the one
that would be sent. The CID is not knowable without doing the upload,
so there is no third option. Two lesser reasons: the upload is free,
repeatable and content-addressed while the transaction is none of
those; and two commands are two processes, so the one holding the key
opens no socket but the RPC. ADR-0009 named this shape itself, in the
escape clause it wrote for a decision it did not expect reversed.
Publishing is gated behind --publish, not --broadcast: nothing here is
broadcast to a chain, and reusing the word would put an HTTP publish
behind a flag whose description says "Send the transaction". The gate
exists because content addressed by a CID cannot be withdrawn, and an
agent pointed at the wrong path leaks a document permanently — unlike a
wasted fee, no money undoes it.
Verified against the live endpoint on 2026-09-09, and against the
function source read from the private kleros/court-functions at master.
Four measurements became rules, each marked [service] — a new marker,
deliberately weaker than [live]: this is a service, with no bytecode to
read and no deployment to fingerprint, so it can change with no signal
this repo can detect.
- A 200 is not a success. An empty or absent file part returns 200
with cids: []. Refused locally first; the 2xx-with-no-CID case is
the backstop.
- `operation` must be present and its value is discarded —
operation=banana returns 200, omitting it returns 400. A test
asserts the parameter stays, because nothing else would notice it
going missing and every upload would break.
- The ceiling is on the base64-encoded request, not the file. Found
by bisection: 6,284,972 encoded bytes accepted, 6,285,020 rejected,
leaving ~6.4 KB of the 6 MiB budget spent elsewhere. No fixed
maximum file size is correct, so the CLI serialises the real
multipart body and measures it — a long filename moves the
boundary. Over the limit the edge returns an empty 413.
- The handler reassigns the file on every `data` event, so a body
arriving in more than one chunk would pin its last chunk alone
under a CID entirely valid for the truncated bytes. It does not
fire today only because the handler feeds busboy the whole body in
one write() — incidental to the bug. Verification is therefore on
by default: fetch the CID back, compare, refuse on a mismatch, warn
and never fail when the gateway is merely silent.
Reading those bytes back is not an ADR-0007 breach. The tool already
holds them, compares them and discards the response; nothing read can
reach a payload or change which call is made. A URI the operator hands
the tool is still never dereferenced.
What survives ADR-0009: exactly one credential, because the endpoint is
unauthenticated; no HTTP client in the signing path; no subgraph in the
write plane; and no state where a submission points at content that
never pinned, since ordering removes it. What is overturned: spec/00's
non-goal 4, the scope table's "no HTTP client", the README's feature
table, and the CLAUDE.md invariant.
Costs accepted: a code path whose correctness depends on somebody
else's deployment, on exactly one command. An outage cannot wedge a
write — no write command talks to it — and the ADR-0009 path of pinning
elsewhere and passing --file-uri by hand still works. spec/06 §5 is the
re-measurement procedure; appendix-a §2.1 lists the four things
measurement cannot settle, including who keeps the content pinned.
Closes spec/05 §5 criteria 9 and 10. 329 tests pass, 25 of them new and
all offline behind a fake fetch; the exit-code map's exhaustiveness
check caught the five new codes at compile time, as designed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0 commit comments