CrateStack HTTP Transport Contract
Status
Proposed target contract. Use this document as the canonical HTTP-wire reference before implementing negotiated multi-codec routing or client fallback behavior. Current implementation is now partially realized:- generated Axum routers can negotiate
application/cborandapplication/json - list-returning procedure routes can also negotiate
application/cbor-seq - route capability metadata is generated publicly and drives handler validation and encoding
application/cbor-seqis not implemented for CRUD/model routes or request bodies- COSE-wrapped transport is still not implemented
Scope
This document covers:- request media type handling
- response negotiation rules
- current and planned media types
- route capability expectations
- error encoding rules
Transport Vocabulary
Terms in this document follow./transport-architecture.md.
- codec means typed value serialization format such as JSON or CBOR
- framing means single-value versus sequence body structure
- envelope means an optional outer wrapper such as future COSE support
Baseline Rules
Requests
- requests with bodies are selected by
Content-Type - requests without bodies do not require
Content-Type - unsupported request media types return
415 Unsupported Media Type
Responses
- response transport is selected from the
Acceptheader when present - if
Acceptis absent, the server chooses its default response transport for that route - if no acceptable response transport is available, the server returns
406 Not Acceptable
Errors
- after response transport selection succeeds, both success and error bodies use the selected response transport
- negotiation failures may return a minimal fallback error body if no response transport can be selected at all
Media Types
Current implemented generated-route media types
application/cborapplication/json
Planned first-class single-value media types
application/jsonapplication/cbor
Planned sequence media type
application/cbor-seq
Future envelope media types
COSE media-type details are intentionally deferred. CrateStack should not imply a COSE wire contract until the outer media type and inner-content signaling rules are documented.Feature Matrix
Current implemented and planned transport features:| Feature | Status | Notes |
|---|---|---|
application/cbor request bodies | Implemented | Generated routes accept CBOR where the route declares request bodies. |
application/json request bodies | Implemented | Generated routes accept JSON where the route declares request bodies. |
application/cbor responses | Implemented | Default generated response media type today. |
application/json responses | Implemented | Negotiated through Accept. |
application/cbor-seq responses | Partially implemented | Only for list-returning generated procedure routes. |
application/cbor-seq request bodies | Not implemented | Explicitly deferred. |
application/cbor-seq on CRUD/model routes | Not implemented | Current rollout is procedure-list only. |
| route capability metadata | Implemented | Exposed as generated per-route constants and ROUTE_TRANSPORTS. |
response decode by actual Content-Type in Rust client | Implemented | Includes JSON and CBOR. |
| generated Rust client sequence decode | Implemented | Buffered decode into Vec<T>. |
| streaming sequence decode API | Not implemented | Current client does not expose incremental stream consumption yet. |
| COSE envelope support | Not implemented | Reserved future seam only. |
Request Contract
Requests without bodies
Examples:GET /productsGET /products/{id}DELETE /products/{id}when the route design carries no request body
Content-Typeis ignored if no body exists unless a future host policy chooses stricter validationAcceptstill controls response negotiation
Requests with bodies
Examples:POST /productsPATCH /products/{id}POST /$procs/publishProduct
Content-Typemust identify one request transport supported by the route- the server decodes the body using the matching transport
- unsupported media types return
415 - malformed bodies return
400 Bad Requestor the existing CrateStack decode error classification
application/jsonapplication/cbor
application/cbor-seq
Response Negotiation Contract
Absent Accept
If the client omits Accept, the server responds with the route’s default response transport.
Recommended default direction:
- prefer CBOR for first-party internal routes unless a service explicitly chooses otherwise
Present Accept
The server should evaluate the Accept header in priority order and choose the best supported response transport for the route.
At minimum the implementation should support:
- exact media-type matches
application/**/*
No acceptable response transport
If none of the route’s supported response transports matchAccept, the server returns 406 Not Acceptable.
Route Capability Contract
Generated routes now publish transport capability metadata publicly through generated Axum constants and a registry. Each route should be able to express:- allowed request media types
- allowed response media types
- default response media type
- whether sequence framing is supported
- whether an envelope is optional, forbidden, or required in a future envelope-aware design
- per-route constants in
cratestack_schema::axum - aggregated registry as
cratestack_schema::axum::ROUTE_TRANSPORTS
RouteTransportDescriptor { name, method, path, capabilities }RouteTransportCapabilities { request_types, response_types, default_response_type, supports_sequence_response }
| Route | Allowed request types | Allowed response types | Default response |
|---|---|---|---|
GET /products/{id} | none | application/cbor, application/json | application/cbor |
POST /products | application/cbor, application/json | application/cbor, application/json | application/cbor |
GET /products | none | application/cbor, application/json, maybe application/cbor-seq | application/cbor |
application/cbor-seq Contract Direction
application/cbor-seq should be introduced carefully because it changes response semantics.
Meaning
- the HTTP body contains multiple top-level CBOR data items
- response decoding is sequence-oriented rather than single-value oriented
Good first uses
- export procedures
- event streams over finite bodies
- large feeds where incremental processing or low-copy buffering matters
Deferred uses
- standard CRUD writes
- standard detail fetches
- blanket list-route replacement without a clear performance or UX reason
Current implementation boundary
- response-only support is implemented
- explicit route opt-in is implemented through generated route capability metadata
- explicit Rust client sequence decoding is implemented for generated list-returning procedures
- request-side
application/cbor-seqis not implemented - model CRUD routes do not expose
application/cbor-seq - current client behavior is buffered sequence decode, not incremental streaming
Error Body Contract
Negotiated errors
When a response transport has been selected successfully:- error payloads must be encoded using that same response transport
Content-Typemust reflect that selected transport
Negotiation failures
Before a response transport has been selected successfully:406and415may use a minimal fallback body format- if the server can still choose a response transport safely, it should prefer doing so for consistency
Client Contract Direction
Client runtimes should align with this HTTP contract. Required direction:- request transport is chosen explicitly when a request body is sent
Acceptcan advertise one or more acceptable response media types- response decoding must key off actual response
Content-Type - sequence responses need explicit decode paths rather than being forced through single-value response helpers
- request body codec defaults to CBOR for first-party internal callers
- response preference order defaults to CBOR first, JSON second where both are supported
- debugging or interoperability callers may choose JSON explicitly
Current Repo Mapping
Current repo reality:cratestack-axumnow validates request and response headers against route transport capabilities rather than only a single router-wide codec assumptioncratestack-client-rustsends a preferredAcceptlist and decodes by actual responseContent-Type- generated Rust client methods for list-returning procedures use explicit sequence-aware decode paths
- generated Axum metadata is publicly inspectable through
cratestack_schema::axum::ROUTE_TRANSPORTS catalog-servicewires generated routes withCodecSet::new(CborCodec, JsonCodec), so its CrateStack-generated routes now support negotiated JSON and CBORcatalog-servicedoes not currently have a list-returning generated procedure that exercisesapplication/cbor-seq
Companion Document
./transport-architecture.md is the architecture source of truth for the codec, framing, and envelope split. Read that file first when deciding where a new concern belongs.