Expand description
The server side of compact wire keys (docs/design/compact-wire-keys.md
§5.1, proposal C): a tower layer that swaps schema-declared field names
for integer keys on the wire, so the handlers behind it never see them.
§What it does
- Response. When
Acceptlists a compact type (application/vnd.cratestack.compact+cbor,+json,+cbor-seq) andCratestack-Contractnames a digest the layer’sKeyTableshold for the op, a2xxbody is rewritten to integer keys, itsContent-Typebecomes the compact type andVary: Accept, Cratestack-Contractis added. Acbor-seqstream is rewritten item by item as it flows, and the in-band error item (tag 48900) passes through unchanged. Anything else (no compactAccept, an unknown selector, an op the tables lack, a non-2xx,/rpc/batch) is answered exactly as without the layer. - Request. A body sent as a compact
Content-Typeis rewritten back to names and itsContent-Typeto the plain type before dispatch. It needs aCratestack-Contractselector the tables know; otherwise the layer answers415(payload_type_unsupportedover RPC,PAYLOAD_TYPE_UNSUPPORTEDover REST), the same refusal the envelope layer gives an unaccepted payload type. - The compact types in
Acceptare replaced by their plain twins before dispatch, so the handler’s own negotiation (CBOR or JSON, buffered or streamed) is untouched.
§How the op is found
The key is the one cratestack_core::op_keys lists: the RPC op_id
(model.Order.list) read from a trailing /rpc/{op_id}, or on REST
"<METHOD> <route template>" from axum’s MatchedPath. Both are matched
by suffix, so a router nested under any prefix needs no configuration.
§Placement
Generated router() and rpc_router() mount it themselves, inside
their DefaultBodyLimit, so a layer you add around the router
(IdempotencyLayer, RateLimitLayer, EnvelopeLayer) sees the compact
bytes and compact payload types the client sent, and the handlers see
named ones. The layer buffers a compact request, bounded by the limit
it is built with (413 over it, also for the named form it expands to),
and a buffered response, bounded by MAX_RESPONSE_REBUFFER_BYTES.
To use compact keys under a COSE envelope, allow the compact types as
payload types: EnvelopeLayer::builder(..).payload_media_types( ["application/cbor", COMPACT_CBOR], ["application/cbor", COMPACT_CBOR])
(add COMPACT_JSON for JSON clients). A signed client names the type in
Cratestack-Payload-Type / Cratestack-Payload-Accept; the envelope
layer hands this layer the opened payload with that Content-Type and
Accept, and seals the compact answer under the compact type.
+cbor-seq is never sealable, so a signed stream stays buffered CBOR.
An Idempotency-Key retry must repeat the first attempt’s encoding: the
idempotency layer hashes the bytes it sees.
Structs§
- Compact
Layer - The compact-keys layer; see the module docs.
- Compact
Service - The service
CompactLayerwraps a router in.