Seven Layer
Authority Fabric

Operations

Submit an operation, receive a signed decision — canonical CBOR on the wire, a nullifier that makes replay a refusal, and no policy anywhere in the transport.

scsa-operationd is the network face of the authority contract. It binds 127.0.0.1:8080 by default and speaks canonical CBOR, not JSON:

scsa-operationd [ADDR:PORT] [STATE_DIR]

Give it a STATE_DIR in any deployment you care about — see the note on nullifier durability below.

Early access. The wire format is specified and stable within a schema version, but schema versions may still advance. Run the service behind your own gateway and authentication.

Why CBOR and not JSON

The bytes on the wire are the same bytes the offline verifier reads and the transparency log commits to. That only works if there is exactly one valid encoding of every object — deterministic CBOR per RFC 8949 §4.2.1: definite lengths, shortest-form integers, sorted map keys, no duplicate keys, no trailing bytes. A decoder that accepted a second encoding of the same object would let an attacker present a signature over bytes the verifier never saw.

The decoder is strict in both directions. Round-trip tests assert that decoding and re-encoding reproduces the exact input bytes, and unknown fields are an error rather than something to skip — a verifier that silently ignores a field cannot claim it saw everything the signer signed.

JSON appears at exactly two read-only endpoints, is labelled an operator convenience in its own payload, and is never the signed representation of anything.

Endpoints

MethodPathInOut
GET/healthzJSON liveness
GET/readyzJSON readiness; 503 if the governance gate did not clear at startup
POST/v1/sessionsJSON session offer
POST/v1/operationscanonical CBOR OperationIntentcanonical CBOR DecisionEnvelope
GET/v1/operations/{intent_digest}canonical CBOR DecisionEnvelope
GET/v1/operations/{intent_digest}/explainJSON, operator only
GET/v1/operations/{intent_digest}/bundlecanonical CBOR EvidenceBundle (audit scope)
GET/v1/operations/{intent_digest}/recipient-bundlecanonical CBOR EvidenceBundle, including seat contributions
GET/v1/nullifiers/{nullifier}canonical CBOR DecisionEnvelope
GET/v1/governanceJSON, operator only

A refusal is a 200

POST /v1/operations returns 200 for every decision, including a refusal. A refusal is signed, journaled, published, and checkable offline years later — it is the product, not an error.

HTTP status codes in the 4xx and 5xx ranges are reserved for failures of transport: a body that is not canonical CBOR, a route that does not exist, a service that is not ready. Mapping a refusal code onto a 4xx would invent a second, unsigned, unloggable refusal vocabulary shadowing the real one — and the two would drift.

The service enforces this structurally rather than by convention. The HTTP handlers cannot name a policy, a world view, a governance assessment, a refusal code, an outcome, a quorum or a constitution; they are handed bytes that are already a decision. A refusal and a completion leave the module through the same line of code.

The flow

POST /v1/sessions

Returns a session offer: the coordinator's identity, the constitution digest it is running under, and the freshness window your intent must fall inside.

POST /v1/operations
Content-Type: application/cbor

The body is a canonical OperationIntent. Its digest is the operation's name for the rest of its life — every later lookup uses it.

The response is a canonical DecisionEnvelope: signed, and already committed to the transparency log if it completed. Read Decisions and refusals for what is inside it, and Offline verification for how to check it without trusting the service that produced it.

Nullifiers: replay is a refusal, not a second effect

Every intent carries a nullifier — a value derived so that the same logical operation cannot be authorized twice. The coordinator reserves it before any contribution is gathered.

Submit the same operation again and you do not get a second effect. You get the original decision envelope, or a refusal carrying NULLIFIER_SPENT (400). GET /v1/nullifiers/{nullifier} looks the decision up directly.

This property depends on the nullifier registry being durable. A restart that freed every nullifier would reopen every replay window the nullifier exists to close, so a real deployment must configure directory-backed storage, not the in-memory default. The restart case is covered by a test that spends a nullifier, restarts the process, and asserts it is still spent.

Single-node durability is durability against process restart. It is not durability against loss of the node's disk, and the service does not pretend otherwise.

Readiness and the governance gate

/readyz is not a liveness check with extra steps. It reports whether the governance gate cleared at startup — whether the compiled policy could be shown to be no weaker than the constitution, and whether the deployment's quorum, diversity and algorithm floors are actually met by the seats present.

If it did not clear, the endpoint returns 503 with the gate's own account of which axis was weakened. A deployment that cannot justify its own configuration does not serve traffic.

On this page