@real-life/trust-protocol 0.5.0 → 0.6.1
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 +119 -0
- package/README.md +97 -34
- package/dist/adapters/vti-mediator.d.ts +18 -5
- package/dist/adapters/vti-mediator.d.ts.map +1 -1
- package/dist/adapters/vti-mediator.js +143 -17
- package/dist/adapters/vti-mediator.js.map +1 -1
- package/dist/codec.d.ts +2 -9
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +61 -96
- package/dist/codec.js.map +1 -1
- package/dist/engine.d.ts +66 -2
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +393 -23
- package/dist/engine.js.map +1 -1
- package/dist/holder-api.d.ts +22 -2
- package/dist/holder-api.d.ts.map +1 -1
- package/dist/holder-api.js +43 -3
- package/dist/holder-api.js.map +1 -1
- package/dist/{holder-state-v2.d.ts → holder-state-v3.d.ts} +210 -207
- package/dist/holder-state-v3.d.ts.map +1 -0
- package/dist/holder-state-v3.js +2 -0
- package/dist/holder-state-v3.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -2
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/results.d.ts +42 -3
- package/dist/results.d.ts.map +1 -1
- package/dist/results.js.map +1 -1
- package/dist/state.d.ts +22 -9
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js.map +1 -1
- package/package.json +4 -6
- package/schemas/{holder-state-v2.schema.json → holder-state-v3.schema.json} +50 -5
- package/dist/holder-state-v1.d.ts +0 -801
- package/dist/holder-state-v1.d.ts.map +0 -1
- package/dist/holder-state-v1.js +0 -2
- package/dist/holder-state-v1.js.map +0 -1
- package/dist/holder-state-v2.d.ts.map +0 -1
- package/dist/holder-state-v2.js +0 -2
- package/dist/holder-state-v2.js.map +0 -1
- package/schemas/holder-state-v1.schema.json +0 -3880
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,124 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.1
|
|
4
|
+
|
|
5
|
+
One actId, one result.
|
|
6
|
+
|
|
7
|
+
- **`replay(actId, when)`**: the journaled result of a human act without
|
|
8
|
+
the act — `{ok: true, result: {actId, commitSeq, at, events, outbound}}`,
|
|
9
|
+
each document of the act with `live` (whether a repeat at `when` would
|
|
10
|
+
hand it out), or `{ok: false, code: 'unknown-act'}` (`actid-missing` for
|
|
11
|
+
an empty id). For `sendMessage` the `message.sent` event carries the
|
|
12
|
+
`mid` and the `digest`; `at` is the commit time the message's `sentAt`
|
|
13
|
+
was taken from. A pure read in the holder's queue: nothing runs, nothing
|
|
14
|
+
is given up, nothing commits, lastWhen included; no key is used, so it
|
|
15
|
+
answers on an opening whose keys are locked. New types `ReplayResult`,
|
|
16
|
+
`ActReplay`, `ReplayOutbound`.
|
|
17
|
+
- **The rule, stated and tested** (it held before; the engine takes one
|
|
18
|
+
path for every human act, and the tests cover `sendMessage`, `show`,
|
|
19
|
+
`scan`, `setProfile`, `foundGroup`, `block`, `unblock` and `trust`): a repeat under a journaled actId answers the
|
|
20
|
+
journaled events — for `sendMessage` the same `mid` — whatever the clock
|
|
21
|
+
says (ahead, set back), across `close()` and a new opening, with keys
|
|
22
|
+
that answer nothing, and after a commit whose outcome was unknown; it
|
|
23
|
+
never mints a second `mid`. Another input under the same actId stays
|
|
24
|
+
`actid-conflict`.
|
|
25
|
+
- **The journal keeps an act as long as its documents' completion**: the
|
|
26
|
+
retention max(P90D, the retry horizon) now runs from the act's commit
|
|
27
|
+
and from the last step of each of its documents (delivered or failed),
|
|
28
|
+
whichever is later; a document not yet terminal keeps the act. Before,
|
|
29
|
+
it ran from the commit alone, so an act delivered late left the journal
|
|
30
|
+
while its delivery status stayed. The bound is in holder time and of
|
|
31
|
+
time, not of count: one entry per human act, removed by the tick that is
|
|
32
|
+
due. A clock set ahead by more than the retention ends it early in real time; `replay` then answers `unknown-act`, and
|
|
33
|
+
a repeat under that actId would be a new act. An app asks `replay`
|
|
34
|
+
instead of repeating an act blind.
|
|
35
|
+
- **Fixes**: a tick that drops journal entries keeps an entry whose actId
|
|
36
|
+
is `__proto__` as an own member (it set the prototype of the kept
|
|
37
|
+
journal, and the commit failed the codec); the settling after an
|
|
38
|
+
`unknown-commit-outcome` counts the act as landed only when the journal
|
|
39
|
+
holds it with the same events, not another opening's commit of the same
|
|
40
|
+
actId at the same commitSeq; an act that issues documents names in
|
|
41
|
+
`nextDueAt` each one's first retry or send deadline, whichever comes
|
|
42
|
+
first (a repeat names its live documents' the same way), so a holder
|
|
43
|
+
whose timer had run out reaches the retries, the give-up and the
|
|
44
|
+
journal's end on the timers alone. An act without documents names no
|
|
45
|
+
journal deadline; the next `resume` names its entry's end, a `tick`
|
|
46
|
+
does not.
|
|
47
|
+
- The state and codec 3 are unchanged: the journal was already part of the
|
|
48
|
+
stored state.
|
|
49
|
+
|
|
50
|
+
## 0.6.0
|
|
51
|
+
|
|
52
|
+
Takes up Delivery Contract 0.83: the holder keeps custody of what waits
|
|
53
|
+
for transport, and only an open, resumed opening hands anything over.
|
|
54
|
+
|
|
55
|
+
- **Custody** (RLTP-DEL-6120 to 6135): `pendingTransport(when)` names
|
|
56
|
+
what the holder keeps for transport, after this opening's first
|
|
57
|
+
completed resume (else `{ok: false, code: 'not-resumed'}`): every
|
|
58
|
+
outbox document neither terminal nor accepted, the ones the adapter
|
|
59
|
+
reported `awaiting-transport` included, and every unsent ack, each with
|
|
60
|
+
its envelope, recipient, `since` (first submission), `notAfter` (the
|
|
61
|
+
horizon or the type's sender lifetime, whichever first), `held` and
|
|
62
|
+
`nextAttemptAt`; none past its deadline, whether or not a resume has
|
|
63
|
+
recorded the give-up. Entries are deep-frozen and issued to the
|
|
64
|
+
opening. `handOver(entry, when, start)` re-reads the store and, for an
|
|
65
|
+
entry this opening issued that is still pending, unchanged and before
|
|
66
|
+
its deadline, calls `start` inside the holder's queue: the check and the
|
|
67
|
+
start of the deposit are one step. `mayHandOver(entry, when)` is the
|
|
68
|
+
check alone, and authorizes nothing afterwards. Both read at
|
|
69
|
+
max(when, lastWhen): the holder's time never runs back. A resume that throws, the settling of an earlier commit
|
|
70
|
+
included, withdraws the opening's authority until one completes. One
|
|
71
|
+
opening per store at a time is the app's to keep: two openings are not
|
|
72
|
+
serialized with each other.
|
|
73
|
+
- **A duplicate's answer** (RLTP-DEL-6330): a duplicate makes an answer
|
|
74
|
+
due, stored with the ack (`answers`, `answerDue`) and so kept across a
|
|
75
|
+
lock; `pendingTransport` offers it as a `resend` entry numbered by it,
|
|
76
|
+
beside the ack's retries and independent of their state, with the
|
|
77
|
+
effect's retention as deadline. The begun hand-over ends it (a commit);
|
|
78
|
+
a duplicate arriving while one is due is answered by it (the same
|
|
79
|
+
bytes), the next one after it makes the next due. A duplicate therefore
|
|
80
|
+
commits that answer (else, as before, lastWhen only).
|
|
81
|
+
- **The opening ends**: `await close()` (a lock, the end of a process),
|
|
82
|
+
resolved once the operation running at the call has finished; only then
|
|
83
|
+
may the next opening start. Every
|
|
84
|
+
later call rejects with `InfraError('closed')` (a new code),
|
|
85
|
+
`mayHandOver` answers false, `closed` says so. The next opening is a new
|
|
86
|
+
`createHolder` over the same store.
|
|
87
|
+
- **Acks** (RLTP-DEL-6330, 6335): an `awaiting-transport` report on an
|
|
88
|
+
ack no longer marks it sent; it records `held`, resume stops re-offering
|
|
89
|
+
it and `pendingTransport` keeps it. `accepted` or `delivered` marks it
|
|
90
|
+
sent (`sentBy: 'accepted'`), `failed` gives it up (`'failed'`), the
|
|
91
|
+
horizon ends its retries (`'horizon'`). Sent or not, it stays with its
|
|
92
|
+
effect until the effect's retention; a duplicate re-sends it once
|
|
93
|
+
without new retries.
|
|
94
|
+
- **Codec 3, a clean cut**: `StoredAck.sentBy` is `accepted | failed |
|
|
95
|
+
horizon`, present exactly when sent, `held` only while unsent, and the
|
|
96
|
+
answers (`answers`, `answerDue`). Codec 3 replaces codecs 1 and 2
|
|
97
|
+
without a migration: neither ever held a rolled-out app's state, so a
|
|
98
|
+
state of either is refused as `codec-unknown`. Removed: the migrations
|
|
99
|
+
1 → 2 and 2 → 3 (`MIGRATIONS` is empty), the codec-1 and codec-2
|
|
100
|
+
validators, `schemas/holder-state-v1.schema.json`,
|
|
101
|
+
`schemas/holder-state-v2.schema.json`, the types `HolderStateV1` and
|
|
102
|
+
`HolderStateV2`, and their fixtures and generators. New:
|
|
103
|
+
`schemas/holder-state-v3.schema.json`, `HolderStateV3`, fixtures
|
|
104
|
+
`state-v3*.json` (generated, reproduced in `npm test`).
|
|
105
|
+
- **`./adapters/vti-mediator`**: `reconcile(view, gate, now)` replaces
|
|
106
|
+
`submitOutbound`. It files the entries of the view not yet filed and
|
|
107
|
+
withdraws every submission before the trias whose digest the view no
|
|
108
|
+
longer names; each deposit starts inside `gate.handOver` (the holder),
|
|
109
|
+
after the adapter's own re-check (retired, withdrawn, past the trias,
|
|
110
|
+
past the entry's deadline or its horizon): the holder's no withdraws,
|
|
111
|
+
an error skips the attempt. Its give-up horizon counts from the entry's
|
|
112
|
+
`since`; an entry taken over past it is given up at once, never
|
|
113
|
+
attempted. A `resend` entry is a submission of its own. One adapter
|
|
114
|
+
serves one opening: bound to the holder of its first reconcile, it
|
|
115
|
+
refuses another, and the next opening gets a new adapter. It keeps no
|
|
116
|
+
custody of its own.
|
|
117
|
+
- Tests: the custody scenarios (`test/transport-custody.test.mjs`, among
|
|
118
|
+
them the open counter-probes of the store-launch reviews) and seeded
|
|
119
|
+
interleavings of locks, the end of a process, hand-overs and acks with
|
|
120
|
+
every cell reached (`test/transport-interleavings.test.mjs`).
|
|
121
|
+
|
|
3
122
|
## 0.5.0
|
|
4
123
|
|
|
5
124
|
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,26 @@ 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`, `replay`, `snapshot`, `subscribe`.
|
|
122
|
+
|
|
123
|
+
One actId, one result: every human act is journaled under its actId with
|
|
124
|
+
its result. A repeat under the same actId answers the journaled events
|
|
125
|
+
(for `sendMessage` the same `mid`) at any clock and across a lock and a
|
|
126
|
+
new opening; it never runs the act again, and another input under the
|
|
127
|
+
same actId is `actid-conflict`. `replay(actId, when)` reads that result
|
|
128
|
+
without the act: `{ok: true, result: {actId, commitSeq, at, events,
|
|
129
|
+
outbound}}` (each document with `live`: whether a repeat at `when` would
|
|
130
|
+
hand it out), or `{ok: false, code: 'unknown-act'}`. It commits nothing and
|
|
131
|
+
needs no keys. The journal keeps an act for max(P90D, the retry horizon)
|
|
132
|
+
after its commit and after the last step of each of its documents
|
|
133
|
+
(delivered or failed; one not yet terminal keeps it), counted in holder
|
|
134
|
+
time and removed by the tick that is due (an act without documents
|
|
135
|
+
names no deadline of its own; the next `resume` names it, a `tick`
|
|
136
|
+
does not); after that its actId is unknown. An app that does not know whether an act committed (a crash, an
|
|
137
|
+
`unknown-commit-outcome`, a lock) asks `replay` and does not repeat the act
|
|
138
|
+
blind: `unknown-act` means the act did not commit or has left the
|
|
139
|
+
journal, and acting anew is the human's decision. Custody:
|
|
140
|
+
`pendingTransport`, `handOver`, `mayHandOver`, `close`, `closed`.
|
|
75
141
|
|
|
76
142
|
## What the root exports
|
|
77
143
|
|
|
@@ -124,7 +190,7 @@ Bare four-digit numbers are RLTP-TRU rules; the other layers name theirs.
|
|
|
124
190
|
| `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
191
|
| `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
192
|
| `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.
|
|
193
|
+
| `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
194
|
| `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
195
|
| `views`, `prompts` | — | the read model: contacts by entry (2120, 2140), groups, prompts, the delivery status |
|
|
130
196
|
|
|
@@ -149,39 +215,36 @@ A holder's state is pure JSON under a codec version. The Store port takes
|
|
|
149
215
|
the state as opaque JSON and gives it back; the library decodes it
|
|
150
216
|
(`decodeState`), the adapter never migrates.
|
|
151
217
|
|
|
152
|
-
- **Codec
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
test.
|
|
218
|
+
- **Codec 3** is the format of 0.6.0 and the only one this library
|
|
219
|
+
reads. It holds the state of Message Tasks 0.1 (`messages`: the block,
|
|
220
|
+
the rates, the ready tuples, the completion record, the inbox), an
|
|
221
|
+
outbox item's sender lifetime (`expiresAt`) and `failed(task-expired)`,
|
|
222
|
+
the record of surfaced and the register of declined invitations, and
|
|
223
|
+
per stored ack (Delivery 0.83) why it counts as sent (`sentBy`:
|
|
224
|
+
`accepted`, `failed`, `horizon`, present exactly when `sent`), whether
|
|
225
|
+
the adapter holds it (`held`), and the duplicates' answers (`answers`,
|
|
226
|
+
`answerDue`). Its fixtures (`test/fixtures/state-v3*.json`,
|
|
227
|
+
deterministic runs of `test/fixtures/gen-state-v3.mjs`, reproduced in
|
|
228
|
+
`npm test`) cover a settled holder, one with work open (prompts, a
|
|
229
|
+
joining invite, duties, a rotation at step 2, a block and a declined
|
|
230
|
+
invitation), one with a tombstone, a revoked device and carrier entries,
|
|
231
|
+
and one with transport in custody (acks held, accepted, failed and
|
|
232
|
+
unsent, an answer due and one handed over, an own document held); each
|
|
233
|
+
decodes and re-encodes byte for byte.
|
|
234
|
+
- Codecs 1 and 2 (lib 0.4.0, 0.5.0) never held a rolled-out app's state:
|
|
235
|
+
they are read no more, a state of either is `codec-unknown`. A later
|
|
236
|
+
codec that must read codec 3 brings its validator, a pure migration from
|
|
237
|
+
it, and its own fixture with a migration test.
|
|
173
238
|
- A newer codec is refused as `codec-newer`, a codec without a validator
|
|
174
239
|
as `codec-unknown`; nothing is read best-effort.
|
|
175
|
-
- `schemas/holder-state-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
`
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
envelope's `rkid` equals its recipient's `keyAgreement`, and (codec 2) a
|
|
184
|
-
set of strings is sorted.
|
|
240
|
+
- `schemas/holder-state-v3.schema.json` describes the codec as a
|
|
241
|
+
non-normative JSON Schema (published with the package). The type
|
|
242
|
+
`HolderStateV3` (`./internal`) is generated from it
|
|
243
|
+
(`scripts/build-holder-state-types.mjs`); the build holds it against the
|
|
244
|
+
library's `HolderState`, and the tests hold the schema against the
|
|
245
|
+
codec's validator on the fixtures and on mutations of them. Rules only
|
|
246
|
+
the codec states: a record's id equals its key, an envelope's `rkid`
|
|
247
|
+
equals its recipient's `keyAgreement`, and a set of strings is sorted.
|
|
185
248
|
|
|
186
249
|
**Writing a Store adapter.** Implement `read(): Promise<{state, version}>`
|
|
187
250
|
(a deep, independent copy; `null` state and version for an empty store) and
|
|
@@ -224,7 +287,7 @@ This package uses its own semver; the specification uses castings.
|
|
|
224
287
|
| trust | Trust Layer 0.31 | — |
|
|
225
288
|
| 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
289
|
| 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.
|
|
290
|
+
| 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
291
|
| 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
292
|
| devices | Identity §5.5 | multi-device production: one writer per holder |
|
|
230
293
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CarrierPrincipal, CarrierScopedIdentity } from '../carrier-identity.js';
|
|
2
|
-
import type { DeliveryReport,
|
|
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
|
-
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
|
|
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":"
|
|
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
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
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
|
-
/**
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
|
|
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 (
|
|
343
|
-
throw new Error('
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
}
|