@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.
- package/CHANGELOG.md +65 -0
- package/README.md +47 -25
- package/dist/adapters/vti-mediator.d.ts +18 -3
- package/dist/adapters/vti-mediator.d.ts.map +1 -1
- package/dist/adapters/vti-mediator.js +51 -13
- package/dist/adapters/vti-mediator.js.map +1 -1
- package/dist/codec.d.ts +9 -1
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +107 -23
- package/dist/codec.js.map +1 -1
- package/dist/engine.d.ts +15 -3
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +130 -16
- package/dist/engine.js.map +1 -1
- package/dist/entropy.d.ts +1 -1
- package/dist/entropy.d.ts.map +1 -1
- package/dist/entropy.js +1 -1
- package/dist/entropy.js.map +1 -1
- package/dist/events.d.ts +21 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/holder-api.d.ts +29 -1
- package/dist/holder-api.d.ts.map +1 -1
- package/dist/holder-api.js +12 -2
- package/dist/holder-api.js.map +1 -1
- package/dist/holder-state-v2.d.ts +858 -0
- package/dist/holder-state-v2.d.ts.map +1 -0
- package/dist/holder-state-v2.js +2 -0
- package/dist/holder-state-v2.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -6
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +2 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/introduction/holder.d.ts.map +1 -1
- package/dist/introduction/holder.js +7 -0
- package/dist/introduction/holder.js.map +1 -1
- package/dist/invariants.d.ts.map +1 -1
- package/dist/invariants.js +70 -0
- package/dist/invariants.js.map +1 -1
- package/dist/membership/holder.d.ts +3 -5
- package/dist/membership/holder.d.ts.map +1 -1
- package/dist/membership/holder.js +165 -88
- package/dist/membership/holder.js.map +1 -1
- package/dist/membership.d.ts +31 -0
- package/dist/membership.d.ts.map +1 -1
- package/dist/membership.js +25 -2
- package/dist/membership.js.map +1 -1
- package/dist/message/holder.d.ts +49 -0
- package/dist/message/holder.d.ts.map +1 -0
- package/dist/message/holder.js +290 -0
- package/dist/message/holder.js.map +1 -0
- package/dist/message.d.ts +43 -0
- package/dist/message.d.ts.map +1 -0
- package/dist/message.js +51 -0
- package/dist/message.js.map +1 -0
- package/dist/receive.d.ts +4 -3
- package/dist/receive.d.ts.map +1 -1
- package/dist/receive.js +23 -4
- package/dist/receive.js.map +1 -1
- package/dist/results.d.ts +17 -2
- package/dist/results.d.ts.map +1 -1
- package/dist/results.js.map +1 -1
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +9 -6
- package/dist/schemas.js.map +1 -1
- package/dist/state.d.ts +66 -5
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +2 -1
- package/dist/state.js.map +1 -1
- package/dist/trust/continuity.d.ts.map +1 -1
- package/dist/trust/continuity.js +7 -1
- package/dist/trust/continuity.js.map +1 -1
- package/dist/trust/entries.d.ts.map +1 -1
- package/dist/trust/entries.js +103 -2
- package/dist/trust/entries.js.map +1 -1
- package/dist/views.d.ts +34 -2
- package/dist/views.d.ts.map +1 -1
- package/dist/views.js +19 -1
- package/dist/views.js.map +1 -1
- package/dist/wire.d.ts +47 -6
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js.map +1 -1
- package/package.json +6 -2
- 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`, `
|
|
71
|
-
|
|
72
|
-
`
|
|
73
|
-
`
|
|
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`, `
|
|
90
|
-
`
|
|
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.
|
|
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.
|
|
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`,
|
|
149
|
-
`test/fixtures/gen-state-v1.mjs
|
|
150
|
-
|
|
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
|
|
153
|
-
|
|
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`
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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":"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|