@real-life/trust-protocol 0.4.0 → 0.5.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 (88) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +47 -25
  3. package/dist/adapters/vti-mediator.d.ts +18 -3
  4. package/dist/adapters/vti-mediator.d.ts.map +1 -1
  5. package/dist/adapters/vti-mediator.js +51 -13
  6. package/dist/adapters/vti-mediator.js.map +1 -1
  7. package/dist/codec.d.ts +9 -1
  8. package/dist/codec.d.ts.map +1 -1
  9. package/dist/codec.js +107 -23
  10. package/dist/codec.js.map +1 -1
  11. package/dist/engine.d.ts +15 -3
  12. package/dist/engine.d.ts.map +1 -1
  13. package/dist/engine.js +130 -16
  14. package/dist/engine.js.map +1 -1
  15. package/dist/entropy.d.ts +1 -1
  16. package/dist/entropy.d.ts.map +1 -1
  17. package/dist/entropy.js +1 -1
  18. package/dist/entropy.js.map +1 -1
  19. package/dist/events.d.ts +21 -0
  20. package/dist/events.d.ts.map +1 -1
  21. package/dist/events.js.map +1 -1
  22. package/dist/holder-api.d.ts +29 -1
  23. package/dist/holder-api.d.ts.map +1 -1
  24. package/dist/holder-api.js +12 -2
  25. package/dist/holder-api.js.map +1 -1
  26. package/dist/holder-state-v2.d.ts +858 -0
  27. package/dist/holder-state-v2.d.ts.map +1 -0
  28. package/dist/holder-state-v2.js +2 -0
  29. package/dist/holder-state-v2.js.map +1 -0
  30. package/dist/index.d.ts +2 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +10 -6
  33. package/dist/index.js.map +1 -1
  34. package/dist/internal.d.ts +2 -1
  35. package/dist/internal.d.ts.map +1 -1
  36. package/dist/internal.js +1 -1
  37. package/dist/internal.js.map +1 -1
  38. package/dist/introduction/holder.d.ts.map +1 -1
  39. package/dist/introduction/holder.js +7 -0
  40. package/dist/introduction/holder.js.map +1 -1
  41. package/dist/invariants.d.ts.map +1 -1
  42. package/dist/invariants.js +70 -0
  43. package/dist/invariants.js.map +1 -1
  44. package/dist/membership/holder.d.ts +3 -5
  45. package/dist/membership/holder.d.ts.map +1 -1
  46. package/dist/membership/holder.js +165 -88
  47. package/dist/membership/holder.js.map +1 -1
  48. package/dist/membership.d.ts +31 -0
  49. package/dist/membership.d.ts.map +1 -1
  50. package/dist/membership.js +25 -2
  51. package/dist/membership.js.map +1 -1
  52. package/dist/message/holder.d.ts +49 -0
  53. package/dist/message/holder.d.ts.map +1 -0
  54. package/dist/message/holder.js +290 -0
  55. package/dist/message/holder.js.map +1 -0
  56. package/dist/message.d.ts +43 -0
  57. package/dist/message.d.ts.map +1 -0
  58. package/dist/message.js +51 -0
  59. package/dist/message.js.map +1 -0
  60. package/dist/receive.d.ts +4 -3
  61. package/dist/receive.d.ts.map +1 -1
  62. package/dist/receive.js +23 -4
  63. package/dist/receive.js.map +1 -1
  64. package/dist/results.d.ts +17 -2
  65. package/dist/results.d.ts.map +1 -1
  66. package/dist/results.js.map +1 -1
  67. package/dist/schemas.d.ts.map +1 -1
  68. package/dist/schemas.js +9 -6
  69. package/dist/schemas.js.map +1 -1
  70. package/dist/state.d.ts +66 -5
  71. package/dist/state.d.ts.map +1 -1
  72. package/dist/state.js +2 -1
  73. package/dist/state.js.map +1 -1
  74. package/dist/trust/continuity.d.ts.map +1 -1
  75. package/dist/trust/continuity.js +7 -1
  76. package/dist/trust/continuity.js.map +1 -1
  77. package/dist/trust/entries.d.ts.map +1 -1
  78. package/dist/trust/entries.js +103 -2
  79. package/dist/trust/entries.js.map +1 -1
  80. package/dist/views.d.ts +34 -2
  81. package/dist/views.d.ts.map +1 -1
  82. package/dist/views.js +19 -1
  83. package/dist/views.js.map +1 -1
  84. package/dist/wire.d.ts +47 -6
  85. package/dist/wire.d.ts.map +1 -1
  86. package/dist/wire.js.map +1 -1
  87. package/package.json +6 -2
  88. package/schemas/holder-state-v2.schema.json +4123 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,70 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.0
4
+
5
+ Takes up Message Tasks 0.1, Delivery Contract 0.82 and Membership Tasks
6
+ 0.20.
7
+
8
+ - **Messages** (Message Tasks 0.1): `sendMessage(contactId, text,
9
+ {when, actId, replyTo?})` sends a `message@1` on a ready, unblocked
10
+ relationship (refused as `not-ready`, `blocked` or `message-invalid`;
11
+ event `message.sent` with the mid and the document digest). Reception
12
+ runs the stages of Delivery 6.2: the MAC under `k_msg`, the age bound
13
+ (`failed(stale-issuance)`) and the future bound on the holder's time
14
+ mark, the completion record per tuple (no new effect for a known body,
15
+ equivocation refused), the inbox entry committed with the cache entry
16
+ and the acknowledgement. `inbox()` lists the messages not yet taken
17
+ over, in display order; `takeMessages(ids, {when})` takes them over.
18
+ `ContactView.canMessage`. The pure forms (`messageKeyOf`,
19
+ `messageMac`, `messageProblem`, `MESSAGE_TYPE`, …) are exported from the
20
+ root.
21
+ - **The block** (Message Tasks 6): `block(contactId)` and
22
+ `unblock(contactId)`, chain-level state of every relationship of the
23
+ entry, kept through chaining, merge and split; event `contact.blocked`,
24
+ `ContactView.blocked`. Under a block messages are discarded and
25
+ invitations declined into the register of declined invitations,
26
+ silently and acknowledged; the holder's own messages, invitations and
27
+ introduction acts toward a blocked relationship are refused. The
28
+ treatment of the Trust Layer's types waits for that layer's next
29
+ casting.
30
+ - **Invitations** (Membership Tasks 0.20 §3.1): attributed to the
31
+ relationship of their prelude binding, or to none; at most 20 new
32
+ credential digests per relationship and day; one surfacing per
33
+ credential digest, decided with the register and the permanent record
34
+ of surfaced invitations.
35
+ - **The prelude** (Membership §3.5): the registered types
36
+ `membership-prelude-request/0.1` and `membership-prelude-reply/0.1` with
37
+ the binding `membership-inviter@1`, kept per relationship for its life;
38
+ genesis digests compared by their bytes. **Breaking:** a holder of 0.4.0
39
+ sends `x-membership-prelude-*`, which 0.5.0 refuses as `unknown-type`.
40
+ - The inviter admits without a human act only before
41
+ `validUntil + membership-skew + P90D` (Membership 3282).
42
+ - **Delivery 0.82**: the transient disposition `failed(rate-limited)`
43
+ (nothing committed, no ack, no cache entry, evaluated afresh when
44
+ delivered again); an outbound may carry its type's sender lifetime,
45
+ after which resume moves it to `failed(task-expired)` and never hands it
46
+ out again — for a message `sentAt + P179D`, after any retry horizon
47
+ (at most P90D), so an undelivered message fails as
48
+ `failed(expired-by-adapter-policy)` first.
49
+ - `./adapters/vti-mediator`: `conclude(collection, now, {keep})` leaves
50
+ transient items unconcluded and offers them again; `concludable(result)`.
51
+ - **State codec 2**, with a pure migration from codec 1 (no block, no
52
+ rate, no record, no inbox; the ready tuples read from the state; every
53
+ received invitation counted as surfaced), its non-normative schema
54
+ `schemas/holder-state-v2.schema.json` and the type `HolderStateV2`, and
55
+ golden fixtures reproduced in `npm test`. The codec 1 fixtures are
56
+ frozen as 0.4.0 wrote them and migrated in the tests.
57
+ - Closed unions grown: `ActError` (`blocked`, `not-ready`,
58
+ `message-invalid`), `ReceiveFailure` (`rate-limited`), `HolderEvent`
59
+ (`contact.blocked`, `message.sent`, `message.received`), the outbox's
60
+ failure reasons (`task-expired`), the entropy purposes (`message.mid`).
61
+ - Engine (for modules): `keepKeys(state, when)` gets the tick's time; the
62
+ module hook `accepted` runs in the stage-9 transaction of every unique
63
+ document.
64
+ - Tests: the scenario set of Message Tasks §10.2, the race of #103 on two
65
+ engines over one store, and a seeded interleaving generator over
66
+ messages, the block, take-over and the record's retention.
67
+
3
68
  ## 0.4.0
4
69
 
5
70
  - `createHolder`: one person's device state behind typed human acts
package/README.md CHANGED
@@ -67,10 +67,11 @@ The human acts: `show`, `scan`, `captureSentCard`, `counter` (the encounter),
67
67
  `decide` (a prompt: confirm an encounter, accept an invitation, take up or
68
68
  consent to an introduction), `trust`, `untrust`, `pauseTrust`, `setClass`,
69
69
  `setGroupClass`, `merge`, `depart`, `setProfile`, `foundGroup`, `invite`,
70
- `vouchFor`, `introduce`, `rotateAnchor`, `addDevice`, `revokeDevice`. Not
71
- human: `receive`, `report`, `resume`, `tick`, `recover`. Reads: `contacts`,
72
- `contact`, `groups`, `profile`, `prompts`, `deliveryStatus`, `deviceKey`,
73
- `snapshot`, `subscribe`.
70
+ `vouchFor`, `introduce`, `sendMessage`, `block`, `unblock`, `rotateAnchor`,
71
+ `addDevice`, `revokeDevice`. Not
72
+ human: `receive`, `report`, `resume`, `tick`, `recover`, `takeMessages`.
73
+ Reads: `contacts`, `contact`, `groups`, `profile`, `prompts`, `inbox`,
74
+ `deliveryStatus`, `deviceKey`, `snapshot`, `subscribe`.
74
75
 
75
76
  ## What the root exports
76
77
 
@@ -80,14 +81,17 @@ human: `receive`, `report`, `resume`, `tick`, `recover`. Reads: `contacts`,
80
81
  `RetryPolicy`, `DeliveryReport`, `Outbound`; `seedKeys(seed)` is the
81
82
  default KeyProvider, `randomEntropy` the default Entropy.
82
83
  - The views: `ContactView`, `GroupView`, `PromptView`, `ProfileView`,
83
- `DeliveryStatusView` and their parts.
84
+ `InboxMessageView`, `DeliveryStatusView` and their parts.
84
85
  - The results and events: `ActResult`, `ReceiveResult`, `ResumeResult`,
85
86
  `InfraError`, `CryptoUnavailableError`, `HolderEvent`.
86
87
  - The state as an opaque type (`HolderState`), `decodeState`, `CODEC`,
87
88
  `StateCodecError`, and `storeContract`, the suite a Store adapter passes.
88
89
  - The pure wire builders and verifiers of every layer (`core`, `crypto`,
89
- `identity`, `profile`, `encounter`, `membership`, `introduction`,
90
- `delivery`, `carrier-identity`, `carrier`, `carrier-holder`), and two
90
+ `identity`, `profile`, `encounter`, `membership`, `message` —
91
+ `MESSAGE_TYPE`, `messageKeyOf`, `messageMac`, `messageProblem`,
92
+ `messageShapeOk` and the constants of Message Tasks 0.1 —,
93
+ `introduction`, `delivery`, `carrier-identity`, `carrier`,
94
+ `carrier-holder`), and two
91
95
  namespaces of pure forms: `visibility` (anchor, star, group star,
92
96
  continuity, acks) and `ceremony` (the encounter's, the group's and the
93
97
  introduction's forms).
@@ -98,7 +102,7 @@ Other entry points:
98
102
  |---|---|
99
103
  | `./testing` | `InMemoryStore`, `memoryLog`, `scriptedEntropy`, `storeContract`, `emptyState`, `stateBuilder`, `freezeDeep`, `sampleState`, `openPromptModule`, the invariant checks, `forgeMapping` (deniability, Trust 6.1), `SCHEMAS` and the generated `wire` types. For tests, vectors and adapter authors; not for production holders. |
100
104
  | `./internal` | The structural `HolderState`, `encodeState` and the codec chain, the engine's module types, the pure view functions, the modules' factories and pure reads. For the library's own modules and tests; not an app contract. |
101
- | `./adapters/vti-mediator` | The VTI mediator as a carrier adapter: its submission states as `DeliveryReport`, collection and ack handling. |
105
+ | `./adapters/vti-mediator` | The VTI mediator as a carrier adapter: its submission states as `DeliveryReport`, collection and ack handling, the selective conclusion of a collection (`conclude(c, now, {keep})`, `concludable(result)`: a `failed(rate-limited)` item stays with the carrier). |
102
106
 
103
107
  ## The modules and the rules they implement
104
108
 
@@ -117,9 +121,10 @@ Bare four-digit numbers are RLTP-TRU rules; the other layers name theirs.
117
121
  | `trust/continuity` | Trust §6a | `continuity-probe@1` and `continuity-mapping@1`, chaining a re-encounter into the existing relationship (6a.4) |
118
122
  | `trust/profile`, `profile` | Trust §6b | `profile@1`: the one revision counter (6450), delivery on the class gates (6455), reception and the image bounds (6475) |
119
123
  | `trust/duties` | Trust 2850, 6370, 6455, 6595, 6596 | the duties a commit leaves: class on, re-issue after a rotation, the lag rule |
120
- | `membership`, `membership/holder` | Membership Tasks 0.19 on Access 0.57 | founding (Access 3.4.1), the prelude, invite, accept, admission and welcome, evidence relay and `vouch@2` (Access 5.3), an in-library Access over the holder's groups |
124
+ | `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
+ | `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) |
121
126
  | `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) |
122
- | `receive`, `engine` | Delivery 0.81 §5, §6.1, §6.2 | the sealed envelope, stages 1–9 once per envelope, 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 |
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 |
123
128
  | `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 |
124
129
  | `views`, `prompts` | — | the read model: contacts by entry (2120, 2140), groups, prompts, the delivery status |
125
130
 
@@ -145,22 +150,38 @@ the state as opaque JSON and gives it back; the library decodes it
145
150
  (`decodeState`), the adapter never migrates.
146
151
 
147
152
  - **Codec 1** is the format of 0.4.0, the first stable one. Its golden
148
- fixtures (`test/fixtures/state-v1*.json`, deterministic runs of
149
- `test/fixtures/gen-state-v1.mjs`, reproduced in `npm test`) cover a
150
- settled holder, one with work open (prompts, a joining invite, duties, a
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
151
156
  rotation at step 2) and one with a tombstone, a revoked device and
152
- carrier entries; each decodes and re-encodes byte for byte. A later codec brings its validator, a pure migration from the
153
- one before, and its own fixture with a migration test.
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.
154
173
  - A newer codec is refused as `codec-newer`, a codec without a validator
155
174
  as `codec-unknown`; nothing is read best-effort.
156
- - `schemas/holder-state-v1.schema.json` describes codec 1 as a
157
- non-normative JSON Schema (published with the package). The type
158
- `HolderStateV1` (`./internal`) is generated from it
159
- (`scripts/build-holder-state-types.mjs`); the build holds it against the
160
- library's `HolderState`, and the tests hold the schema against the
161
- codec's validator on the fixtures and on mutations of them. Two rules
162
- only the codec states: a record's id equals its key, and an envelope's
163
- `rkid` equals its recipient's `keyAgreement`.
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.
164
185
 
165
186
  **Writing a Store adapter.** Implement `read(): Promise<{state, version}>`
166
187
  (a deep, independent copy; `null` state and version for an empty store) and
@@ -201,9 +222,10 @@ This package uses its own semver; the specification uses castings.
201
222
  | identity | Identity 0.54 | recovery derivation (§5.3), service identities (§7) |
202
223
  | encounter | Encounter 0.31, cards 0.25, credentials 0.26 | — |
203
224
  | trust | Trust Layer 0.31 | — |
204
- | membership | Membership Tasks 0.19, 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`, and the prelude as `x-membership-prelude-*`); roster replication: a holder knows the members it admitted and the roster its welcome carried |
225
+ | 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 |
205
226
  | 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` |
206
- | delivery | Delivery 0.81 | transport adapters beyond `./adapters/vti-mediator` |
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) |
228
+ | 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)` |
207
229
  | devices | Identity §5.5 | multi-device production: one writer per holder |
208
230
 
209
231
  ## The contract: the vectors
@@ -1,14 +1,15 @@
1
1
  import type { CarrierPrincipal, CarrierScopedIdentity } from '../carrier-identity.js';
2
2
  import type { DeliveryReport, Outbound, ReceiverRefusalCode, SubmissionRef } from '../ports.js';
3
+ import type { ReceiveResult } from '../results.js';
3
4
  export type { ReceiverRefusalCode } from '../ports.js';
4
- export declare const DECLARED: {
5
+ export declare const DECLARED: Readonly<{
5
6
  readonly giveUpHorizonMs: number;
6
7
  readonly statusHorizonMs: 60000;
7
8
  readonly queueFloorCount: 64;
8
9
  readonly queueFloorBytes: 1048576;
9
10
  readonly maxQueueBytes: 16777216;
10
11
  readonly duplicateWindowMs: number;
11
- };
12
+ }>;
12
13
  export type WireErrorKind = 'unreachable' | 'refused-retriable' | 'refused-admission' | 'oversize';
13
14
  export declare class WireError extends Error {
14
15
  kind: WireErrorKind;
@@ -57,6 +58,12 @@ export type SubmissionState = {
57
58
  /** The ports' DeliveryReport for a document `digest` in adapter state `st`
58
59
  * (Delivery §6.1): one-to-one, `late` and the refusal code carried over. */
59
60
  export declare function deliveryReportOf(digest: string, st: SubmissionState): DeliveryReport;
61
+ /** Whether a receive outcome is terminal and its item may be concluded
62
+ * (Delivery 5a.8): every disposition but `failed(rate-limited)`, the one
63
+ * failure that is transient (RLTP-DEL-5820). An InfraError of the
64
+ * holder's store is transient as well (RLTP-DEL-5810): an item whose
65
+ * receive threw is kept, too. */
66
+ export declare function concludable(r: ReceiveResult): boolean;
60
67
  export interface CollectedItem {
61
68
  bytes: Uint8Array;
62
69
  queueId: string;
@@ -137,9 +144,17 @@ export declare class MediatorCarrierAdapter {
137
144
  to: 'delivered';
138
145
  }>;
139
146
  collect(rawNow: number): Collection;
147
+ /** Concludes a collection (profile §4, Delivery 5a.8): its items'
148
+ * digests become ack intent. `keep` names the items whose disposition
149
+ * is transient (Delivery RLTP-DEL-5810, 5820: `failed(rate-limited)`,
150
+ * or a transaction that could not commit, see `concludable`): they are
151
+ * not concluded and not acknowledged, they go back to the buffer and the
152
+ * next collect offers them again, and the mediator keeps its copy. */
140
153
  conclude(collection: {
141
154
  id: string;
142
- }, rawNow: number): void;
155
+ }, rawNow: number, opts?: {
156
+ keep?: readonly string[];
157
+ }): void;
143
158
  ackState(): AckState;
144
159
  advance(rawNow: number): void;
145
160
  /** §5a.10 lifecycle boundary: after close, every state port is
@@ -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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"}
@@ -12,6 +12,13 @@
12
12
  // round journals under design/adapter-review*). A
13
13
  // transport adapter contract — NOT a carrier at the port.
14
14
  //
15
+ // Beyond the profile (lib 0.5.0): conclude(collection, now, {keep}) leaves
16
+ // the items whose disposition is transient unconcluded and offers them
17
+ // again (Delivery 0.82 RLTP-DEL-5810, 5820: failed(rate-limited) is never
18
+ // concluded); `concludable(result)` says which receive outcomes are
19
+ // terminal. The profile's §4 concludes a collection whole; its next
20
+ // casting is to take this up.
21
+ //
15
22
  // Structural rules (reviews 3+4):
16
23
  // · No port operation ever awaits the mediator — including
17
24
  // register: keylist synchronization is a recorded intent with a
@@ -26,14 +33,15 @@ import { jcs } from '../core.js';
26
33
  import { sha } from '../crypto.js';
27
34
  import { carrierPrincipal, carrierScopedIdentity } from '../carrier-identity.js';
28
35
  // ── Declared constants (profile §2) — promises of THIS casting ──────
29
- export const DECLARED = {
36
+ // frozen: the declared promises cannot be changed at run time either
37
+ export const DECLARED = Object.freeze({
30
38
  giveUpHorizonMs: 72 * 3_600_000,
31
39
  statusHorizonMs: 60_000,
32
40
  queueFloorCount: 64,
33
41
  queueFloorBytes: 1_048_576,
34
42
  maxQueueBytes: 16_777_216,
35
43
  duplicateWindowMs: 30 * 86_400_000,
36
- };
44
+ });
37
45
  export class WireError extends Error {
38
46
  kind;
39
47
  constructor(kind, message) {
@@ -59,6 +67,16 @@ export function deliveryReportOf(digest, st) {
59
67
  : { digest, status: 'failed', reason: st.reason };
60
68
  }
61
69
  }
70
+ /** Whether a receive outcome is terminal and its item may be concluded
71
+ * (Delivery 5a.8): every disposition but `failed(rate-limited)`, the one
72
+ * failure that is transient (RLTP-DEL-5820). An InfraError of the
73
+ * holder's store is transient as well (RLTP-DEL-5810): an item whose
74
+ * receive threw is kept, too. */
75
+ export function concludable(r) {
76
+ return !(r.disposition === 'failed' && r.reason === 'rate-limited');
77
+ }
78
+ /** The adapter's own copy of a wire's token set: the wire keeps no handle on it. */
79
+ const tokenCopy = (t) => ({ accessToken: t.accessToken, accessExpiresAt: t.accessExpiresAt, refreshToken: t.refreshToken, refreshExpiresAt: t.refreshExpiresAt });
62
80
  const hex = (b) => Array.from(b, (x) => x.toString(16).padStart(2, '0')).join('');
63
81
  // construction capability: only create() holds it
64
82
  const KEY = Symbol('MediatorCarrierAdapter.create');
@@ -161,7 +179,8 @@ export class MediatorCarrierAdapter {
161
179
  this.#wire.onDeliver(async (raw) => {
162
180
  if (this.#closed)
163
181
  return;
164
- const bytes = raw.slice();
182
+ // a plain copy of its own: a Node Buffer's slice() would share memory
183
+ const bytes = new Uint8Array(raw);
165
184
  const queueId = hex(await sha(bytes));
166
185
  if (this.#closed)
167
186
  return;
@@ -229,7 +248,7 @@ export class MediatorCarrierAdapter {
229
248
  const refreshed = await this.#wire.refresh(this.#tokens.refreshToken);
230
249
  if (this.#closed)
231
250
  throw new Error('retired'); // as on the handshake path (R6-B1)
232
- this.#tokens = refreshed;
251
+ this.#tokens = tokenCopy(refreshed);
233
252
  return this.#tokens.accessToken;
234
253
  }
235
254
  catch (e) {
@@ -247,7 +266,7 @@ export class MediatorCarrierAdapter {
247
266
  const { challenge, sessionId } = await this.#wire.authChallenge(this.#connectionDid);
248
267
  if (this.#closed)
249
268
  throw new Error('retired'); // nothing new starts, not even the answer (R6-B1)
250
- this.#tokens = await this.#wire.authenticate({ did: this.#connectionDid, challenge, sessionId });
269
+ this.#tokens = tokenCopy(await this.#wire.authenticate({ did: this.#connectionDid, challenge, sessionId }));
251
270
  return this.#tokens.accessToken;
252
271
  })();
253
272
  this.#authing = run.finally(() => { this.#authing = null; });
@@ -278,7 +297,7 @@ export class MediatorCarrierAdapter {
278
297
  const token = await this.#ensureAuth(now);
279
298
  if (this.#closed)
280
299
  return;
281
- await this.#wire.keylistUpdate(token, wanted);
300
+ await this.#wire.keylistUpdate(token, [...wanted]); // the wire's own list: `wanted` is compared below
282
301
  // land only if the desire has not moved meanwhile
283
302
  if (!this.#closed && this.#desiredRkids !== null &&
284
303
  wanted.join(' ') === this.#desiredRkids.join(' ')) {
@@ -303,7 +322,8 @@ export class MediatorCarrierAdapter {
303
322
  const id = 's' + this.#nextId++;
304
323
  const knownOffline = this.#wire.offline?.() === true;
305
324
  const sub = {
306
- bytes: envelopeBytes.slice(),
325
+ // a plain copy of its own (a Node Buffer's slice() shares memory)
326
+ bytes: new Uint8Array(envelopeBytes),
307
327
  submittedAt: now,
308
328
  attemptStartedAt: null,
309
329
  lastKind: null,
@@ -355,7 +375,8 @@ export class MediatorCarrierAdapter {
355
375
  const token = await this.#ensureAuth(now);
356
376
  if (this.#closed)
357
377
  return; // retirement between auth and deposit: nothing new starts (R5-B2)
358
- await this.#wire.deposit(token, sub.bytes, this.#egressDid);
378
+ // 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);
359
380
  sub.lastKind = null;
360
381
  next = { state: 'accepted' };
361
382
  }
@@ -406,7 +427,7 @@ export class MediatorCarrierAdapter {
406
427
  if (!sub)
407
428
  return null;
408
429
  if (sub.st !== null && sub.st.state !== 'awaiting-transport')
409
- return sub.st;
430
+ return { ...sub.st };
410
431
  if (this.#wire.offline?.() === true) {
411
432
  return { state: 'awaiting-transport', reason: 'offline' };
412
433
  }
@@ -414,7 +435,7 @@ export class MediatorCarrierAdapter {
414
435
  this.#clock - sub.attemptStartedAt >= DECLARED.statusHorizonMs) {
415
436
  return { state: 'awaiting-transport', reason: 'transport-unreachable' };
416
437
  }
417
- return sub.st;
438
+ return sub.st === null ? null : { ...sub.st };
418
439
  }
419
440
  acknowledged(id) {
420
441
  if (this.#closed)
@@ -469,17 +490,33 @@ export class MediatorCarrierAdapter {
469
490
  this.#inboxDigests.clear();
470
491
  const id = 'c' + this.#nextColId++;
471
492
  this.#outstanding.set(id, new Map(items.map((i) => [i.queueId, i])));
472
- return { id, items: items.map((i) => ({ ...i })) };
493
+ // the caller gets its own copies: the outstanding entries stay intact,
494
+ // whatever it does with the bytes, for a conclusion that keeps them
495
+ return { id, items: items.map((i) => ({ ...i, bytes: new Uint8Array(i.bytes) })) };
473
496
  }
474
- conclude(collection, rawNow) {
497
+ /** Concludes a collection (profile §4, Delivery 5a.8): its items'
498
+ * digests become ack intent. `keep` names the items whose disposition
499
+ * is transient (Delivery RLTP-DEL-5810, 5820: `failed(rate-limited)`,
500
+ * or a transaction that could not commit, see `concludable`): they are
501
+ * not concluded and not acknowledged, they go back to the buffer and the
502
+ * next collect offers them again, and the mediator keeps its copy. */
503
+ conclude(collection, rawNow, opts = {}) {
475
504
  this.#open();
476
505
  const now = this.#now(rawNow);
477
506
  const col = this.#outstanding.get(collection.id);
478
507
  if (!col)
479
508
  return;
480
509
  this.#outstanding.delete(collection.id);
510
+ const keep = new Set(opts.keep ?? []);
481
511
  for (const item of col.values()) {
482
512
  this.#outstandingBytes -= item.bytes.length;
513
+ if (keep.has(item.queueId)) {
514
+ // transient: back to the buffer, offered again, never acknowledged
515
+ this.#inbox.push(item);
516
+ this.#inboxBytes += item.bytes.length;
517
+ this.#inboxDigests.add(item.queueId);
518
+ continue;
519
+ }
483
520
  this.#concluded.set(item.queueId, now);
484
521
  this.#pendingAcks.add(item.queueId);
485
522
  }
@@ -531,7 +568,8 @@ export class MediatorCarrierAdapter {
531
568
  for (const id of live)
532
569
  this.#flightAcks.add(id);
533
570
  try {
534
- await this.#wire.ackReceived(token, live);
571
+ // the wire gets its own list: what it does with it never reaches the bookkeeping below
572
+ await this.#wire.ackReceived(token, [...live]);
535
573
  if (!this.#closed)
536
574
  for (const id of live)
537
575
  this.#pendingAcks.delete(id);