@real-life/trust-protocol 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +79 -34
  3. package/dist/adapters/vti-mediator.d.ts +18 -5
  4. package/dist/adapters/vti-mediator.d.ts.map +1 -1
  5. package/dist/adapters/vti-mediator.js +143 -17
  6. package/dist/adapters/vti-mediator.js.map +1 -1
  7. package/dist/codec.d.ts +2 -9
  8. package/dist/codec.d.ts.map +1 -1
  9. package/dist/codec.js +61 -96
  10. package/dist/codec.js.map +1 -1
  11. package/dist/engine.d.ts +62 -1
  12. package/dist/engine.d.ts.map +1 -1
  13. package/dist/engine.js +289 -15
  14. package/dist/engine.js.map +1 -1
  15. package/dist/holder-api.d.ts +15 -1
  16. package/dist/holder-api.d.ts.map +1 -1
  17. package/dist/holder-api.js +28 -3
  18. package/dist/holder-api.js.map +1 -1
  19. package/dist/{holder-state-v2.d.ts → holder-state-v3.d.ts} +210 -207
  20. package/dist/holder-state-v3.d.ts.map +1 -0
  21. package/dist/holder-state-v3.js +2 -0
  22. package/dist/holder-state-v3.js.map +1 -0
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/internal.d.ts +1 -2
  27. package/dist/internal.d.ts.map +1 -1
  28. package/dist/internal.js.map +1 -1
  29. package/dist/results.d.ts +5 -1
  30. package/dist/results.d.ts.map +1 -1
  31. package/dist/results.js.map +1 -1
  32. package/dist/state.d.ts +22 -9
  33. package/dist/state.d.ts.map +1 -1
  34. package/dist/state.js.map +1 -1
  35. package/package.json +4 -6
  36. package/schemas/{holder-state-v2.schema.json → holder-state-v3.schema.json} +50 -5
  37. package/dist/holder-state-v1.d.ts +0 -801
  38. package/dist/holder-state-v1.d.ts.map +0 -1
  39. package/dist/holder-state-v1.js +0 -2
  40. package/dist/holder-state-v1.js.map +0 -1
  41. package/dist/holder-state-v2.d.ts.map +0 -1
  42. package/dist/holder-state-v2.js +0 -2
  43. package/dist/holder-state-v2.js.map +0 -1
  44. package/schemas/holder-state-v1.schema.json +0 -3880
package/CHANGELOG.md CHANGED
@@ -1,5 +1,77 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0
4
+
5
+ Takes up Delivery Contract 0.83: the holder keeps custody of what waits
6
+ for transport, and only an open, resumed opening hands anything over.
7
+
8
+ - **Custody** (RLTP-DEL-6120 to 6135): `pendingTransport(when)` names
9
+ what the holder keeps for transport, after this opening's first
10
+ completed resume (else `{ok: false, code: 'not-resumed'}`): every
11
+ outbox document neither terminal nor accepted, the ones the adapter
12
+ reported `awaiting-transport` included, and every unsent ack, each with
13
+ its envelope, recipient, `since` (first submission), `notAfter` (the
14
+ horizon or the type's sender lifetime, whichever first), `held` and
15
+ `nextAttemptAt`; none past its deadline, whether or not a resume has
16
+ recorded the give-up. Entries are deep-frozen and issued to the
17
+ opening. `handOver(entry, when, start)` re-reads the store and, for an
18
+ entry this opening issued that is still pending, unchanged and before
19
+ its deadline, calls `start` inside the holder's queue: the check and the
20
+ start of the deposit are one step. `mayHandOver(entry, when)` is the
21
+ check alone, and authorizes nothing afterwards. Both read at
22
+ max(when, lastWhen): the holder's time never runs back. A resume that throws, the settling of an earlier commit
23
+ included, withdraws the opening's authority until one completes. One
24
+ opening per store at a time is the app's to keep: two openings are not
25
+ serialized with each other.
26
+ - **A duplicate's answer** (RLTP-DEL-6330): a duplicate makes an answer
27
+ due, stored with the ack (`answers`, `answerDue`) and so kept across a
28
+ lock; `pendingTransport` offers it as a `resend` entry numbered by it,
29
+ beside the ack's retries and independent of their state, with the
30
+ effect's retention as deadline. The begun hand-over ends it (a commit);
31
+ a duplicate arriving while one is due is answered by it (the same
32
+ bytes), the next one after it makes the next due. A duplicate therefore
33
+ commits that answer (else, as before, lastWhen only).
34
+ - **The opening ends**: `await close()` (a lock, the end of a process),
35
+ resolved once the operation running at the call has finished; only then
36
+ may the next opening start. Every
37
+ later call rejects with `InfraError('closed')` (a new code),
38
+ `mayHandOver` answers false, `closed` says so. The next opening is a new
39
+ `createHolder` over the same store.
40
+ - **Acks** (RLTP-DEL-6330, 6335): an `awaiting-transport` report on an
41
+ ack no longer marks it sent; it records `held`, resume stops re-offering
42
+ it and `pendingTransport` keeps it. `accepted` or `delivered` marks it
43
+ sent (`sentBy: 'accepted'`), `failed` gives it up (`'failed'`), the
44
+ horizon ends its retries (`'horizon'`). Sent or not, it stays with its
45
+ effect until the effect's retention; a duplicate re-sends it once
46
+ without new retries.
47
+ - **Codec 3, a clean cut**: `StoredAck.sentBy` is `accepted | failed |
48
+ horizon`, present exactly when sent, `held` only while unsent, and the
49
+ answers (`answers`, `answerDue`). Codec 3 replaces codecs 1 and 2
50
+ without a migration: neither ever held a rolled-out app's state, so a
51
+ state of either is refused as `codec-unknown`. Removed: the migrations
52
+ 1 → 2 and 2 → 3 (`MIGRATIONS` is empty), the codec-1 and codec-2
53
+ validators, `schemas/holder-state-v1.schema.json`,
54
+ `schemas/holder-state-v2.schema.json`, the types `HolderStateV1` and
55
+ `HolderStateV2`, and their fixtures and generators. New:
56
+ `schemas/holder-state-v3.schema.json`, `HolderStateV3`, fixtures
57
+ `state-v3*.json` (generated, reproduced in `npm test`).
58
+ - **`./adapters/vti-mediator`**: `reconcile(view, gate, now)` replaces
59
+ `submitOutbound`. It files the entries of the view not yet filed and
60
+ withdraws every submission before the trias whose digest the view no
61
+ longer names; each deposit starts inside `gate.handOver` (the holder),
62
+ after the adapter's own re-check (retired, withdrawn, past the trias,
63
+ past the entry's deadline or its horizon): the holder's no withdraws,
64
+ an error skips the attempt. Its give-up horizon counts from the entry's
65
+ `since`; an entry taken over past it is given up at once, never
66
+ attempted. A `resend` entry is a submission of its own. One adapter
67
+ serves one opening: bound to the holder of its first reconcile, it
68
+ refuses another, and the next opening gets a new adapter. It keeps no
69
+ custody of its own.
70
+ - Tests: the custody scenarios (`test/transport-custody.test.mjs`, among
71
+ them the open counter-probes of the store-launch reviews) and seeded
72
+ interleavings of locks, the end of a process, hand-overs and acks with
73
+ every cell reached (`test/transport-interleavings.test.mjs`).
74
+
3
75
  ## 0.5.0
4
76
 
5
77
  Takes up Message Tasks 0.1, Delivery Contract 0.82 and Membership Tasks
package/README.md CHANGED
@@ -49,6 +49,53 @@ const { outbound, nextDueAt } = await holder.resume(Date.now())
49
49
  holder.subscribe(async () => render(await holder.contacts(), await holder.prompts()))
50
50
  ```
51
51
 
52
+ **Custody and the lock** (Delivery 0.83, RLTP-DEL-6120 to 6135). The
53
+ holder keeps every outbound document until a service takes it or the
54
+ holder gives it up, across a lock and the end of a process; a transport
55
+ takes what it hands over from the holder, never from a store of its own.
56
+ One `createHolder` is one opening of the store:
57
+
58
+ ```ts
59
+ await holder.resume(Date.now()) // the opening's first resume comes first
60
+ const view = await holder.pendingTransport(Date.now())
61
+ if (view.ok) for (const entry of view.entries) {
62
+ // entry: digest, kind ('document' | 'ack'), envelope, recipient,
63
+ // since (first submission), notAfter (deadline), held?, nextAttemptAt?, resend?
64
+ // the check and the start of the send are one step of the holder
65
+ await holder.handOver(entry, Date.now(), () => { transport.send(entry); return true })
66
+ }
67
+ // a lock or the end of the process: nothing issued before authorizes anything;
68
+ // it resolves once the operation running at the call has finished
69
+ await holder.close()
70
+ ```
71
+
72
+ `pendingTransport` answers `{ok: false, code: 'not-resumed'}` until a
73
+ resume of this opening has completed (a resume that throws withdraws it
74
+ again). It names every outbox document neither terminal nor accepted
75
+ (held ones included) and every unsent ack, none past its deadline.
76
+ `handOver(entry, when, start)` checks an entry and calls `start` inside
77
+ the holder's queue, so nothing of this holder runs between the check and
78
+ the start of the send; it says yes only for an entry this opening issued
79
+ that is still pending, unchanged and before its deadline (`mayHandOver`
80
+ is the check alone). A document missing from the view is not to be sent.
81
+ A duplicate makes an answer due, stored with the ack and kept across a
82
+ lock: a `resend` entry beside the ack's retries, independent of their
83
+ state, ended by the begun hand-over; a duplicate while one is due is
84
+ answered by it (the same bytes). The check is the opening's:
85
+ two openings over one store at once are not serialized with each other,
86
+ so an app keeps one opening per store (its writer lock), and releases
87
+ that lock, or starts the next opening, only after `await holder.close()`:
88
+ until then the operation running at the close may still commit. After `close()` every call rejects with
89
+ `InfraError('closed')` and `mayHandOver` answers false; the next opening
90
+ is a new `createHolder` over the same store. An ack is sent once a
91
+ service took it (`accepted`/`delivered` report), the adapter reports it
92
+ failed, or its horizon passes; an `awaiting-transport` report records
93
+ that the adapter holds it and ends nothing (RLTP-DEL-6330), and it stays
94
+ retained with its effect for a duplicate (6335).
95
+ `./adapters/vti-mediator` reconciles from the view
96
+ (`reconcile(view, holder, now)`); one adapter serves one opening, so the
97
+ next opening creates a new one.
98
+
52
99
  **The timer.** Every act, receive and resume may name `nextDueAt`: the
53
100
  earliest time something is due, delivery work (retries, duties, acks)
54
101
  and expiry work (a prompt's expiry, an act's timeout or lifetime, an aging
@@ -71,7 +118,8 @@ consent to an introduction), `trust`, `untrust`, `pauseTrust`, `setClass`,
71
118
  `addDevice`, `revokeDevice`. Not
72
119
  human: `receive`, `report`, `resume`, `tick`, `recover`, `takeMessages`.
73
120
  Reads: `contacts`, `contact`, `groups`, `profile`, `prompts`, `inbox`,
74
- `deliveryStatus`, `deviceKey`, `snapshot`, `subscribe`.
121
+ `deliveryStatus`, `deviceKey`, `snapshot`, `subscribe`. Custody:
122
+ `pendingTransport`, `handOver`, `mayHandOver`, `close`, `closed`.
75
123
 
76
124
  ## What the root exports
77
125
 
@@ -124,7 +172,7 @@ Bare four-digit numbers are RLTP-TRU rules; the other layers name theirs.
124
172
  | `membership`, `membership/holder` | Membership Tasks 0.20 on Access 0.57 | founding (Access 3.4.1), the prelude (3.5: `membership-prelude-request/0.1`, `membership-prelude-reply/0.1`, the binding per relationship), invite, accept, admission within the bound of 3282 and welcome, evidence relay and `vouch@2` (Access 5.3), an in-library Access over the holder's groups |
125
173
  | `message`, `message/holder` | Message Tasks 0.1 | `message@1`, `k_msg` and its MAC (3); sending on a ready, unblocked relationship with the sender lifetime (5); reception through the stages with the age and future bounds on the time mark, the completion record, the inbox taken over by the app, the rates and the inbox bound as `failed(rate-limited)` (4); the block of a relationship (6010–6030, 6510), its discard of messages and its treatment of invitations and prelude replies (Membership 3115, 3975), the holder's own offers refused (6410) |
126
174
  | `introduction`, `introduction/holder` | Trust §8 on Delivery §4.4 | the Introduction Act in its three roles: request, forward, reply, the mediator's one ack at its declared delay, the vouchers; the requester's timeout at the act's expiry (8340; A20, spec follow-up 18: no ack timeout of 8690) |
127
- | `receive`, `engine` | Delivery 0.82 §5, §6.1, §6.2 | the sealed envelope and the two key classes, stages 1–9 once per envelope with the dispositions `discarded` and the transient `failed(rate-limited)`, the task register by full task URI, the completed-effect cache with stored acks, the outbox with the status trias, retry by backoff, give-up at the horizon, the act journal |
175
+ | `receive`, `engine` | Delivery 0.83 §5, §6.1, §6.2 | the sealed envelope and the two key classes, stages 1–9 once per envelope with the dispositions `discarded` and the transient `failed(rate-limited)`, the task register by full task URI, the completed-effect cache with stored acks, the outbox with the status trias, retry by backoff, give-up at the horizon, custody before the trias and the hand-over gate (`pendingTransport`, `mayHandOver`, `close`), ack retries apart from ack retention, the act journal |
128
176
  | `carrier-identity`, `carrier`, `carrier-holder` | Identity §7a, Delivery §4.4, §5a, Identity §9.3 | the carrier-relationship principal, the carrier port's state machine, proofs, verdicts and recovery |
129
177
  | `views`, `prompts` | — | the read model: contacts by entry (2120, 2140), groups, prompts, the delivery status |
130
178
 
@@ -149,39 +197,36 @@ A holder's state is pure JSON under a codec version. The Store port takes
149
197
  the state as opaque JSON and gives it back; the library decodes it
150
198
  (`decodeState`), the adapter never migrates.
151
199
 
152
- - **Codec 1** is the format of 0.4.0, the first stable one. Its golden
153
- fixtures (`test/fixtures/state-v1*.json`, written by lib 0.4.0 with
154
- `test/fixtures/gen-state-v1.mjs` and frozen since) cover a settled
155
- holder, one with work open (prompts, a joining invite, duties, a
156
- rotation at step 2) and one with a tombstone, a revoked device and
157
- carrier entries. They validate under codec 1 and migrate to codec 2.
158
- - **Codec 2** is the format of 0.5.0: codec 1 with the state of Message
159
- Tasks 0.1 (`messages`: the block, the rates, the ready tuples, the
160
- completion record, the inbox), an outbox item's sender lifetime
161
- (`expiresAt`) and `failed(task-expired)`, and the record of surfaced
162
- and the register of declined invitations (`joining.surfaced`,
163
- `joining.declined`); an invitation may be attributed to no
164
- relationship. The migration from codec 1 is pure: no block, no rate, no
165
- record, no inbox, the ready tuples read from what the counterparts'
166
- documents left, every invitation received counted as surfaced. Its
167
- fixtures (`test/fixtures/state-v2*.json`, deterministic runs of
168
- `test/fixtures/gen-state-v2.mjs`, reproduced in `npm test`) add
169
- messages to the settled holder and a block and a declined invitation to
170
- the open one; each decodes and re-encodes byte for byte. A later codec brings its validator, a pure
171
- migration from the one before, and its own fixture with a migration
172
- test.
200
+ - **Codec 3** is the format of 0.6.0 and the only one this library
201
+ reads. It holds the state of Message Tasks 0.1 (`messages`: the block,
202
+ the rates, the ready tuples, the completion record, the inbox), an
203
+ outbox item's sender lifetime (`expiresAt`) and `failed(task-expired)`,
204
+ the record of surfaced and the register of declined invitations, and
205
+ per stored ack (Delivery 0.83) why it counts as sent (`sentBy`:
206
+ `accepted`, `failed`, `horizon`, present exactly when `sent`), whether
207
+ the adapter holds it (`held`), and the duplicates' answers (`answers`,
208
+ `answerDue`). Its fixtures (`test/fixtures/state-v3*.json`,
209
+ deterministic runs of `test/fixtures/gen-state-v3.mjs`, reproduced in
210
+ `npm test`) cover a settled holder, one with work open (prompts, a
211
+ joining invite, duties, a rotation at step 2, a block and a declined
212
+ invitation), one with a tombstone, a revoked device and carrier entries,
213
+ and one with transport in custody (acks held, accepted, failed and
214
+ unsent, an answer due and one handed over, an own document held); each
215
+ decodes and re-encodes byte for byte.
216
+ - Codecs 1 and 2 (lib 0.4.0, 0.5.0) never held a rolled-out app's state:
217
+ they are read no more, a state of either is `codec-unknown`. A later
218
+ codec that must read codec 3 brings its validator, a pure migration from
219
+ it, and its own fixture with a migration test.
173
220
  - A newer codec is refused as `codec-newer`, a codec without a validator
174
221
  as `codec-unknown`; nothing is read best-effort.
175
- - `schemas/holder-state-v1.schema.json` and
176
- `schemas/holder-state-v2.schema.json` describe the codecs as
177
- non-normative JSON Schemas (published with the package). The types
178
- `HolderStateV1` and `HolderStateV2` (`./internal`) are generated from
179
- them (`scripts/build-holder-state-types.mjs`); the build holds the
180
- current one against the library's `HolderState`, and the tests hold each
181
- schema against its codec's validator on the fixtures and on mutations
182
- of them. Rules only the codec states: a record's id equals its key, an
183
- envelope's `rkid` equals its recipient's `keyAgreement`, and (codec 2) a
184
- set of strings is sorted.
222
+ - `schemas/holder-state-v3.schema.json` describes the codec as a
223
+ non-normative JSON Schema (published with the package). The type
224
+ `HolderStateV3` (`./internal`) is generated from it
225
+ (`scripts/build-holder-state-types.mjs`); the build holds it against the
226
+ library's `HolderState`, and the tests hold the schema against the
227
+ codec's validator on the fixtures and on mutations of them. Rules only
228
+ the codec states: a record's id equals its key, an envelope's `rkid`
229
+ equals its recipient's `keyAgreement`, and a set of strings is sorted.
185
230
 
186
231
  **Writing a Store adapter.** Implement `read(): Promise<{state, version}>`
187
232
  (a deep, independent copy; `null` state and version for an empty store) and
@@ -224,7 +269,7 @@ This package uses its own semver; the specification uses castings.
224
269
  | trust | Trust Layer 0.31 | — |
225
270
  | membership | Membership Tasks 0.20, Access 0.57 | the Access envelope (Membership 3.3: `access-operation/0.1` with `member.add` and the sealed welcome; this library sends its own `x-membership-welcome/0.1`); an admission prompt after the bound of RLTP-MT-3282; roster replication: a holder knows the members it admitted and the roster its welcome carried |
226
271
  | introduction | Trust 0.31 §8 | how a requester learns the mediator's designator (8.3): this library's mediator sends `x-introduction-offer/0.1` |
227
- | delivery | Delivery 0.82 | transport adapters beyond `./adapters/vti-mediator`; the block of the Trust Layer's types (Message Tasks 6240: their treatments wait for the next casting of the Trust Layer) |
272
+ | delivery | Delivery 0.83 | transport adapters beyond `./adapters/vti-mediator`; the block of the Trust Layer's types (Message Tasks 6240: their treatments wait for the next casting of the Trust Layer) |
228
273
  | message | Message Tasks 0.1 | holding a message unsent until its tuple is ready (5020): `sendMessage` answers `not-ready` and the app keeps the message; checking `replyTo` against the relationship's history (5120), which is the app's; multi-device (§9); `failed(task-expired)` for a message: reachable only with a retry horizon beyond P179D, which the engine does not admit (at most P90D), so an undelivered message ends as `failed(expired-by-adapter-policy)` |
229
274
  | devices | Identity §5.5 | multi-device production: one writer per holder |
230
275
 
@@ -1,5 +1,6 @@
1
1
  import type { CarrierPrincipal, CarrierScopedIdentity } from '../carrier-identity.js';
2
- import type { DeliveryReport, Outbound, ReceiverRefusalCode, SubmissionRef } from '../ports.js';
2
+ import type { DeliveryReport, ReceiverRefusalCode, SubmissionRef } from '../ports.js';
3
+ import type { TransportEntry, TransportView } from '../engine.js';
3
4
  import type { ReceiveResult } from '../results.js';
4
5
  export type { ReceiverRefusalCode } from '../ports.js';
5
6
  export declare const DECLARED: Readonly<{
@@ -82,6 +83,14 @@ export interface AckState {
82
83
  }>;
83
84
  pending: string[];
84
85
  }
86
+ /** The holder's side of a hand-over (lib 0.6.0): the holder itself.
87
+ * handOver checks the entry and calls `start` inside the holder's queue;
88
+ * `start` re-checks the adapter's own state, starts the deposit
89
+ * synchronously and says whether it did. `closed` is read in `start`. */
90
+ export interface TransportGate {
91
+ handOver(entry: TransportEntry, when: number, start: () => boolean): Promise<boolean>;
92
+ readonly closed?: boolean;
93
+ }
85
94
  export declare class MediatorCarrierAdapter {
86
95
  #private;
87
96
  get principal(): string;
@@ -103,10 +112,14 @@ export declare class MediatorCarrierAdapter {
103
112
  * honest report exists, status() returns null — §6.1's own
104
113
  * pre-report window, represented as absence, not as a value. */
105
114
  submit(envelopeBytes: Uint8Array, rawNow: number): string;
106
- /** Files an Outbound (its envelope as JCS bytes) and returns the
107
- * SubmissionRef the holder's reports are keyed by: submission id ↔
108
- * document digest (note §4.3). */
109
- submitOutbound(outbound: Pick<Outbound, 'digest' | 'envelope'>, rawNow: number): SubmissionRef;
115
+ /** Takes the holder's custody set (holder.pendingTransport, after a
116
+ * resume of this opening) and the holder as the gate: files every entry
117
+ * not yet filed under its digest (its envelope as JCS bytes; the give-up
118
+ * horizon from entry.since) and returns their SubmissionRefs; withdraws
119
+ * every filed submission still before the trias (no report, or
120
+ * awaiting-transport) whose digest the view does not name. Accepted and
121
+ * terminal submissions stay for their reports. */
122
+ reconcile(view: TransportView, gate: TransportGate, rawNow: number): SubmissionRef[];
110
123
  /** The current state as the ports' DeliveryReport for holder.report(),
111
124
  * or null while no honest report exists (and for an unknown id). A
112
125
  * submission filed without a digest (submit) has no report form. */
@@ -1 +1 @@
1
- {"version":3,"file":"vti-mediator.d.ts","sourceRoot":"","sources":["../../src/adapters/vti-mediator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vti-mediator.d.ts","sourceRoot":"","sources":["../../src/adapters/vti-mediator.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,24 @@
1
1
  // RLTP transport adapter: VTI mediator — published as
2
2
  // @real-life/trust-protocol/adapters/vti-mediator (lib Stage 2, note §4.6).
3
3
  //
4
- // Its reports reach the holder as the ports' DeliveryReport: submitOutbound
5
- // files an Outbound under its document digest and returns the
6
- // SubmissionRef (submission id ↔ digest); report(id) maps the adapter's
7
- // SubmissionState onto DeliveryReport (deliveryReportOf), which the app
8
- // hands to holder.report(). An adapter's `delivered` is its claim about
9
- // transport; the holder's receive core verifies the delivery-ack itself.
4
+ // It takes the holder's documents from the holder's custody (Delivery 0.83
5
+ // RLTP-DEL-6120 to 6135, lib 0.6.0): reconcile(view, gate, now) files every
6
+ // entry of holder.pendingTransport(when) not yet filed, and withdraws every
7
+ // filed submission still before the trias whose digest the view no longer
8
+ // names — a missing entry means stop, never keep sending. Before every
9
+ // deposit it asks gate.mayHandOver(entry, now) (the holder): false
10
+ // withdraws the submission, an error skips the attempt. It keeps no
11
+ // custody of its own: its copies are volatile and bound to the holder's
12
+ // opening: bound to the holder of its first reconcile, it refuses another,
13
+ // so after holder.close() nothing it holds goes out, and the next opening
14
+ // gets a new adapter (restored from ackState like any other). Its give-up horizon counts
15
+ // from the entry's first submission (`since`), never from when it took the
16
+ // entry over (RLTP-DEL-6130). Its reports reach the holder as the ports'
17
+ // DeliveryReport: report(id) maps the adapter's SubmissionState onto
18
+ // DeliveryReport (deliveryReportOf), keyed by the document digest, which
19
+ // the app hands to holder.report(). An adapter's `delivered` is its claim
20
+ // about transport; the holder's receive core verifies the delivery-ack
21
+ // itself.
10
22
  //
11
23
  // Profile: spec/adapter-vti-mediator.md v0.23 (twenty-third casting;
12
24
  // round journals under design/adapter-review*). A
@@ -95,6 +107,8 @@ export class MediatorCarrierAdapter {
95
107
  #tokens = null;
96
108
  #clock = Number.NEGATIVE_INFINITY;
97
109
  #closed = false;
110
+ /** the opening this adapter serves, bound at its first reconcile */
111
+ #gate = null;
98
112
  #subs = new Map();
99
113
  #inflight = new Set();
100
114
  #nextId = 1;
@@ -322,6 +336,7 @@ export class MediatorCarrierAdapter {
322
336
  const id = 's' + this.#nextId++;
323
337
  const knownOffline = this.#wire.offline?.() === true;
324
338
  const sub = {
339
+ id,
325
340
  // a plain copy of its own (a Node Buffer's slice() shares memory)
326
341
  bytes: new Uint8Array(envelopeBytes),
327
342
  submittedAt: now,
@@ -334,16 +349,79 @@ export class MediatorCarrierAdapter {
334
349
  this.#startAttempt(sub, now);
335
350
  return id;
336
351
  }
337
- /** Files an Outbound (its envelope as JCS bytes) and returns the
338
- * SubmissionRef the holder's reports are keyed by: submission id ↔
339
- * document digest (note §4.3). */
340
- submitOutbound(outbound, rawNow) {
352
+ /** Takes the holder's custody set (holder.pendingTransport, after a
353
+ * resume of this opening) and the holder as the gate: files every entry
354
+ * not yet filed under its digest (its envelope as JCS bytes; the give-up
355
+ * horizon from entry.since) and returns their SubmissionRefs; withdraws
356
+ * every filed submission still before the trias (no report, or
357
+ * awaiting-transport) whose digest the view does not name. Accepted and
358
+ * terminal submissions stay for their reports. */
359
+ reconcile(view, gate, rawNow) {
341
360
  this.#open();
342
- if (typeof outbound?.digest !== 'string' || outbound.digest === '')
343
- throw new Error('an outbound needs its document digest');
344
- const submissionId = this.submit(new TextEncoder().encode(jcs(outbound.envelope)), rawNow);
345
- this.#subs.get(submissionId).digest = outbound.digest;
346
- return { submissionId, digest: outbound.digest };
361
+ if (view?.ok !== true || !Array.isArray(view.entries))
362
+ throw new Error('reconcile needs a view of holder.pendingTransport');
363
+ if (typeof gate?.handOver !== 'function')
364
+ throw new Error('reconcile needs the holder as its gate');
365
+ // one adapter per opening: its copies and the holder's grants belong to
366
+ // that opening, and the next opening gets an adapter of its own
367
+ if (this.#gate === null)
368
+ this.#gate = gate;
369
+ else if (this.#gate !== gate)
370
+ throw new Error('one adapter per opening: this adapter reconciles with another holder (create a new adapter for the next opening)');
371
+ const now = this.#now(rawNow);
372
+ // a submission's key: the digest, and for a duplicate's answer its
373
+ // grant too — the retries and each answer are hand-overs of their own
374
+ const keyOf = (digest, resend) => resend === undefined ? digest : `${digest}#${resend}`;
375
+ const named = new Set(view.entries.map((e) => keyOf(e.digest, e.resend)));
376
+ const filed = new Map();
377
+ for (const [id, sub] of this.#subs) {
378
+ if (sub.digest === undefined)
379
+ continue;
380
+ const key = keyOf(sub.digest, sub.entry?.resend);
381
+ const before = sub.st === null || sub.st.state === 'awaiting-transport';
382
+ if (before && !named.has(key)) {
383
+ this.#subs.delete(id);
384
+ continue;
385
+ }
386
+ filed.set(key, sub);
387
+ }
388
+ const refs = [];
389
+ for (const entry of view.entries) {
390
+ const had = filed.get(keyOf(entry.digest, entry.resend));
391
+ if (had !== undefined) {
392
+ // the newest entry of a live submission is the one its gate checks
393
+ if (had.st === null || had.st.state === 'awaiting-transport') {
394
+ had.entry = entry;
395
+ had.gate = gate;
396
+ continue;
397
+ }
398
+ // an answer the holder still offers is still owed — its hand-over
399
+ // never began, or the holder could not record that it did: a
400
+ // terminal submission of it is filed again (the holder ends it)
401
+ if (entry.resend === undefined)
402
+ continue;
403
+ this.#subs.delete(had.id);
404
+ }
405
+ const id = 's' + this.#nextId++;
406
+ const sub = {
407
+ id, digest: entry.digest, entry, gate,
408
+ bytes: new TextEncoder().encode(jcs(entry.envelope)),
409
+ // a duplicate's ack is a hand-over of its own, from now
410
+ submittedAt: entry.resend !== undefined ? now : Math.min(entry.since, now),
411
+ attemptStartedAt: null, lastKind: null, st: null,
412
+ };
413
+ this.#subs.set(id, sub);
414
+ refs.push({ submissionId: id, digest: entry.digest });
415
+ // taken over past its horizon (counted from the first submission,
416
+ // RLTP-DEL-6130): given up at once, never attempted
417
+ if (now - sub.submittedAt >= DECLARED.giveUpHorizonMs) {
418
+ sub.st = { state: 'failed', reason: 'expired-by-adapter-policy' };
419
+ continue;
420
+ }
421
+ if (this.#wire.offline?.() !== true)
422
+ this.#startAttempt(sub, now);
423
+ }
424
+ return refs;
347
425
  }
348
426
  /** The current state as the ports' DeliveryReport for holder.report(),
349
427
  * or null while no honest report exists (and for an unknown id). A
@@ -354,7 +432,7 @@ export class MediatorCarrierAdapter {
354
432
  return null;
355
433
  const digest = this.#subs.get(id).digest;
356
434
  if (digest === undefined)
357
- throw new Error('submitted without a document digest: use submitOutbound');
435
+ throw new Error('submitted without a document digest: file holder documents through reconcile');
358
436
  return deliveryReportOf(digest, st);
359
437
  }
360
438
  has(id) {
@@ -376,7 +454,55 @@ export class MediatorCarrierAdapter {
376
454
  if (this.#closed)
377
455
  return; // retirement between auth and deposit: nothing new starts (R5-B2)
378
456
  // each attempt hands the wire a copy: what it does with it never reaches the next
379
- await this.#wire.deposit(token, new Uint8Array(sub.bytes), this.#egressDid);
457
+ const deposit = () => {
458
+ try {
459
+ return this.#wire.deposit(token, new Uint8Array(sub.bytes), this.#egressDid);
460
+ }
461
+ catch (e) {
462
+ return Promise.reject(e);
463
+ }
464
+ };
465
+ if (sub.gate !== undefined) {
466
+ // the holder decides, and the deposit starts in the same step of its
467
+ // queue (Delivery RLTP-DEL-6125 to 6135); the adapter re-checks its
468
+ // own state there: retired, withdrawn, past the trias, past the
469
+ // entry's deadline or its own horizon → no deposit
470
+ // the deposit's outcome, caught at once: the holder's step may still
471
+ // be committing when the wire answers
472
+ let started = null;
473
+ let declined = false;
474
+ const entry = sub.entry;
475
+ const start = () => {
476
+ if (this.#closed || this.#subs.get(sub.id) !== sub || sub.gate.closed === true ||
477
+ (sub.st !== null && sub.st.state !== 'awaiting-transport') || this.#clock >= entry.notAfter ||
478
+ this.#clock - sub.submittedAt >= DECLARED.giveUpHorizonMs) {
479
+ declined = true;
480
+ return false;
481
+ }
482
+ started = deposit().then(() => null, (e) => ({ failed: e }));
483
+ return true;
484
+ };
485
+ let ok;
486
+ try {
487
+ ok = await sub.gate.handOver(entry, this.#clock, start);
488
+ }
489
+ catch {
490
+ ok = null;
491
+ }
492
+ if (started === null) {
493
+ // the holder said no: withdrawn; the adapter declined or the
494
+ // holder was unreachable: not this attempt
495
+ if (ok === false && !declined && this.#subs.get(sub.id) === sub)
496
+ this.#subs.delete(sub.id);
497
+ return;
498
+ }
499
+ const outcome = await started;
500
+ if (outcome !== null)
501
+ throw outcome.failed;
502
+ }
503
+ else {
504
+ await deposit();
505
+ }
380
506
  sub.lastKind = null;
381
507
  next = { state: 'accepted' };
382
508
  }