43. Exact HTTP, event, query, federation, and SDK contracts
The API exposes three deliberately different resources: portable canonical records, store admission receipts, and policy-bound projections. A client must be able to tell which one it received from media type, path, schema lock, digest headers and response…
The API exposes three deliberately different resources: portable canonical records, store admission receipts, and policy-bound projections. A client must be able to tell which one it received from media type, path, schema lock, digest headers and response body. The v1.3 contract below supersedes the illustrative combined envelope in Section 14.2 and the v1.2 endpoint sketches wherever they conflict.
Canonical bytes are not an audience view
GET /records/{recordId}/canonical returns the admitted portable bytes only to a principal authorized for that record class and representation. GET /vehicles/{assetId}/profile returns a rebuildable, redacted projection. Their ETags, digests, caching, deletion behavior and authorization are not interchangeable.#43.1 Endpoint matrix and consistency contract
| Method + path | Contract | Consistency / authority |
|---|---|---|
| POST /v1/records:append | admit one RecordCore plus detached proofs | serializable per stream; expected sequence; idempotent scoped key |
| POST /v1/records:appendBatch | atomic one-stream batch; bounded count/bytes | all-or-none under one stream head |
| GET /v1/records/{id}/canonical | exact admitted representation and record digest | authoritative ledger; strong read after receipt checkpoint |
| GET /v1/records/{id}/receipt | admission placement, policy decision and chain position | authoritative ledger; tenant-bound |
| GET /v1/streams/{id}?afterSequence= | ordered canonical record/receipt pairs | monotonic stream sequence; signed continuation cursor |
| POST /v1/telemetry/sessions | declare capture locks and obtain upload capability | policy + quota checkpoint; no segment admitted yet |
| POST /v1/telemetry/segments:reserve | reserve bounded object upload and expected digests | short-lived; one manifest identity |
| POST /v1/telemetry/segments:commit | validate bytes + admit SegmentManifest | idempotent; duplicate/equivocation explicit |
| POST /v1/actions:propose | store inert typed proposal | no external effect |
| POST /v1/actions/{id}:confirm | consume exact challenge response | one-time; authenticated principal and terms digest |
| POST /v1/actions/{id}:dispatch | policy/mandate recheck + connector operation | compare-and-append; may return UNKNOWN_EFFECT |
| POST /v1/auctions/{id}/bids | admit confirmed bid intent | linearizable auction stream |
| POST /v1/auctions/{id}:close | create close fence and result | serializable single winner/no-sale receipt |
| GET /v1/vehicles/{id}/profile | audience and as-of bound projection | checkpoint-bounded, cacheable by projection ETag |
| POST /v1/queries | typed bounded query AST | snapshot checkpoint and policy digest returned |
| POST /v1/exports | asynchronous signed export manifest | checkpoint-pinned, policy filtered and auditable |
Strong consistency is required only where an invariant needs it: stream append, authority epoch, bid admission/close, budget reservation and journal posting. Search, feeds, analytics and public profiles are checkpointed projections and may lag; every response exposes that checkpoint and staleness. A client cannot request strong consistency by hiding a stale projection behind an uncached URL.
#43.2 Append request headers and result
Normative append exchange
POST /v1/records:append HTTP/1.1
Content-Type: application/vnd.rtracer.record+json;version=1.3
Accept: application/vnd.rtracer.ingest-receipt+json;version=1.3
Authorization: DPoP <short-lived-token>
DPoP: <proof bound to method, URL, token and key>
RTracer-Tenant: 5abfa9c8-4ec5-4de9-b786-3b3b1b1d8ed4
RTracer-Stream: 606f60af-fb14-42b4-8677-1ea9a35c2d5b
RTracer-Expected-Sequence: 184
RTracer-Writer-Epoch: 12
Idempotency-Key: 01JAZ...client-random...
Content-Digest: sha-256=:...:
Body = exact RecordCore bytes. Detached proofs are supplied as a bounded
multipart part or by digest-addressed ProofRecord references.
201 Created
Location: /v1/records/019b.../receipt
ETag: "receipt-sha256-..."
RTracer-Record-Digest: sha256:...
RTracer-Receipt-Digest: sha256:...
RTracer-Stream-Sequence: 185
RTracer-Logical-Shard: ledger-03
RTracer-Chain-Epoch: 1
RTracer-Commit-Sequence: 93828011
Retry of same scoped key + same requestDigest returns 200 and original receipt.
Same key + different requestDigest returns 409 RTR-IDEMPOTENCY-EQUIVOCATION.- Request digest covers operation family, canonical body digest and semantic headers that affect the operation. Transport attempt count, TCP identity, trace ID and retry timing never enter the canonical event or record digest.
- A 202 response means admitted asynchronous work, never semantic record acceptance unless it contains a committed IngestReceipt. Upload reservation is not telemetry admission.
- If the connection fails after dispatch, the client queries the scoped idempotency key before retrying. It never changes the key for the same intended effect merely to escape an unknown result.
- Proof verification failure, schema failure, policy denial and storage failure use different stable codes. Safe responses never echo secrets, private identifiers, raw policy or existence of unauthorized resources.
#43.3 Projection, ETag, cursor, and typed query semantics
Reproducible view and confidential pagination contract
ProjectionDescriptor {
projectionKind, projectionVersion, audienceClass, requestingPrincipalId?,
purpose, validAt, knownAt, sourceCheckpointVector,
policyBundleDigest, registryCheckpoint, codeDigest,
locale, unitProfile, privacyTransformRefs[], inputRecordDigestSetDigest
}
projectionDigest = SHA256(domain("PROJECTION", version) ||
canonicalize({descriptor, renderedSemanticObject}))
CursorBindingV1_4 {
cursorProfile, queryDigest, requestingPrincipalId?, audienceClass,
purpose, checkpointVector, projectionDescriptorDigest,
codeDigest, registryCheckpoint, policyBundleDigest,
sortPosition, expiresAt, keyEpoch
}
The client receives authenticated encryption of CursorBindingV1_4 or a random
server-side handle. Plaintext private sort tuples and tenant IDs are prohibited.
ProjectionDescriptor {
projectionKind, projectionVersion, audienceClass, requestingPrincipalId?,
purpose, validAt, knownAt, sourceCheckpointVector,
policyBundleDigest, registryCheckpoint, codeDigest,
locale, unitProfile, privacyTransformRefs[], inputRecordDigestSetDigest
}
projectionDigest = SHA256(
domain("PROJECTION", version) ||
canonicalize({descriptor, renderedSemanticObject})
)
Projection ETag = quoted projectionDigest.
Canonical-record ETag = quoted recordDigest.
They are never compared as equivalent validators.
CursorPayload {
queryDigest, tenantId, principalOrAudienceBinding, purpose,
checkpointVector, sortTuple, pageSize, policyDigest, expiresAt, nonce
}
cursor = base64url(payload) + "." + signature
A cursor is opaque, short-lived, tamper-evident and bound to the exact query,
policy and principal/audience. It is not a free-form database offset.Bounded typed query surface
Query {
from: closed resource family,
where: allOf|anyOf|not|eq|in|range|exists|relationshipPath,
validAt, knownAt, projectionPurpose,
select: allowlisted field paths,
orderBy: stable indexed keys ending in opaque ID,
page: {first: 1..500, after?: signedCursor},
consistency: CHECKPOINTED|AT_LEAST_CHECKPOINT,
minimumCheckpoint?: vector
}
Limits: depth 16; predicates 100; IN values 1,000; relationship depth 5;
wall time 5 s interactive/60 s export; scanned-row/byte budget per tenant.
No arbitrary expression, regex over unindexed private data, user SQL, mutation,
recursive graph query or model-selected query text is accepted.#43.4 Problem details and stable machine codes
RFC 9457-compatible failure object
HTTP/1.1 409 Conflict
Content-Type: application/problem+json
{
"type":"urn:rtracer:problem:stream-sequence-conflict",
"title":"Stream sequence conflict",
"status":409,
"code":"RTR-LEDGER-EXPECTED-SEQUENCE",
"stage":"admission.commit",
"correlationId":"019b...",
"retryable":true,
"safeContext":{"expected":"184","current":"185"}
}
Fields are closed. detail is optional and localized; clients branch on code.
Validation findings carry JSON Pointer, constraint ID and safe rejected-value
class—not the private value. Retryable never overrides idempotency semantics.| Code family | Representative codes | Default HTTP / disposition |
|---|---|---|
| RTR-WIRE | NONCANONICAL, DIGEST-MISMATCH, SCHEMA-LOCK, UNKNOWN-MEMBER | 400/422; reject |
| RTR-AUTHN | TOKEN-BINDING, AUDIENCE, CREDENTIAL-REVOKED, STEP-UP | 401; deny/challenge |
| RTR-AUTHZ | NO-GRANT, EXPLICIT-DENY, STALE-EPOCH, UNSATISFIED-DUTY | 403; deny |
| RTR-LEDGER | EXPECTED-SEQUENCE, WRITER-EPOCH, IDEMPOTENCY-EQUIVOCATION | 409; retry/query/quarantine |
| RTR-TELEM | SEGMENT-EQUIVOCATION, CLOCK, DECOMPRESSION-LIMIT, DESCRIPTOR | 409/422; quarantine/reject |
| RTR-COMP | PORT-INCOMPATIBLE, SOLVER-NONCONVERGENT, EFFECTIVITY-UNKNOWN | 422; false/unknown/error |
| RTR-CONTROL | AUTHORITY-EPOCH, FRAME, ENVELOPE, HANDOVER-TIMEOUT | 409/422; reject/fallback |
| RTR-MARKET | AUCTION-CLOSED, TERMS, BID-INELIGIBLE, CLOSE-CONFLICT | 409/422; reject/reload |
| RTR-MONEY | MANDATE, JOURNAL-UNBALANCED, UNKNOWN-EFFECT, RECONCILIATION | 409/422/202; hold/operator |
| RTR-PRIVACY | PURPOSE, NONINTERFERENCE, RETENTION-CONFLICT, RIGHTS | 403/409; deny/freeze |
#43.5 CloudEvents transport profile and topic catalog
At-least-once event envelope
CloudEvent attributes:
specversion = "1.0"
id = eventId (UUIDv7; stable across delivery retries)
source = urn:rtracer:service:<serviceId>
type = io.rtracer.<domain>.<event>.v1
subject = opaque canonical subject route; never VIN/MAC/plate/person
time = transaction/recorded time, not necessarily validTime
datacontenttype = application/vnd.rtracer.event+json;version=1.3
dataschema = immutable schema URI containing or resolving to digest
tenantid, shardid, shardposition, streamid, streamsequence,
recordid?, recorddigest?, receiptdigest?, correlationid, causationid
data = canonical EventData referencing records/receipts by ID and digest.
transportAttempt, broker partition/offset, delivery time, retry count,
consumer lag and trace propagation are delivery metadata outside EventData.
CloudEvents serialization is a transport envelope, not RecordCore identity.| Topic family | Partition key | Consumer obligation |
|---|---|---|
| record.admitted.v1 | tenantId + logicalShardId + chainEpoch | one event per receipt; preserve commitSequence order; atomically dedupe, mutate projection, and advance checkpoint |
| record.invalidated.v1 | tenantId + affected streamId | invalidate/rebuild descendants by dependency graph |
| telemetry.segment-admitted.v1 | tenantId + captureSessionId | honor manifest locks and gap/correction events |
| configuration.compiled.v1 | tenantId + assetId | discard older config/context result; retain proof receipt |
| action.state-changed.v1 | tenantId + actionId | enforce legal sequence; never infer effect from delivery |
| auction.state-changed.v1 | tenantId + auctionId | preserve stream order; result only from close receipt |
| journal.posted.v1 | tenantId + economicEventId | exactly-once projection by batch ID; never repost |
| publication.changed.v1 | tenantId + publicationId | apply correction/withdrawal lineage and audience policy |
The outbox is authoritative for event production. Broker delivery is at least once. Consumers store event ID plus source checkpoint in the same transaction as their materialized state. Poison messages enter a bounded quarantine lane with alerting; skipping without a recorded gap decision is forbidden.
#43.6 SDK behavioral contract
| SDK layer | Required behavior | Forbidden convenience |
|---|---|---|
| wire | strict builders; exact integers/decimals; canonicalize; digest; local schema/vocabulary locks | map unknown enum to first/default; silently normalize signed data |
| auth | DPoP/mTLS support, audience check, key protection, step-up challenge | long-lived bearer token in app storage/logs |
| append | stable idempotency key, expected sequence, receipt verification, unknown-result query | automatic new key after timeout |
| query | typed AST, signed cursor, checkpoint/staleness exposure, redaction marker types | raw query string or hiding projection checkpoint |
| telemetry | descriptor/session lock, bounded chunking, exact bit patterns, resumable digest-verified upload | unit/frame conversion in raw capture batch |
| actions | proposal/confirmation/dispatch types; legal state parser; reconciliation polling | report timeout as failure or payment success as title |
| events | dedupe, monotonic stream handling, checkpoint atomicity and quarantine | assume global order or exactly-once broker |
| offline edge | local signed journal, monotonic local sequence, pack lock, conflict-preserving sync | last-write-wins over lifecycle/evidence/authority |
Generated SDKs are subordinate to semantic fixtures. Each supported language must reproduce canonical golden bytes/digests, exact UUID grammar, unit/dimension vectors, proof verification, HTTP retries, cursor rejection, event deduplication and error-code exhaustiveness. A generator release is not conforming until its language-specific corpus passes byte-for-byte.
#43.7 Offline synchronization, import/export, and federation
Disconnected capture and portable dossier contract
EdgeJournalEntry {
edgeDeviceId, bootId, localSequence, recordId, canonicalBytes,
recordDigest, proofRefs[], packLock, capturedAtDevice,
priorLocalDigest, localDigest, syncState
}
Sync:
1 authenticate device/workload and authorize exact journal scope
2 compare server acknowledgement vector and local chain
3 upload missing bytes/proofs with stable per-record idempotency keys
4 admit independently; preserve conflicts/equivocation and server receipts
5 append SyncReceipt mapping local sequence to admission disposition
6 never rewrite edge history; append corrections/invalidation
ExportManifest pins tenant, subject selectors, purpose, policy decision,
source checkpoint, schema/vocabulary/pack locks, record and artifact digests,
encryption recipients, redactions, retention/recipient duties and signature.
Import verifies bytes first, then maps external IDs as assertions; it never
assumes imported tenant, ownership, grants or registry status are local truth.- Public ActivityPub objects remain projections. Remote Actor/Note/Follow/Like IDs never become vehicle, principal, grant, bid, appraisal or lifecycle evidence IDs.
- Federated inbox content is quarantined untrusted data. It cannot invoke tools, promote memory, reveal private dossiers or enter the physical-control plane.
- Withdrawal/correction propagates best-effort to federated copies with durable receipts, while the system reports that remote deletion cannot be guaranteed.
- Government, insurer, workshop and event adapters preserve issuer, native identifier/value, fetch/import time, effective time, source artifact and transformation receipt. They do not flatten external claims into owner-authored facts.