X-Monaco-* authentication. Both use the same wallet-authorized Ed25519 session
keys. Deploy server support before opting clients in. Existing clients and
sessions continue to work; legacy authentication is not retired.
What the signature protects
The signature binds the HTTP method, escaped path, raw query, and exact body bytes to a registered session key. Changing an order’s price or quantity, query parameter ordering, percent encoding, or JSON whitespace invalidates it. Use HTTPS: signatures provide integrity and key possession, not confidentiality. The gateway remains trusted to verify signatures, resolve wallet-to-session-key bindings, and enforce authorization. Downstream services do not independently verify the original HTTP signature. This protocol does not make a compromised gateway unable to fabricate internal commands. Host and scheme are not covered; keys must not be reused across independently trusted deployments. These trust boundaries also apply to legacy request signing. Session expiry, revocation, delegated-agent permissions, and account restrictions continue to use the existing shared authentication checks. Register a new session key through the existing wallet flow for rotation, migrate clients, then revoke the old session. WebSocket and gRPC signing are unchanged.Signing profile
This is an application profile of RFC 9421 using RFC 9530 Content-Digest. It is not a general-purpose verifier for arbitrary HTTP signature profiles. Every signed request, including GET and empty-body requests, sends:- The signature label is
monaco; exactly one dictionary member is accepted. The four covered components appear in the order shown.created,keyid, andalgare required; their ordering may vary. Extra components, parameters, signature labels, or digest algorithms are rejected. Structured-field spaces are accepted and signature parameters are serialized canonically. Raw header limits are 1,024 bytes forSignature-Inputand 128 bytes each forSignatureandContent-Digest. createdis an integer Unix timestamp in seconds, within 30 seconds of server time. Legacy timestamps use milliseconds.keyidis the registered 32-byte session public key, encoded as 64 lowercase hexadecimal characters. It is a lookup identifier, not a remote key URL.algis exactlyed25519.@methodis the uppercase request method.@pathis the escaped path as sent.@querystarts with?and retains raw ordering, duplicates, and encoding. Its value is?when the query is absent or empty. Do not decode or sort it.content-digestcovers SHA-256 of the exact body bytes, encoded as a structured byte sequence. Hash the empty byte string when there is no body.
@query values, since divergent canonicalization is the most common
cause of an otherwise-correct signature failing:
GET /api/v1/orderssigns@pathas/api/v1/ordersand@queryas?.GET /api/v1/orders?status=FILLED%2CPARTIALLY_FILLED&pageSize=20signs@queryas?status=FILLED%2CPARTIALLY_FILLED&pageSize=20— exactly the bytes on the wire, neither percent-decoded nor reordered. Sending the same filter unencoded, as?status=FILLED,PARTIALLY_FILLED&pageSize=20, is a different signature base even though the server reads the same filter, so sign the string you actually send.
Signature or Signature-Input selects standard verification; failure
never falls back to legacy authentication.
Content-Digest alone is ordinary HTTP integrity metadata, not a signature
credential. It does not select RFC authentication or override the body hash
verified by legacy authentication.
Freshness, retries, and idempotency
The 30-second clock window limits how long a captured request can be reused. It does not prevent an identical signed request from being accepted again inside that window. Monaco keeps no nonce or distributed replay cache. A fresh signature is also not proof that an operation is new. Use the endpoint’s documented idempotency mechanism where available. Do not blindly retry writes without it. Re-sign a retry if its timestamp has aged out, while retaining the original operation’s idempotency key and payload. Endpoints without idempotency support provide no exactly-once guarantee.TypeScript migration
After server deployment, opt in when constructing the SDK:"legacy". RFC signing covers the final URL and serialized
string body. The SDK manages all signature headers and rejects caller overrides
or unsupported body types. RFC-signed requests disable redirects, including caller
overrides. Public requests keep their existing
authentication. No automatic protocol downgrade occurs on errors.
Rust migration
The Rust REST SDK signs generated session-authenticated operations using the final request URL and serialized body. Existing unsigned constructors keep their signatures. Public operations remain unsigned.RequestSigning::Legacy for a server that has not migrated. Authenticated
clients disable HTTP redirects. The low-level SessionSigner::sign_at helper is
also available for reusable reqwest requests; its caller must preserve the signed
bytes and disable redirects. Streaming bodies are rejected rather than silently
signed as empty.
Deployment sequence
- Deploy the server accepting both protocols and verify signed reads and writes with an existing session key in staging.
- Verify stale timestamps, query/body tampering, revoked sessions, and malformed signatures fail. Confirm public routes bypass auth buffering.
- Release SDKs and opt individual applications in. Keep old clients on legacy
until their migration is verified. Client rollback selects
legacy; do not roll the server back while RFC-only clients still target it. - Retire legacy signing only through a separate coordinated contract change after deployed-client usage is known. No retirement date is set.

