mesh: a grant rides over the OpenAI-compatible HTTP binding, and a receipt can be shown back as a witness #312

Merged
dcharlot merged 1 commit from feat/mesh-http-grants into main 2026-10-18 07:20:05 -04:00
Owner

The gap

The grant work (#294, #301, #306) was reachable only through the library:

  • No grants: the reference HTTP server passed no grant to every request.
  • No way to learn the steward: a client couldn't find out which steward identity a grant must name.
  • No witness: there was no way to show a receipt back against the journal.
  • Memory-only journal: the server binaries kept the journal in memory.
  • No body cap: the server read whatever body length a client declared.

The fix

  • Grants over HTTP: a request's body carries its presentation in jcp_presentation (presentation_in).

    • The presentation is signed for a request id, so a granted request is served as exactly the ServeRequest the body alone determines (granted_serve_request / granted_serve_request_responses). A caller can compute the id it signs for.
    • The prompt-token figure stays the body's estimate rather than the server's tokenizer count, which a caller cannot reproduce. That figure is only the router's cost estimate; the energy is metered.
    • A granted body must name its own metadata.joule_ceiling_uj, since a server default would otherwise enter the id unseen.
  • GET /v1/mesh/steward returns the steward's did:key, the subject a grant must name.

  • POST /v1/mesh/witness, with the receipt and envelope_hex a response's usage.energy carried, answers 200 when the journal holds the receipt and 409 when it does not. A 409 means the journal was cut, and grants are refused from then on. Mesh::witness_wire does the check: the envelope must verify under this steward and seal exactly that wire receipt.

  • Statuses (http_status):

    Status When
    401 the caller is not the grant's issuer
    403 the steward refuses the grant
    503 the steward has stopped honouring grants
    500 the journal can't be written
    400 everything else
  • Durable journal in the binaries: with MESH_JOURNAL set, the reference server example and serve_openai_falcon record to that file and replay it before serving, printing any unresolved serve.

  • Body cap: a body declared over 4 MiB is refused with 413 before it is read.

  • Mesh SPEC §2.3 describes the binding (informative).

Checked

On the build host:

  • mesh-rs: 81 tests pass.
  • mesh-openai with the server feature: 15 tests pass, including an end-to-end socket test. It learns the steward, grants to it, presents the grant over HTTP, witnesses the receipt, refuses a tampered witness and refuses an oversize body.
  • Builds: the gguf and openai/server features build, every Cargo.lock is current, and clippy is clean.
  • Mutations: each fails tests:
    • a server that drops the grant
    • a granted path that recounts tokens
    • a wire witness that skips the seal check
    • no body cap
  • Repository checks: spec references, tests-in-CI, vanity metrics and orphan sources pass locally.

Touches mesh/mesh-rs/Cargo.lock and mesh/SPEC.md like #311, but in different entries and sections, so the two merge in either order.

🤖 Generated with Claude Code

## The gap The grant work (#294, #301, #306) was reachable only through the library: - **No grants:** the reference HTTP server passed no grant to every request. - **No way to learn the steward:** a client couldn't find out which steward identity a grant must name. - **No witness:** there was no way to show a receipt back against the journal. - **Memory-only journal:** the server binaries kept the journal in memory. - **No body cap:** the server read whatever body length a client declared. ## The fix - **Grants over HTTP:** a request's body carries its presentation in `jcp_presentation` (`presentation_in`). - The presentation is signed for a request id, so a granted request is served as exactly the `ServeRequest` the **body alone** determines (`granted_serve_request` / `granted_serve_request_responses`). A caller can compute the id it signs for. - The prompt-token figure stays the body's estimate rather than the server's tokenizer count, which a caller cannot reproduce. That figure is only the router's cost estimate; the energy is metered. - A granted body must name its own `metadata.joule_ceiling_uj`, since a server default would otherwise enter the id unseen. - **`GET /v1/mesh/steward`** returns the steward's `did:key`, the subject a grant must name. - **`POST /v1/mesh/witness`**, with the `receipt` and `envelope_hex` a response's `usage.energy` carried, answers 200 when the journal holds the receipt and 409 when it does not. A 409 means the journal was cut, and grants are refused from then on. `Mesh::witness_wire` does the check: the envelope must verify under this steward and seal exactly that wire receipt. - **Statuses** (`http_status`): | Status | When | |---|---| | 401 | the caller is not the grant's issuer | | 403 | the steward refuses the grant | | 503 | the steward has stopped honouring grants | | 500 | the journal can't be written | | 400 | everything else | - **Durable journal in the binaries:** with `MESH_JOURNAL` set, the reference server example and `serve_openai_falcon` record to that file and replay it before serving, printing any unresolved serve. - **Body cap:** a body declared over 4 MiB is refused with 413 before it is read. - **Mesh SPEC §2.3** describes the binding (informative). ## Checked On the build host: - **mesh-rs:** 81 tests pass. - **mesh-openai with the `server` feature:** 15 tests pass, including an end-to-end socket test. It learns the steward, grants to it, presents the grant over HTTP, witnesses the receipt, refuses a tampered witness and refuses an oversize body. - **Builds:** the `gguf` and `openai/server` features build, every `Cargo.lock` is current, and clippy is clean. - **Mutations:** each fails tests: - a server that drops the grant - a granted path that recounts tokens - a wire witness that skips the seal check - no body cap - **Repository checks:** spec references, tests-in-CI, vanity metrics and orphan sources pass locally. Touches `mesh/mesh-rs/Cargo.lock` and `mesh/SPEC.md` like #311, but in different entries and sections, so the two merge in either order. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
mesh: a grant rides over the OpenAI-compatible HTTP binding, and a receipt can be shown back as a witness
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 12s
standards workspaces build + test / macOS energy backends (pull_request) Has been skipped
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 3m11s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 59s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m13s
ec2b3841ee
The grant work (#294, #301, #306) was reachable only through the library.
The reference HTTP server passed no grant to every request, had no way to
learn which steward a grant must name, and no way to witness a receipt
against the journal. Its binaries kept the journal in memory. It also read
whatever request body length a client declared.

- **Grants over HTTP.** A request's body carries its presentation in
  `jcp_presentation` (`presentation_in`). The presentation is signed for a
  request id, so a granted request is served as exactly the `ServeRequest`
  the body alone determines (`granted_serve_request`, plus a
  `_responses` variant). Its prompt-token figure stays the body's estimate
  rather than the server's tokenizer count, which a caller cannot
  reproduce. The body must name its own `metadata.joule_ceiling_uj`,
  since a server default would otherwise enter the id unseen.
- **Endpoints.** `GET /v1/mesh/steward` returns the steward's did:key,
  the subject a grant must name. `POST /v1/mesh/witness`, with the
  `receipt` and `envelope_hex` a response carried, answers 200 when the
  journal holds the receipt and 409 when it does not.
  `Mesh::witness_wire` checks a receipt in that wire form: the envelope
  must verify under this steward and seal exactly that wire receipt.
- **Statuses** (`http_status`): 401 when the caller is not the grant's
  issuer, 403 when the steward refuses the grant, 503 once it has stopped
  honouring grants, 500 for an unwritable journal, 400 otherwise.
- **Journal in the binaries.** With `MESH_JOURNAL` set, the reference
  server example and serve_openai_falcon record to that file and replay
  it before serving, reporting any unresolved serve.
- **Body cap.** A body declared over 4 MiB is refused (413) before it is
  read.
- **Spec.** Mesh SPEC §2.3 describes the binding (informative).

Checked on the build host:
- mesh-rs: 81 tests pass. mesh-openai with the server feature passes 15,
  including an end-to-end socket test: learn the steward, grant to it,
  present over HTTP, witness the receipt, refuse a tampered one, refuse
  an oversize body.
- The gguf and openai server features build, every Cargo.lock is
  current, and clippy is clean.
- Mutations each fail tests: a server that drops the grant, a granted
  path that recounts tokens, a wire witness that skips the seal check,
  and no body cap.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!312
No description provided.