trust-protocol

RLTP Delivery Contract

Real Life Trust Protocol — service contract: Delivery

Abstract

This document specifies how RLTP documents travel between people. The delivery service moves proof-carrying, anchor-encrypted, typed documents from one person to another — eventually, at least once, never silently lost — and tells both sides honestly what it knows: the sender its transport state, the receiver nothing the content does not prove itself, and the sender never what the receiver decided.

Messages are Trust Task documents of private, versioned types under https://real-life.org/trust-tasks/ (ToIP DTGWG Trust Tasks framework 0.4, §6.5 private specifications). This casting registers four types of its own — encounter-bundle, delivery-ack, encounter-credential-delivery, registry-declaration — the RLTP document profile all types share, the sealed envelope they travel in, and the task registry (4.4) through which companion layers register theirs: the membership and access types (registered in their own documents), the introduction and continuity types of the network-visibility layer, and the member-mapping disclosure of the access layer.

Addressing is a triple, never an account (Section 5a): the rkid a sender seals to, one per relationship; a queue locator that is the carrier’s own business and appears in no rule of this contract; and a control principal, derived per (relationship × carrier) by Identity §7a, under which a recipient registers addresses and collects what arrives. Submission needs no sender identity, and abuse is answered with resources rather than accounts. The point is a modest one, stated plainly: a carrier of a relationship knows that relationship — but relationships must not converge into a person at whoever carries them.

Status of This Document

This is an Editor’s Draft with no standing. It is the seventy-ninth casting of the Delivery Contract — the post-convergence sweep after the loop’s criterion was met, finishing the registration addendum to the converged sixty-ninth: the trust-act task types of Network Visibility join the §4.4 registry, and nothing else changes. The document remains as the scope re-cast of 2026-08-27 shaped it: the promise is protocol; the mechanism is carrier policy. What a counterpart can observe at the port line is specified and vector-tested; how a carrier meets it internally is its own affair. The carrier sections (4.4’s role, 5a) are the youngest part of the document and the most likely to change; the sealed envelope, the delivery promises and the disposition machinery (5, 6) have been stable across many castings. Review happens against the converged companion documents; the convergence criterion is a review round with no blocker-level findings. Feedback belongs in the design journal of the private workshop repository; the public mirror is real-life-org/trust-protocol.

1. Introduction (informative)

1.1 Essence and principles

1.2 The user experience this serves (informative)

After A scans and confirms, A’s app shows a waiting state; the arrival acknowledgement dissolves it (“nothing more to do on your side”), and its absence within ack-wait flips A’s screen to the optical presentation of the sent card — the same enactment on another carrier. When B’s counter-credential later arrives, A sees the relation confirmed; B’s own view becomes mutual only when A’s credential reaches B (Encounter 4.2 — every view is local). Section 8 gives both state machines; a lost acknowledgement after B’s commit reconciles through redelivery and duplicate-known, never through a second enactment (6.3).

2. Conventions and Terminology

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals.

The interim securing profile of Encounter 2.3 applies (did:key anchors, Ed25519/X25519 as Multikeys with decoded multicodec verification, eddsa-jcs-2022 embedded proofs, JCS, SHA-256, multibase, RFC3339-UTC-Z timestamps).

Document — a Trust Task document conforming to the RLTP document profile (Section 3). Document digest — the multibase-encoded multihash (Encounter 2.3: emit u, accept u/z, SHA-256) over JCS(document) of the plaintext document; the document’s identity for idempotency and acknowledgement reference, and format-identical to DTGWG digestMultibase values. Sealed envelope — the encrypted form in which a document travels (Section 5). Disposition — the receiver’s classification of a processed envelope (Section 6).

Carrier — a party that holds delivery queues on behalf of recipients: the adapter side of this contract, below the port line, key-blind by construction (Section 5a). Control principal — the carrier-relationship identity of Identity §7a, the identity a recipient presents to one carrier for one relationship. Queue locator — a carrier-local, opaque handle for one queue (5a.1).

Disambiguation, because the word does double duty in ordinary English: where 1.1 says authenticity “has exactly one carrier”, and where 1.2 and 6.3 speak of the carrier switch to the optical leg (Encounter 5.8), the word means “that which carries” and names no party. The role defined here is always the party of Section 5a, and every normative sentence about it points there.

Term Fragment
Document digest #DocumentDigest
Sealed envelope #SealedEnvelope
Disposition #Disposition
Delivery acknowledgement #DeliveryAck
Carrier #Carrier
Control principal #ControlPrincipal
Queue locator #QueueLocator

3. The RLTP Document Profile

RLTP delivery documents are Trust Task documents [TT], target framework version 0.4, under the private task-type rules of TT §6.5. The profile — normative wire form schemas/rltp-delivery-document.schema.json — requires:

Offline schema rule: implementations MUST pre-register every schema of this contract by its $id and MUST NOT resolve any $ref over the network. The shipped schemas/ directory is the complete closure; a validator that cannot resolve a reference from its local registry treats the document as malformed.

Unknown type → the document MUST be rejected with disposition failed(unknown-type); a document that would have an effect is never silently ignored.

4. Registered Task Types

Each type below is a private Trust Task specification with: Type URI, target framework 0.4, payload schema (shipped), proof declaration (per Section 3), and the consistency rules stated here.

4.1 encounter-bundle/0.1

The transmission of the one-scan ceremony (Encounter 5.8): the scanner’s sent card and step credential.

4.2 delivery-ack/0.1

The arrival acknowledgement (DO-1).

4.3 encounter-credential-delivery/0.1

Post-enactment delivery of a step credential: the counter-step of encounter-scan ("counter"), or a standalone credential delivery outside any bundle thread ("deliver").

4.4 The task registry — companion-registered types

Every type under the task-type namespace shares the document profile (Section 3), the sealed envelope (Section 5), and the disposition and acknowledgement rules (Section 6). Beyond the four types this document registers itself, the registry’s members are registered where their semantics live, each as a private Trust Task specification naming its type URI, payload schema, proof declaration, and consistency rules:

The acknowledgement class rule (normative, for every registered type): an acknowledgement MUST NOT be more provable than the payload it acknowledges. For a type whose payload authenticity is a transferable signature, the delivery-ack carries its standard proof. For a type whose payload authenticity is designated-verifier — the trust-act family above, continuity-probe/0.1 and continuity-mapping/0.1, and every future MAC-authenticated type — the delivery-ack’s proof is the MAC form of 4.2, under the arrival tuple’s ack key: one channel-keyed regime for every DV-type ack, independent of the payload’s internal MAC structure. Deniable, receiver-forgeable exactly as the payload is, verifiable by the sender alone. The ack still fires at the defined effect (4.2, §11) — only its proof form follows the payload’s audience class.

The registry-entry form (normative): a registry is a set of entries, one per type, each carrying exactly: the type URI (under the task-type namespace) · the payload schema reference (the shipped schema file the receiver validates against — dispatch resolves through the entry, so a companion schema needs no $id equal to the type URI; §3’s $id rule binds the types this document registers itself) · the proof declaration (proof present/absent and its carrier, per the registering specification) · and optionally operational constants of the role (below). A type absent from a receiver’s registry is unknown-type (6.2), exactly as for this document’s own types; registration creates no authority anywhere — every type’s effects are governed by its own specification, under one global rule no registration can waive: a registered type’s defined effect MUST NOT write replicated state directly. Where an effect touches replicated state, it does so exclusively by handing full entries with their closure to the Replication Contract’s ingest admission (Replication §7, I14) — the admission verdict, not the delivery disposition, decides any replicated effect. A registry entry whose specification defines a direct replicated write is not registrable.

Operational constants and registry-declaration/0.1 (normative): a party serving a role whose specification names a published constant declares it per party — fixed, non-adaptive — and its counterparts MUST hold it before they depend on it. The carrier is this contract’s own task type registry-declaration/0.1: payload { "declaration": { "role": <type URI of the served role>, "revision": <int-string, ≥ 1>, "constants": { <name>: <value string> } } } per schemas/payload-registry-declaration.schema.json; threadId fresh; proof REQUIRED, verifying under the document issuer (the declaring party — §3’s proof rule names this type). Defined effect: durable recording per (issuer, role) under the generic revision rule (the pattern of Visibility §6.4): a higher revision wins; an equal revision with JCS-identical payload is idempotent; an equal revision with a different payload is an equivocation error — reject, keep state; a lower revision is rejected — so a delayed or redelivered older declaration can never roll a value back. A replacement is prospective, never retroactive for a running act. Roles and their constants are named by the registering specification: the role URI is the type URI of the task the party serves as receiver — for the introduction mediator, https://real-life.org/trust-tasks/introduction-request/0.1 — and the registering specification closes which constant names a role admits and their domains; unknown constant names or out-of-domain values reject the declaration. The first registration: ack-delay, a duration in the grammar this section fixes below, PT1S ≤ ack-delay ≤ PT1H (Visibility §8.4). This declaration IS the “task registration entry” Visibility §8.4 publishes from — that entry’s per-party published form; one mechanism, two names. Act binding and revision skew, stated honestly: each side of a running act computes from the declaration it holds — the mediator from its own current value at the act’s arrival, the requester from the highest revision it holds at send. A revision landing between the two is safe by construction: the requester’s early failed is Visibility §8.4’s named role divergence, converged by the retry as a new act; a mediator that raises its ack-delay SHOULD expect such retries until the new declaration reaches its contacts. A party MUST declare identical constants to all counterparts (“per party, fixed”); the declaration is transferably signed for exactly this reason — two counterparts comparing declarations hold attributable proof of an equivocation. The first registered constant is the introduction mediator’s ack-delay (Visibility §8.4): a requester computes its verdict window from the mediator’s declared value and MUST NOT send an introduction-request to a mediator whose declaration it does not hold — asking first is the flow, not an error path.

The carrier role: what a carrier declares, and what it guarantees (normative). A carrier (Section 5a) serves no task type — and its declaration therefore needs a role key that is not one (round-44 B-1): a carrier declares its constants in a registry-declaration/0.1 whose role is byte-equal to https://real-life.org/trust-tasks/delivery-carrier/0.1. That URI names the carrier role, and only the role: used as a document type it stays failed(unknown-type) (Section 11), because a carrier is nobody’s document counterparty. Constants for this role declared under any other role URI are not a carrier declaration, and a counterpart MUST NOT treat them as one — without this rule, two counterparts could hold different entries as authoritative and the first guarantee below would not be byte-decidable. It is below the port line, it is nobody’s document counterparty, and how it manages its own storage, its own rate limiting and its own denial-of-service defence is its business, not this contract’s. Nineteen review rounds were spent specifying that machinery here, and the specification of it never belonged in a protocol: every carrier that runs will do it differently, and none of it is what a counterpart observes.

What a counterpart does observe, and what this contract therefore fixes, is five things.

  1. A carrier publishes the constants a counterpart plans with, in a transferably signed declaration (the form above). For these seven, the declaration binds: a carrier MUST NOT enforce a value stricter or looser than it published, and MUST NOT hold a counterpart to a constant it did not publish. Its remaining private limits reach the port line only as guarantee 2’s retriable refusals — never as a terminal verdict, and never as a silently different value for a published constant (round-44 B-3: an earlier wording read as if no unpublished value could influence any outcome, which guarantees 2 and 3 plainly contradict). The seven are orphan-horizon and give-up-horizon (how long a holder may be away, and how long a deposit lives — 5a.9), challenge-lifetime (how long an issued challenge is good for — 5a.3), queue-floor and max-queue-bytes (guarantee 5), max-binding-tombstones (how many released addresses keep their anti-resurrection record — 5a.3), and status-horizon (below). Every other limit a carrier keeps is unpublished by design: publishing occupancy is publishing occupancy.
  2. Refusals for a carrier’s own limits exist, are marked retriable, and say nothing about any party. The family is closed and split across the two closed sets: on registration, registration-refused(capacity) and refused(admission-resource) (5a.3); on submission, refused(admission-resource), refused(queue-saturated) and refused(capacity) (5a.5) — and which member answers is not the carrier’s choice: the evaluation orders of 5a.3 (r1–r5) and 5a.5 (s1–s6) name it (round-45 B-2: an earlier wording listed two of the five, and a carrier could have read the other three as forbidden or as free variants). A carrier that refuses with a reason about who asked has left this contract, and a carrier that marks any of these terminal has misreported its own state.
  3. No asymmetric operation before the decision to spend on it. A carrier MUST decide whether it will serve a request — against whatever budget it keeps — before performing any randomness, sealing, or asymmetric key operation for it. This is the one resource rule that is protocol-shaped rather than local: 5a.3 has the carrier seal a challenge before it knows anything about the requester, so a carrier that seals first and counts afterwards can be made to do unbounded work by an unauthenticated party. What it counts is its own affair; the order is not.
  4. Traffic for other addresses cannot starve a binding the carrier holds. A request that names an rkid this carrier has a live or closing binding for MUST NOT be refused because requests for unknown addresses have exhausted something — at any step of an evaluation order, r2 and r4 alike (round-44 B-2: an admission meter drained by unknown-address traffic that then refuses a held binding’s rebind at r2 is exactly the starvation this guarantee forbids). Whatever a carrier reserves or meters, it arranges so that this holds; the guarantee is the requirement, the arrangement is its own.
  5. A queue below its declared queue-floor is beyond the reach of every other queue. A submission whose post-admission occupancy stays within the floor MUST NOT be refused for global occupancy (s6) and MUST NOT be refused because traffic for other queues drained a meter (s4) — the same cross-traffic rule as guarantee 4, in the submission direction (round-45 B-1: an earlier wording said its admission “depends on nothing but itself”, which overclaimed — the queue’s own metering may refuse it, retriably, and that is the DO-6 residual: the party spending a queue’s own budget holds its address). Above the floor, a carrier may refuse with capacity like anywhere else. queue-floor ≤ max-queue-bytes and both are published, so a holder knows what it can rely on and what it is merely hoping for.

What this contract deliberately does not answer, and records as open rather than half-building: nothing in it authenticates a depositor, so anyone who learns an rkid may spend a queue’s own budget (Section 12, DO-6). Guarantees 4 and 5 bound what that costs the rest of a carrier; they do not stop it, and no arrangement of local limits will, because the gap is the absence of a sender-side gate rather than the size of any limit.

How the published constants are written, and how they are read (normative). The values above travel as value strings in a signed declaration, so two carriers must not be able to read one declaration differently. Three rules, and none of them is new work:

5. The Sealed Envelope

A document travels sealed to its recipient:

seal = { "rkid":       <recipient key-agreement key, Multikey z6LS…>,
         "epk":        <ephemeral X25519 public key, base64url, 32 bytes>,
         "nonce":      <96-bit nonce, base64url>,
         "ciphertext": <AES-256-GCM ciphertext || 128-bit tag, base64url> }

Normative construction, exactly one way:

A shipped test vector (vectors/seal.json) fixes recipient key, ephemeral key, nonce, plaintext, ciphertext, and document digest; implementations MUST reproduce it byte-for-byte. The vector’s plaintext document is a seal-only sample (type …/seal-vector-sample/0.1, never a wire type): it exercises this section’s construction, not the document profile, and MUST NOT be processed as a delivery document.

No channel authentication is required or assumed of a sender: confidentiality comes from the seal, authenticity from the payloads’ own proofs — signatures or designated-verifier MACs — bound to anchors by the Layer-1 binding rule. This is a decision, not an omission, and 5a.4 states it as one. The recipient side is the exact opposite and always has been: registering, collecting from, and concluding a queue are authorized acts under a control principal, and 5a.3 fixes the proofs they require. The two are not in tension — they are the asymmetry the whole section rests on: anyone may put something into a queue, and exactly one party may take it out.

5a. Addressing, registration, and collection

A carrier holds queues for other people. It never reads a document — the seal of Section 5 sees to that, and no rule of this contract asks a carrier to be trusted with content. What it does see is who registers which addresses and who comes to collect them, and that is a social fact even when every byte is opaque.

This section fixes the identities involved. Its aim is bounded and worth stating before the rules, so that nobody reads more into them: it is not a goal to hide from a carrier the relationship it carries. The carrier of a relationship knows the relationship. The goal is that a person’s relationships do not converge into a person at whoever carries them — that a carrier holding six of someone’s relationships holds six relationships, not a directory of one life.

5a.1 The addressing triple (normative)

Addressing is three identifiers with three different owners, and conflating any two of them is where the previous generation went wrong:

Identifier Owned by Seen by Governed in
rkid the recipient sender and carrier Section 5, 5a.2
queue locator the carrier the carrier, and whoever it hands it to 5a.1 (below the port line)
control principal the recipient the carrier Identity §7a, 5a.3–5a.10

The binding rule (MUST): a principal registers, queries, and collects only the rkids of its own relationship. A carrier MUST NOT expose any operation that answers, for one principal, about another principal’s registrations, and a conforming recipient MUST NOT ask for one. A convenience query returning “every key of this person” is the whole failure this section exists to prevent, wearing a helpful face. What makes this rule enforceable rather than merely stated is 5a.3: the carrier does not take the claim “this is my relationship’s address” on trust, because it cannot check it by computation — it requires it to be proved.

5a.2 The recipient address is per relationship (normative)

An rkid MUST NOT be reused across relationships. This is less a new rule than a rule finally written down: under fresh-always enactment (Encounter §4.4) every relationship-creation act derives its own pair context with its own key-agreement key (Identity §5.2), so encounter relationships already address differently by construction. What this section adds is that the property is required, not incidental: a persona/ or group/ context that shows one card to many counterparts makes those counterparts neighbours of one node at the carrier, and a recipient MUST NOT register such a shared rkid at a carrier as if it were a relationship address.

A recipient MAY hold arbitrarily many rkids (this is the recipient-managed property the contract has always allowed), and Section 5’s key-retention rule applies to every one of them unchanged.

5a.3 Registration proves possession, of both halves (normative)

5a.1 states the binding rule — a principal registers only the rkids of its own relationship — as a duty of honest behaviour. Duty is not enough here: an rkid is a public value. A sender sees it, a carrier sees it on every envelope, and anyone who holds a contact card holds one. If registration were a mere assertion, whoever observed an rkid could register it under a principal of their own — a perfectly well-formed principal, since principals are derived, not issued — and the carrier could not tell the two claims apart, because 7a.5’s first prohibition deliberately removed every computable relation between a principal and the addresses it registers. The consequence is not confidentiality loss (sealing is to the rkid; the attacker decrypts nothing) but queue hijack: collecting, concluding, and thereby discarding another person’s deliveries.

The binding cannot be computed — that is the privacy property — so it MUST be proved:

A carrier MUST NOT bind an rkid to a principal without, in one exchange, a valid proof of possession of the principal’s Ed25519 private key and a valid proof of possession of the rkid’s key-agreement private key. A registration presenting one proof, or neither, is refused.

The proof exchange, byte-precisely. The signed object is a versioned artifact whose v constant is inside the signed bytes, in the shape Access §7.3 already uses:

{ "v": "rltp-carrier-proof/0.3",
  "type": "carrier-registration-proof",
  "purpose": "register",
  "carrier": "did:web:carrier.example",
  "principal": "did:key:z6Mk…",
  "rkid": "z6LS…",
  "generation": 1,
  "principalChallenge": "…43 base64url characters…",
  "addressChallenge": "…43 base64url characters…",
  "sig": "…" }

Outcomes are a closed, deterministic set, in the manner of the Replication Contract’s §7.4 verdicts — a function of the proofs, the carrier’s declared constants, and its held state, and of nothing about the party presenting them:

Verdicts — what a presenter is told:

registered · registered(idempotent) · rebound · served · refused(no-such-queue) · refused(possession-failed) · refused(malformed) · refused(stale-generation) · registration-refused(capacity) (retriable) · refused(admission-resource) (retriable)

Internal transitions — cells with no presenter and no verdict, listed so that the table draws every cell from one closed set and Section 11 can require exactly that:

wind-up begins · obligations discharged + released (one transition, 5a.9) · evicted · cannot arise

And the machine that produces them is one total table. One rkid at one carrier is in exactly one state:

unbound (no binding, no tombstone) · live(g, P) · closing(g, P) (5a.9’s wind-up) · released(t) (no binding; a binding tombstone recording the highest generation t ever accepted). The tombstone store holds exactly the rkids in released.

release is not an input of this table at all: a release happens only inside the deadline transition, so “no release before the deadline” is enforced by the domain rather than by a row (round-43 B-1, round-45 M-2 — a pseudo-row for a non-existent input made the totality proof lie about its domain). The table is entered only by a request that has already passed the possession proofs its operation requires — both halves for register/rebind, the principal proof alone for collect/conclude (5a.3’s collection rule: the same proof, without the sealed half); refused(malformed), refused(admission-resource) and refused(possession-failed) are decided before the state is consulted, in the r1–r5 order — syntax, then admission, then possession — for every operation (collect and conclude run the same order over the proofs they carry), and are therefore not cells; a submission is not an input here either — it runs 5a.5’s own outcome set. g′/P′ are the generation and principal the proof carries. registration-refused(capacity) is a carrier resource answer (4.4 guarantee 2) and may meet any register/rebind request; two rules bound it rather than a formula: a request for a binding the carrier already holds is never refused because traffic for other addresses exhausted something (4.4 guarantee 4), and a return outranks an arrival (5a.9) — at its limits a carrier refuses the registration that would create a new binding before the one that resumes a binding it already holds.

The registration-side checks run in one normative order too (round-43 B-3): r1 syntax and canonical encodings → refused(malformed) · r2 the carrier’s own admission metering → refused(admission-resource) (before any asymmetric work, 4.4 guarantee 3 — and subject to guarantee 4: a request naming an rkid the carrier holds a binding for is not refused here because unknown-address traffic drained the meter, round-44 B-2) · r3 both possession proofs → refused(possession-failed) · r4 capacity, under the two rules above → registration-refused(capacity) · r5 the state table. The first condition that holds names the verdict. An implementation free to pick among simultaneously true refusals would be observably divergent from one that picked differently; this order removes the choice.

state input precondition outcome next state
unbound register / rebind — registered live(g′, P′)
unbound collect / conclude — refused(no-such-queue) (5a.5) unbound
unbound orphan-expiry · deadline · eviction — cannot arise (no binding, no tombstone) unbound
live(g, P) register / rebind g′ > g rebound live(g′, P′)
live(g, P) register / rebind g′ = g, P′ = P registered(idempotent) live(g, P)
live(g, P) register / rebind g′ = g, P′ ≠ P refused(stale-generation) live(g, P)
live(g, P) register / rebind g′ < g refused(stale-generation) live(g, P)
live(g, P) collect / conclude proof under P served (5a.7, 5a.8) live(g, P)
live(g, P) collect / conclude valid, fresh proof under P′ ≠ P refused(no-such-queue) (5a.5 — no queue exists under that principal; the answer reveals nothing about other principals’ queues) live(g, P)
live(g, P) orphan-expiry no collection within orphan-horizon wind-up begins (5a.9) closing(g, P)
live(g, P) deadline · eviction — cannot arise (the wind-up has not begun) live(g, P)
closing(g, P) register / rebind as the four live rows above, verbatim same outcome on rebound / registered(idempotent): live, the wind-up ends (5a.9); on a refusal: closing(g, P)
closing(g, P) collect / conclude proof under the bound principal served — concluding is what closing is for closing(g, P)
closing(g, P) collect / conclude valid, fresh proof under P′ ≠ P refused(no-such-queue) (as in live) closing(g, P)
closing(g, P) orphan-expiry · eviction — cannot arise (the wind-up has begun; only released is evictable) closing(g, P)
closing(g, P) deadline the wind-up deadline arrives (5a.9) obligations discharged and released — one linearized transition: every held deposit’s inherited life has ended at or before this instant, so all are given up, and queue and binding release in the same step; one tombstone created, t := g. A submission or return linearized before this transition is served by the closing rows; one linearized after it meets released(t) released(g)
released(t) register / rebind g′ > t registered live(g′, P′), and the tombstone is consumed
released(t) register / rebind g′ ≤ t refused(stale-generation) released(t)
released(t) collect / conclude — refused(no-such-queue) (5a.5) released(t)
released(t) eviction the store exceeds the declared max-binding-tombstones — a new tombstone at the bound, or a revision that lowered it — and this is the longest-released tombstone (ties by ascending rkid key bytes) evicted — the anti-resurrection guarantee for this rkid ends unbound
released(t) orphan-expiry · deadline — cannot arise (no binding exists) released(t)

Consumption weakens nothing: the only proof that consumes a tombstone is one the tombstone already permits, and an rkid never holds both a binding and a tombstone. rebound names the succession, not a change of person: with g′ > g the recorded generation MUST advance, P′ = P or not. And purpose does not select a cell — register and rebind are distinct in the signed bytes and equivalent in effect, because a holder recovering from a partial loss cannot know which state the carrier is in, and 5a.9’s return path depends on its guess not mattering; what purpose separation buys is transplantation resistance, not intent.

Rebind, and the exact condition under which it exists. A rebind is the ordinary path, not an incident: the holder comes back with a new principal for an address it already registered, because its carrier nonce changed while the address did not (Identity §7a.3, §9.3). Three situations produce it — a carrier entry lost or corrupted, a deliberate rotation of N, and the convergence of two concurrently created nonces in a multi-device register — and all three share the one property the proof rule needs:

A rebind requires the rkid’s private key, so it exists exactly where the pair context survived. That key is the pair context’s key-agreement half (Section 5, Identity §5.2); it does not live in the carrier entry and is not reconstructible from anything a counterpart holds — a counterpart holds the public address. After a total register loss with no state copy there is therefore no rebind at all: the pair contexts are gone with everything else, the sealed challenge cannot be opened by anyone, and the relationship re-addresses instead (5a.9). Which loss took the pair contexts with it is decided in Identity §9.3.

An attacker never holds that key in any of these cases, so no loss on the holder’s side ever becomes an opportunity on the attacker’s. Two rules complete the mechanism:

  1. Possession is the authority; incumbency is not. A live binding does not outrank a fresh valid pair of proofs. Any other rule would let a hijack that once succeeded become permanent, and would leave the honest holder with no way back short of abandoning the address.
  2. A rebind is one durable commit — the linearizable compare-and-swap of Access §7.3’s acceptance rule, for the same reason: verification, the displacement of the old binding, and the installation of the new one either all take effect or none do. Two concurrent valid registrations for one rkid yield one binding, never two, and never a queue with no authorized collector.

The residual this creates, named. A carrier that observes a rebind learns that two principals successively controlled one rkid — a link within one relationship at one carrier, which is precisely what that carrier already carries and is not a join across relationships (the same argument Identity §7a.3 makes for one principal across a relationship chain). A holder who does not want even that link rotates the rkid as well — the ordinary Section 5 tombstone path, one card exchange. And a rebind is only ever available to a party that can open a challenge sealed to the address, so the rule buys the honest holder a return path without buying an attacker anything.

Collection is the same proof, without the sealed half. Every collection and every conclusion (5a.8) under a principal MUST be authorized by a fresh proof of possession of that principal’s key — a signature over a carrier-issued challenge, Access §7.3 again. A carrier MUST NOT grant standing to a bearer token that outlives the session, and MUST NOT accept a collection for one principal inside a session authorized for another.

Three things have been called “session” here, and they are kept apart:

Term What it is Rule
Authorization session the scope of one proof of possession: what a carrier has been shown the right to act on MUST carry exactly one principal. A collection or conclusion for a second principal inside it is nonconformant, full stop.
Mediation connection the neighbouring protocol’s long-lived relationship (a DIDComm connection, a TSP relationship) MUST be one per principal (5a.10).
Transport socket TCP, TLS, WebSocket, an HTTP connection below the port line; it carries whatever the transport carries, and this contract says nothing about it.

The MUST of the first row is what this section owns. What 5a.7 discusses is none of the three: it is the timing of separate authorization sessions, and that is a SHOULD because it is a scheduling cost, not an authorization question.

5a.4 Submission is principal-free (normative)

This contract carries no submitter identifier. A submission consists of an rkid, a size, and the sealed bytes — that is the whole of what this contract puts on the wire — and no rule of it requires a sender principal, a sender account, or channel authentication of the submitter. No field at or above this contract’s port line identifies a submitter or links two submissions to one.

What the transport underneath shows, stated rather than promised away. An earlier casting said categorically that a sender presents no identity to a carrier of any kind, and the transport this stack targets contradicts it: TSP authenticates the outer VID pair of a direct relationship to each neighbour, so a carrier reached over TSP sees an authenticated transport identifier of the party delivering to it (5a.10 fixes which one it may be). The categorical sentence is withdrawn, and the honest statement has two halves:

This contract contributes no submitter identifier, and the transport identifier beneath it MUST be scoped per carrier relationship — exclusive to that relationship, never person-wide, never reused across carriers (5a.10). A carrier can therefore count and rate one transport peer’s submissions; what it cannot do, from any protocol field at either layer, is name the person behind them or join them to that peer’s relationships elsewhere.

That is the boundary TSP itself draws — the outer VID of a direct relationship is visible to that neighbour by design; it is the nested inner VID that is private, and 5a.10 forbids mapping the control principal onto the outer one — and this contract does not claim to be more anonymous than the transport it rides.

Principal-free submission removes the identifier; it does not remove the observation. A carrier still sees, and this contract does not pretend otherwise: the network layer (source addresses, connection reuse, TLS session resumption — without network anonymity, a carrier that wants a submitter identity has one and needed no protocol field for it); timing and volume; ack pairing (a carrier holding both directions pairs two rkids by response time with no identity whatsoever); and collection patterns (5a.7, where the residual is priced). Unlinkability is a property of a mix network — cover traffic, randomized delay, constant-rate collection, in the manner of the Loopix line of work — and this contract implements none of it and claims none of it (Section 10 restates the same boundary from the privacy side; the two MUST NOT drift apart).

This is a decision with a price, and the price is named: there is no sender quota, no sender ban, and no negotiated permission to send. Defence is resource-shaped (5a.5), and a carrier that answers abuse by identifying senders has not hardened this contract; it has replaced it.

The neighbours resolve this the same way: a DIDComm mediator does not authenticate the sender at all and draws its permission from the recipient’s standing connection, and TSP addresses a carrier under a per-relationship VID rather than a person. Both authorize relationship-wise, not person-wise.

5a.5 Admission: what a carrier may check, and what it never may (normative)

This section fixes what a carrier is allowed to look at when a submission arrives, and closes the outcome set — otherwise “principal-free submission” is a prohibition with no positive story, and an implementer fills the silence with the first thing that works, which is a sender identity.

A carrier MAY consider exactly these, and MUST consider nothing else:

  1. Whether the rkid names a queue it holds — live or closing per 5a.3, or tombstoned per Section 5. This is a lookup on the value the envelope carries, not a judgement about anyone.
  2. The envelope’s shape and size against Section 5 and its declared max-queue-bytes.
  3. Its own resource state, at a metering scale of per submission or per queue — never per person, and never per inferred submitter.
  4. Its own held state: the storage already occupied by that queue, and its overall capacity.

A carrier MUST NOT consider, require, or record as an input to this decision: any sender identity, account, credential, invitation, device certificate or attestation; any channel authentication of the submitter; any counter, quota, or reputation keyed to a submitter rather than to a submission or a queue; or any correlation of two submissions to one submitter. The last one is a rule about inputs: a carrier inevitably observes addresses and timing (5a.4), and this contract does not pretend to prevent that — but it MUST NOT turn such an observation into an admission rule, because a rule keyed to an inferred submitter is a sender account under another name.

The outcome set is closed, with retriability marked because the sender’s behaviour depends on it:

admitted · duplicate · refused(no-such-queue) · refused(bounds) · refused(admission-resource) (retriable) · refused(queue-saturated) (retriable) · refused(capacity) (retriable)

And the checks run in one normative order, so that overlapping conditions name one verdict rather than an implementation’s choice of several (round-43 B-3 — two carriers answering queue-saturated and capacity to the same submission are observably divergent, and no internal bookkeeping is needed to prevent it, only a port rule):

s1 queue lookup → refused(no-such-queue) · s2 shape and size → refused(bounds) · s3 byte-identity → duplicate · s4 the carrier’s own admission metering → refused(admission-resource) · s5 the queue’s own bound → refused(queue-saturated) · s6 global occupancy → refused(capacity) · otherwise admitted.

The first condition that holds names the verdict, later ones are not consulted, and the order is from the most specific to the most global — a sender learns the most actionable true thing: that the address is wrong before that the queue is full, that the queue is full before that the carrier is.

The residual, stated at full strength. Because admission is resource-shaped and the address is public within the relationship, anyone who holds an rkid can spend that queue’s budget — sealed garbage is indistinguishable from sealed content to a key-blind carrier. The queue fills, legitimate submissions meet refused(queue-saturated), and per-queue metering means the flooder and the honest counterpart share one budget: the attack is a denial of service against one relationship, never the person — 4.4’s guarantees 4 and 5 are what hold that boundary, since below its floor a queue’s admission is beyond the reach of any other queue.

And the reason no resource parameter closes it is the whole architecture in one sentence: telling the flooder apart from the honest sender is sender identification. Every resource-shaped lever changes what the attack costs and never who is allowed. So the defence is not prevention but a cheap, terminating cure, and the contract owes that path precisely:

Rotation is the named healing path (normative). A recipient whose queue is under flood SHOULD rotate the rkid of that relationship: it publishes a fresh key-agreement key to its counterpart in a new contact card over the existing relationship channel, and retires the flooded one by the ordinary Section 5 tombstone rule. The old queue winds up by 5a.9, nothing admitted is silently lost, and the counterpart’s outbound path is uninterrupted, because it holds the new card before the old address dies (key-retention, Section 5).

Who is left, once rotation is in place. An rkid travels only inside the relationship it addresses, so the flood needs the address, and there are exactly two ways to hold it:

A second residual of the same family: door starvation. Nothing here authenticates a requester before the registration exchange, so an identity-free flood can keep new onboarding at one carrier — and the return of an address whose binding has already been released — starved indefinitely. What it cannot reach is any binding the carrier still holds: 4.4’s guarantee 4 puts every live and closing binding’s service beyond traffic for unknown addresses, so device restore, nonce rotation and the rebind of any relationship whose address the attacker does not know cannot be starved at all. The post-release return is not time-critical — a tombstone does not expire — and the deployment answer is the plural one this design assumes: a person’s relationships are not all at one carrier, and a starved door is a reason to register at another, never a reason to lose a relationship. This is the DO-6 family: bounded in blast radius, priced rather than prevented, re-evaluated on evidence of operational abuse rather than on possibility.

The road not taken, named rather than omitted. A recipient-issued admission token — a secret capability handed to accepted counterparts, presented with each submission, revoked by rotation — would close exactly the part of this residual that belongs to parties the recipient never accepted. It would close nothing else: an accepted insider holds the token by design (that is what accepting means — Signal’s sealed-sender delivery token has precisely this shape and this boundary), and door starvation is not a submission question at all. The editor’s decision for the 0.x line is: not now, and remembered (Section 12, DO-6). If it is ever adopted, it belongs in a casting of its own, and the property it must preserve is stated in advance: the token MUST be per (relationship × carrier), like the principal beside it, or it is a person-wide credential with a friendly name.

5a.6 Two roles, one process, never one principal (normative)

A party may run both the storage a person recovers from and the queues a person’s relationships are delivered to. The deployed previous-generation relay does exactly that in one process. The port line between the two roles MUST therefore also be an identity line:

A party MUST NOT present, to the same carrier, one identity for both the storage-entry role (the recovery context, Identity §5.3) and any delivery-collection role (a control principal, Identity §7a). The two MUST be distinct identities even where one process, one operator, and one endpoint serve both.

The reason is not tidiness. The recovery context is, by construction, the one identity of a person that is derivable with no register at all — it must be, or recovery could not start. It is therefore the closest thing a person has to a person-wide constant. A service that saw a person’s delivery principals and their recovery context on one connection would join every one of those relationships to that constant: not to a pseudonym, but to the root of the person’s own storage. That is the strongest join available anywhere in this stack, and it is available for free to any service that is asked to be both things at once.

Two consequences, stated so that implementers do not have to derive them: a device MUST NOT reuse a storage session for collection or the reverse, and a carrier MUST NOT offer, and a recipient MUST NOT accept, any “link your storage account” convenience that establishes such a binding.

What this MUST proves — and what it does not, which is the part that must not be overstated. The rule is written as an identity and interface rule precisely because that is the part that is checkable: the two identities are distinct values, the sessions are distinct sessions, and no operation of either role answers about the other. A conformance run can decide all three from what crosses the interface, and 5a.3’s session rule makes the last one operative. What no rule reachable from here can decide is operator conduct behind the interface: one company running both roles can join a recovery context and a set of control principals through source addresses, timing, device telemetry, billing, or an internal account, and it needs no protocol field to do it. That is a named residual, not a MUST — a MUST that a conformance run cannot decide is a claim, not a requirement, and this document would rather carry the residual honestly (Section 10 restates it from the privacy side). What the rule therefore buys, exactly: it removes the free join — the one that needs no analysis, only an equality check on two identifiers presented on one connection — and it makes the remaining join a matter of deployment trust, which a person can at least choose against by choosing two operators. A recipient that wants the residual closed rather than named uses a different party for storage and for delivery, and this document RECOMMENDS exactly that.

Migrated identities are the honest exception. For an identity migrated from the previous generation, the historic recovery context already carries social attachments (Identity §10), and its own storage is already bound to the person. The separation of this section therefore works prospectively for them — new principals are separate from the first day — and cannot undo what the deployed generation already showed its relay.

5a.7 Collection discipline (normative, SHOULD)

This section is about timing, and only about timing. Serving two principals inside one authorization session is already forbidden by 5a.3’s MUST, and sharing one mediation connection between them is forbidden by 5a.10; neither is discussed here. The join this section addresses is the one that survives all of those rules being kept: a device that opens two perfectly separate, perfectly authorized sessions three seconds apart, every time, hands the carrier the same grouping through their shape in time. No derivation can prevent that: it is scheduling, not cryptography.

A recipient SHOULD NOT correlate its collection times across principals more tightly than its traffic already requires — and an implementation that claims this discipline MUST declare the policy by which it does so: its minimum spacing between collection sessions for different principals, the range of the randomization it applies to that spacing, and whether it multiplexes two principals’ mediation relationships over one persistent transport (5a.10’s SHOULD, which lands here because a shared transport is a co-timing decision and nothing else).

The declaration is what makes a SHOULD checkable, which is the half an earlier casting was missing: “more tightly than its traffic already requires” can excuse any burst, so a conformance run had nothing to test and the rule was decorative. It is deliberately not a fixed number in this contract — a phone on a metered link and a desktop on mains power have honestly different answers, and a value invented here would be wrong for both. What is required is that the answer be stated and then kept: a run can check the declared spacing against observed behaviour, and a party that declares PT0S has said plainly that it does not take this discipline, which is information rather than silence.

It stays a SHOULD for a reason this document would rather write down than pretend away: separate, spaced sessions cost connections, battery, and latency, and on a mobile device that cost is real and recurring. A MUST that implementations cannot afford is a MUST that gets quietly worked around, and a specification that carries such a rule is less honest, not more. And the honest ceiling is named too: spacing and jitter raise the cost of the correlation, they do not remove it. Removing it is mixnet terrain — cover traffic, Poisson delays, constant-rate pulls, the Loopix line of work — and this contract implements none of that and claims none of it (5a.4, §10).

Several devices, one principal (informative). Under the shared-seed device model of Identity §3.2 every device of a person derives the same principal for the same (relationship, carrier). A carrier therefore sees one principal collected from several sessions, which reveals device multiplicity for that one relationship and nothing across relationships. This is DO-3’s question in the identity dimension, and it is answered the same way: the principal is relationship-scoped, not device-scoped, and whatever a future device model brings must keep it so.

5a.8 Conclusion: nothing admitted ends silently (normative)

The Abstract’s promise — “eventually, at least once, never silently lost” — has, until this casting, had no counterpart on the carrier side of the interface. Two holes followed from that, and the field record found both:

Both are closed here, in one rule and its two directions.

The receiver’s duty (MUST). A recipient that has reached a terminal disposition for a collected document MUST conclude it toward the carrier, under the principal that collected it, and a carrier MUST offer that operation and MUST NOT redeliver what has been concluded. Terminal means every 6.2 outcome that repetition cannot change: the failure dispositions of stages 1–8, a stage-9 outcome that completed, and duplicate-known. A disposition that is transient — the device could not complete the critical section, storage was unavailable, the lock set was not obtained — is expressly not terminal, is not concluded, and is redelivered; that distinction is what keeps this rule from turning a local fault into a silent loss.

A conclusion carries no reason. It says “this digest is decided”, and nothing else — not the disposition, not the stage, not whether the document was accepted or rejected. The carrier is key-blind and stays verdict-blind, and 6.1’s rule that the sender never learns what the receiver decided is untouched: the sender’s view of a concluded-but-rejected document is accepted and, absent an acknowledgement, nothing more.

The carrier’s duty (MUST). A carrier MUST NOT discard an admitted deposit without first concluding it toward the sender path. Concretely: a deposit that reaches the carrier’s declared give-up-horizon (4.4) without being collected and concluded is given up — the carrier stops offering it and the sender’s status becomes failed(expired-by-adapter-policy), the reason 6.1 has always carried for exactly this — and only then may its storage be released. There is no other way for an admitted deposit to leave a queue: it is collected and concluded, or it is given up. A carrier that silently drops an admitted deposit is nonconformant, and no other constant of 4.4 — the orphan-horizon included (5a.9) — creates an exception.

Why the sender path can be served at all here, since the carrier knows no sender: it does not need to. failed(...) is a statement the sender’s own adapter makes about a submission it is tracking, from the carrier’s refusal to keep offering it; the carrier concludes the deposit, not the person. That is the same asymmetry 6.1 already relies on, written down.

5a.9 Loss, re-registration, and orphans (normative)

The carrier nonce of a relationship lives in the holder’s register (Identity §7a.3), and its recovery is the recovery of the register itself — which this stack does not get from the Replication Contract’s group rebind (that machinery rebinds a stable group identity and presupposes held or recovered group and member identity; it defines no path to a person’s own register). It gets it from the storage side of the S-DID cut, and Identity §9.3 states it conditionally, which is the honest form and is adopted here verbatim in substance: the recovery context of Identity §5.3 is derivable with no register at all, and with any state copy it unlocks, the register returns and every principal re-derives, with no carrier involved. Ordinary device loss is that case. The storage contract that makes the state copy exist is a named, still unwritten external prerequisite (Identity §6.3), satisfied in fact by today’s encrypted vault rather than by a referenceable specification — so this section claims recovery exactly where §9.3 does, and not one sentence further.

Two losses, and only one of them leaves a way back. Which one happened is decided in Identity §9.3, and this contract follows it without adding anything:

The wind-up runs to a deadline, and the deadline does not move (normative). A queue nobody collects would grow without end, so a binding whose orphan-horizon passes with no collection enters closing and is released when it is done. What makes that terminate is one absolute instant, fixed when closing begins:

wind-up deadline := the instant closing begins + give-up-horizon

A deposit admitted during closing does not start a fresh give-up life. It inherits the remaining time to that deadline. So the newest deposit the queue can hold expires no later than every other one, and the bound follows from the construction rather than from comparing two durations.

And the deadline is the release — one transition, not two (round-43 B-1). An earlier draft of this casting split the deadline into a give-up sweep and a separate release, and the split was a hole: between the two, an admission could arrive undisposed and 5a.8 would forbid the release it was already due — a flooder could repeat that forever, and a return in the gap could void a deadline that had already arrived. So the arrival of the deadline is the linearized transition (4.4’s atomicity rule): everything held is given up — every inherited life has ended at or before this instant by construction — and queue and binding release in the same step. There is no release before the deadline: an empty closing queue simply waits, which costs nothing, and the early release was the race in another place. A submission or return linearized before the transition is served by the closing rows; one linearized after it meets released(t) — the return then costs the tombstone path’s two exchanges instead of ending the wind-up, and the boundary between the two is exact rather than raced.

Admission therefore stays open, and this is a deliberate reversal. Earlier castings closed admission at the instant closing began, and answered every further submission with refused(queue-closed) — a verdict this contract no longer has. Closing admission was never the point; terminating was, and a fixed deadline terminates without refusing anyone. What closing admission cost is easy to name and was paid for several castings: a deposit that arrived while the wind-up ran was lost even when the holder came back the next day, and a sender was told an address does not receive when it was about to receive again. Both are gone. A carrier keeps taking bytes for a closing queue exactly as it does for a live one, and it may refuse for capacity there exactly as it does anywhere else.

And a closing queue is now indistinguishable from a live one at the port line, which is a stronger privacy property than the one the withdrawn verdict was defending: there is no longer any answer that separates a wind-up is running from this address is fine, because there is no longer any separate answer at all.

A holder who comes back reopens it — 5a.3’s outcome table, closing × register/rebind. Possession is the authority here as everywhere, and a person who recovers a device on day 91 must not lose a relationship to a bookkeeping state. A return outranks an arrival: when a carrier is at its limits it refuses the registration that would create a new binding before it refuses the one that resumes a binding it already holds. A full service stops taking new work; it does not shed work it has already accepted, and the asymmetry of harm is the argument — a refused newcomer registers later or elsewhere and loses nothing, a refused returner loses a channel that exists and the deposits sitting in it. This cannot be gamed: the cell is reachable only by both possession proofs for an address the carrier already holds.

And the return ends the wind-up, deadline included. With the rebound or registered(idempotent) the binding is live again, the wind-up deadline is void, and every deposit still held reverts to its own admission-dated give-up-horizon — including those admitted during closing, whose inherited short life existed only to make an unattended wind-up terminate. The inherited deadline was never a property of the deposit; it was a property of the wind-up, and the wind-up is over. (Without this rule a deposit admitted a minute before the holder’s return would expire almost immediately in a queue that is being actively collected, which serves nobody and protects nothing.)

What was concluded stays concluded; what is still held is collectable again, including whatever arrived while the wind-up ran.

A carrier that discards a binding together with undisposed admitted deposits has violated 5a.8, whatever its horizons say.

Senders are unaffected in their contract: an envelope to a retired address fails through the ordinary path (Section 5’s tombstone rule, §6.2 stage 2/3), with the ordinary status.

5a.10 Neighbouring carrier forms: adapter obligations (normative)

E9’s decision stands — the control principal is mapped onto the form the neighbouring layer already has, and no second wire form is added here. What changes in this casting is the status of the mapping: “an adapter maps it onto the relationship VID” and “an adapter maps it onto the connection” were sentences that named no unique object, and a naive adapter built exactly against the neighbouring standard’s defaults would re-create the very joins 5a.1 exists to prevent. The mapping stays adapter work; the obligations of that work are normative, and they are stated here because there is nowhere else they could live. An adapter profile that does not declare them is not a conforming carrier adapter.

ToIP/TSP (deterministic mapping). TSP names several distinct relationships at once — the endpoint’s relationship with its direct intermediary, each intermediary-to-intermediary hop, the end-to-end relationship, and private VIDs nested inside it — so “the private relationship VID” was underdetermined. The rule:

TSP ingress, and what “principal-free” was always a statement about. A complete TSP carrier adapter must also deliver into a carrier, and there it meets a real collision: an outer TSP envelope carries and authenticates the sender-side VID of its direct neighbour relationship, and nesting hides the inner VIDs from intermediaries, not the outer one from the direct carrier. A reviewer reading 5a.4’s “no carrier-visible sender identifier of any kind” as a claim about every layer beneath us is reading it correctly as written, and as written it made a conforming TSP ingress impossible. So the rule is restated as what it always was — a statement about this contract’s port:

This contract defines no sender identity, requires none, carries none in any field it specifies, and permits no rule of its own to depend on one (5a.4). It does not claim that a neighbouring transport carries no hop identifier; a transport that authenticates its own hops is not thereby nonconformant, and pretending otherwise would exclude every real protocol from the port.

What an adapter owes instead is that the hop identifier stays hop-local and relationship-scoped, and these are MUSTs:

What that buys, exactly, and what it does not. A carrier then sees, for one queue, deposits arriving under one stable sender-side VID — which links those deposits to each other inside a relationship the carrier already carries end to end (there is exactly one counterpart per rkid, 5a.2). Through that VID it learns nothing across relationships and nothing about the person — and the qualifier is the whole sentence, not a hedge: the same carrier can still group relationships by source address, timing, volume, ack pairing or a shared transport, exactly as §10 records. What the rule buys is that the identifier contributes nothing to those joins; it does not buy their absence, and this contract claims no unlinkability anywhere (5a.4, §10). A fresh VID per deposit is explicitly not required and not better: it is equally visible, buys nothing the per-relationship rule does not already buy, and costs an establishment handshake per message. The residual is stated plainly: a TSP carrier sees a per-relationship ingress identifier, and this contract’s claim is bounded accordingly — no join across a person’s relationships, never a claim that ingress is unobservable (§10).

DIDComm mediation and pickup (the standard’s defaults are the attack). Coordinate Mediation 2.0 and Message Pickup 3.0 are connection-scoped protocols: a keylist belongs to a connection, keylist-query returns everything registered for that connection, status-request and delivery-request may omit recipient_did and then speak for the whole connection, batches and messages-received receipt lists span recipients, and Live Mode is a state of a connection that a persistent transport carries. Each of the first four is, in this contract’s terms, a cross-principal answer; the last is two things at once, and is split accordingly below — the logical live state is governed here, the transport underneath it is not. A conforming adapter therefore MUST:

Both mappings remain adapter work, below the port line, and neither adds a wire form to this contract. The vector debt they create is named in Section 11: these obligations are decidable only against an implementation with a carrier interface, and the first place that exists is the mediator adapter itself.

6. Delivery Promises

6.1 Sender: the status trias

State Meaning
accepted the service durably buffered it; delivery is owed
delivered a valid delivery-ack (4.2 consistency) referencing its digest arrived
failed(reason) the service gave up; reason from the closed set below

Sender failure reasons (closed set): unroutable · oversize · expired-by-adapter-policy (the adapter’s declared give-up bound) · rejected-by-receiver(<receiver reason>) where an adapter conveys one. A late valid acknowledgement after failed transitions the status to delivered; implementations MUST surface the transition. No other states exist — in particular, no acceptance state.

Before the trias there is a report, not a fourth state. The three states above describe a submission a service has taken. A submission an adapter still holds — because it is offline, the transport is unreachable, or the carrier refused retriably (5a.5) — has no trias state yet, and inventing one would break the closed set. What it MUST have, within the adapter’s declared status-horizon (4.4), is an honest pre-transport report from the closed set awaiting-transport(offline) · awaiting-transport(transport-unreachable) · awaiting-transport(carrier-refused-retriable). These are adapter conditions, never verdicts about a party, never visible to a receiver, and they end the moment the submission enters the trias. Silence is not one of them: an adapter that reports nothing within its declared horizon is nonconformant, and that rule — not a shortened delivery time — is what the forty-minute CONNECTING socket of the field record would have caught.

6.2 Receiver: dispositions, in mandatory order

The pipeline, as a picture (informative — the numbered stages below are normative):

flowchart TD
    E[envelope arrives] --> S1{stage 1 size gate}
    S1 -- no --> F1[failed oversize]
    S1 --> S2{stage 2 envelope schema, rkid known}
    S2 -- no --> F2[failed malformed]
    S2 --> S3{stage 3 decryption}
    S3 -- no --> F3[failed decryption-failed]
    S3 --> S4{stage 4 parse, digest, dedup}
    S4 -- fails --> F4[failed malformed]
    S4 -- duplicate --> DK[duplicate-known, stored ack re-sent]
    S4 --> S5{stages 5 to 7 profile, recipient, type, payload}
    S5 -- no --> F5[failed at the failing stage]
    S5 --> S8{stage 8 consistency, pre-lock checks}
    S8 -- no --> F8[failed validation or stale-issuance]
    S8 --> S9[stage 9 critical section under the lock set]
    S9 --> EO[own challenge open: record-creating effect]
    S9 --> ER[recorded: the record decides]
    S9 --> EU[unknown: failed validation-failed]
    EO --> U[effect plus retained ack, one durable transaction]
    ER --> U

Every received envelope is evaluated in this order, and the first failing stage names the disposition:

  1. size bound (5) — else failed(oversize);
  2. envelope schema, base64url canonicity (lengths mod 4 ≠ 1, zero trailing bits) and rkid known — live or tombstoned (Section 5) — else failed(malformed);
  3. decryption (all-zero check, tag) — else failed(decryption-failed);
  4. document parse + digest computation — a plaintext that does not parse as JSON or defeats JCS/digest computation is failed(malformed); duplicate check against the completed-effect cache — the cache contains ONLY digests whose stage 9 completed successfully; a digest previously rejected at any stage is NOT in it and is re-evaluated in full. Duplicate → duplicate-known: the prior outcome applies idempotently and the stored acknowledgement of the completed effect MUST be re-sent, byte-identical (it exists by construction for every acknowledging type: the ack document is retained inside the effect’s transaction, stage 9; a crash between commit and transmission would otherwise lose it permanently — for a terminal document, 4.2, no acknowledgement exists and nothing is re-sent). Evaluation ends;
  5. document-profile schema + recipient = own anchor — else failed(malformed) / failed(wrong-recipient);
  6. type known — else failed(unknown-type);
  7. payload schema — else failed(malformed);
  8. type consistency rules and pre-lock acceptance checks (Section 4, incl. the issuance window for bundles) — else failed(validation-failed) / failed(stale-issuance); one exception: a bundle whose bound challenge provisionally resolves unknown (the resolution itself has already latched any held aged value — Encounter 5.3) skips the rest of this stage and enters stage 9, where the authoritative resolution decides: still unknown → dispose; any other state → release and re-enter at stage 4 (4.1 check 5);
  9. gates and effect, serialized per lock set: stage 9 is a critical section whose lock set is the document digest; for bundles, additionally the credential’s bound challenge; for chunked star documents, additionally the assembly key (tuple, salt) (4.4) (the record key). The lock protocol, normatively: the full lock set is acquired atomically, as one acquisition — never one key after the other; an evaluation whose set overlaps a held set holds nothing while waiting; and when the way is free it does not resume — it re-enters at stage 4, rechecking the completed-effect cache and re-selecting its branch on the state actually found. One rule covers both keys; there is no ordering to get wrong and no lock held across the re-entry. The record-key namespace and lifetime are shared with the optical input of Encounter 5.8/5.5 — the same lock, not an equivalent one — so record creation, branch selection, and credential uniqueness are serialized with every competing trigger. The completed-effect cache therefore never holds provisional state: a waiter either finds a completed entry (→ duplicate-known, mandatory re-ack where one is retained — for a terminal document, 4.2, there is nothing to re-send) or finds nothing and proceeds as a fresh evaluation. Inside the critical section: the authoritative resolution and effects of 4.1 — open → record-creating behind the future check (else failed(gate-future)), recorded → the record decides (foreign counterparty: failed(consumed-challenge); else record-aware), unknown → failed(validation-failed) — each effect committed as one durable transaction (record where created, accepted material, cache entry, the acknowledgement document itself where the type acknowledges, retained together with the cache entry per 4.2; 4.1) → unique.

Validate, then consume: no stage before 9 consumes single-use material, and stage 9 consumes only after 1–8 passed in full — for a bundle that includes the issuance window, so nothing that stage 9 records can subsequently fail (the poisoning rule, closed).

incomplete(missing) exists in the taxonomy for types with declared dependencies. No type of this casting declares any; a future type that does MUST define its closed missing vocabulary, its re-evaluation trigger, and its retention bound in its own specification.

6.3 At-least-once and reconciliation

Adapters MAY deliver any envelope multiple times; receivers converge via stage 4 (duplicate-known). A lost acknowledgement is indistinguishable from a lost document to the sender; the sender’s remedy is the carrier switch of Encounter 5.8 — the same enactment continues on the optical leg, and the late bundle is accepted via the record (4.1). A fresh enactment arises only when the optical leg’s boundTo no longer resolves — the gate-expired outcome of Encounter 5.8; the resulting parallel enactments are reconciled by Encounter 0.29 (wire 0.25), 4.2 and 5.8: both are valid, a late counter-credential to the first is accepted, and enactment multiplicity never multiplies edges (one edge per anchor pair). This contract adds nothing to those rules and relies on them.

7. Timing

Parameter Default Meaning
ack-wait PT30S RECOMMENDED sender-side wait before automatically presenting the optical leg — the carrier switch within the same enactment (Encounter 5.8); presentation is permitted at any moment, and conformance never depends on when the switch happens; cancelled by an arriving acknowledgement or counter-credential
key-retention max(P90D, longest adapter give-up horizon) minimum retention of a key-agreement private key after it last appeared in any card (Section 5)

ack-wait is a UX pacing parameter, not a validity rule: an acknowledgement arriving after it is still valid (6.1 late transition), and the record-aware effect (4.1) makes any switch timing safe. Delivery time itself is unbounded; no rule in this contract references arrival time for validity.

8. State Machines (informative)

Sender (A, one-scan): scanning → confirmed/sent (waiting animation) → delivered ("nothing more to do") → [counter-credential accepted] → relation confirmed — with waiting --ack-wait elapsed--> optical presentation (show the sent card as QR), and failed --late valid ack--> delivered.

Receiver (B, one-scan): envelope → staged evaluation (6.2) → recorded + auto-ack → prompt: "verify A back?" → [human confirms] → counter-step issued → relation confirmed — any rejection before the final stage (6.2 stage 9) sends no ack and consumes nothing; B’s prompt is C4, never automated.

9. Security Considerations

10. Privacy Considerations

11. Conformance

12. Open Issues

Appendix A (informative): mapping to the current implementation

This contract Today (Sync 001/003, wot-core)
Document (Trust Task, profile §3) MessageEnvelope / DIDComm plaintext + MessageType union
threadId thid/pthid
Sealed envelope (5) ECIES body {epk, nonce, ciphertext}, info wot/ecies/v1 → rltp/v1/seal, plus new rkid
Status trias (6.1) RelayReceipt accepted/delivered/failed
delivery-ack (4.2) attestation-receipt (Häkchen 2) — semantics move to arrival-at-recording, and the ack gains a proof
Dispositions (6.2) K1 InboxAck* taxonomy, now with mandatory order
Transport queue ack relay {type:'ack'} — below the port line, unspecified here

References

[RFC2119] · [RFC8174] BCP 14 · [RFC3339] · [RFC8785] JCS · [RFC5869] HKDF · RLTP Identity Layer 0.51 (normative; §5.2 the rkid’s key material, §5.3 the recovery context, §7 service identities, §7a the carrier-relationship identity, §9.3 recovery) · [TT] ToIP DTGWG Trust Tasks framework specification 0.4 (§4.8.2, §4.11.1, §6.1, §6.3, §6.5, §7.2–7.3) · RLTP Encounter Layer 0.29, wire 0.25 (delivery port, binding 5.4, ceremony 5.8, fresh-always §4.4, state model 5.3, merge rule 4.2) · RLTP Network Visibility 0.29 (§2.1, §6a, §8) · RLTP Access Layer 0.53, wire 0.24 · RLTP Replication Contract 0.26 (§7/I14, the direct-effect seam of 4.4) · RLTP Membership Tasks 0.16 (§3) · Sync 001/003 (superseded transport specs, Appendix A).

External references of the adapter obligations (5a.10). These are the only normative rules of this contract that point outside the stack, and they were the only ones with no reference entry at all — so a reader could not tell which document “TSP-conformant” meant:

Reference Identity Version pin
[TSP] ToIP Trust Spanning Protocol trustoverip/tswg-tsp-specification commit ea01152425d281da944f40e8da799d7fa7a79f51 (spec/spec.md). The VID taxonomy, the routed model, nested envelopes, and the direct-neighbour relationship are the surfaces 5a.10 consumes
[DIDCOMM-MED] Coordinate Mediation 2.0 decentralized-identity/aries-rfcs / didcomm.org the immutable snapshot the adapter profile records (below); the version label 2.0 names a moving target and is not by itself a pin
[DIDCOMM-PICKUP] Message Pickup 3.0 didcomm.org as above — label plus recorded snapshot, never the label alone

One status, stated once. This document is read against the commit named in the table above — ea01152… — and against nothing else. Two earlier castings each added a sentence around that pin without removing the previous one, so the same reference carried three statuses at once: pinned, “written against the model, not a fixed revision”, and “honestly unpinned”. All three cannot be true, and only the first is: the surfaces 5a.10 consumes — outer versus nested VIDs, routed mode, the direct-neighbour relationship — are read in that commit.

What is not a second status but a duty on someone else:

A conforming adapter profile MUST identify every external specification it implements by an immutable snapshot identity — a commit hash, a content digest, or another identifier that names bytes — and MUST state which of its constructs it maps 5a.10’s obligations onto. A version label is not a pin and neither is a date: “Rev 2”, “Latest Draft” and “2026-08-27” all admit more than one byte sequence, and two changes on one day are indistinguishable under the last of them. A profile that names only a label is not a conforming profile, for the same reason a carrier that declares only "rate" is not a conforming carrier (4.4): a rule whose external target is not reproducible is not reproducibly checkable. No such profile exists in this corpus yet, so the two DIDComm rows above are not byte-reproducible today — unlike the TSP row, which carries a commit. That is reference debt of the same kind §11 records as vector debt, owed at the first adapter casting, and it is why 5a.10’s obligations are written to stand on their own reading rather than on a clause number. What that debt does and does not cover, since an unpinned reference is otherwise an open invitation to read it charitably (round-25 M-2): 5a.10’s obligations are normative as written here and their conformance is decided against this text, so nothing in this contract becomes uncheckable while the debt stands; what is not reproducibly checkable is the mapping claim — that those obligations land on the constructs those two documents actually define. A review of that claim can today only be read against the moving pages and is therefore advisory until the adapter casting pins bytes: a commit in the repository named above, or a content digest of the retrieved document. This contract does not guess one — and no review round can supply it either: a reviewer reads the same moving pages, so a reviewed mapping claim is advisory by construction until the adapter casting records bytes. That is why it is carried here as a dated obligation on a future casting rather than as an open question about this one (round-29 M-2).

(Two earlier repairs are folded into that one rule: “a commit or a dated revision” was half a repair, since a commit identifies bytes and a date does not; and a deferral of the whole pin is not a status a pinned row can also have.)

The direction of travel, which is not a second status: an adapter profile records the snapshot it implements, which may be this commit or a later one; where it is later, this document is re-read against that snapshot at the adapter stage and the table above follows. Until such a re-reading happens, 5a.10 means what it means against ea01152…, and nothing about a newer draft is silently inherited.