@cello-protocol/daemon 0.0.167 → 0.0.169

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 (80) hide show
  1. package/dist/daemon.d.ts.map +1 -1
  2. package/dist/daemon.js +115 -254
  3. package/dist/daemon.js.map +1 -1
  4. package/dist/document-amendment-store.d.ts +94 -46
  5. package/dist/document-amendment-store.d.ts.map +1 -1
  6. package/dist/document-amendment-store.js +217 -102
  7. package/dist/document-amendment-store.js.map +1 -1
  8. package/dist/document-delivery-transport.d.ts +41 -52
  9. package/dist/document-delivery-transport.d.ts.map +1 -1
  10. package/dist/document-delivery-transport.js +6 -156
  11. package/dist/document-delivery-transport.js.map +1 -1
  12. package/dist/document-frame-router.d.ts +13 -17
  13. package/dist/document-frame-router.d.ts.map +1 -1
  14. package/dist/document-frame-router.js +21 -93
  15. package/dist/document-frame-router.js.map +1 -1
  16. package/dist/document-handlers.d.ts +1 -1
  17. package/dist/document-handlers.d.ts.map +1 -1
  18. package/dist/document-handlers.js +471 -466
  19. package/dist/document-handlers.js.map +1 -1
  20. package/dist/document-handshake.d.ts +4 -1
  21. package/dist/document-handshake.d.ts.map +1 -1
  22. package/dist/document-handshake.js +3 -1
  23. package/dist/document-handshake.js.map +1 -1
  24. package/dist/document-inbound.d.ts +18 -5
  25. package/dist/document-inbound.d.ts.map +1 -1
  26. package/dist/document-inbound.js +94 -110
  27. package/dist/document-inbound.js.map +1 -1
  28. package/dist/document-layer.d.ts +28 -67
  29. package/dist/document-layer.d.ts.map +1 -1
  30. package/dist/document-layer.js +670 -565
  31. package/dist/document-layer.js.map +1 -1
  32. package/dist/document-lifecycle.d.ts +11 -157
  33. package/dist/document-lifecycle.d.ts.map +1 -1
  34. package/dist/document-lifecycle.js +26 -561
  35. package/dist/document-lifecycle.js.map +1 -1
  36. package/dist/document-publish.d.ts +13 -0
  37. package/dist/document-publish.d.ts.map +1 -1
  38. package/dist/document-publish.js +19 -11
  39. package/dist/document-publish.js.map +1 -1
  40. package/dist/document-reconcile-engine.d.ts +66 -0
  41. package/dist/document-reconcile-engine.d.ts.map +1 -0
  42. package/dist/document-reconcile-engine.js +225 -0
  43. package/dist/document-reconcile-engine.js.map +1 -0
  44. package/dist/document-reconcile-scheduler.d.ts +91 -0
  45. package/dist/document-reconcile-scheduler.d.ts.map +1 -0
  46. package/dist/document-reconcile-scheduler.js +153 -0
  47. package/dist/document-reconcile-scheduler.js.map +1 -0
  48. package/dist/document-rejection.d.ts +2 -0
  49. package/dist/document-rejection.d.ts.map +1 -1
  50. package/dist/document-rejection.js +5 -1
  51. package/dist/document-rejection.js.map +1 -1
  52. package/dist/document-store.d.ts +18 -220
  53. package/dist/document-store.d.ts.map +1 -1
  54. package/dist/document-store.js +95 -717
  55. package/dist/document-store.js.map +1 -1
  56. package/dist/initiate-session-handler.d.ts +5 -0
  57. package/dist/initiate-session-handler.d.ts.map +1 -1
  58. package/dist/initiate-session-handler.js +1 -0
  59. package/dist/initiate-session-handler.js.map +1 -1
  60. package/package.json +5 -5
  61. package/dist/delivery-sweep-bound.d.ts +0 -79
  62. package/dist/delivery-sweep-bound.d.ts.map +0 -1
  63. package/dist/delivery-sweep-bound.js +0 -109
  64. package/dist/delivery-sweep-bound.js.map +0 -1
  65. package/dist/document-ack-inbound.d.ts +0 -71
  66. package/dist/document-ack-inbound.d.ts.map +0 -1
  67. package/dist/document-ack-inbound.js +0 -210
  68. package/dist/document-ack-inbound.js.map +0 -1
  69. package/dist/document-control-notifier.d.ts +0 -107
  70. package/dist/document-control-notifier.d.ts.map +0 -1
  71. package/dist/document-control-notifier.js +0 -137
  72. package/dist/document-control-notifier.js.map +0 -1
  73. package/dist/document-delivery.d.ts +0 -229
  74. package/dist/document-delivery.d.ts.map +0 -1
  75. package/dist/document-delivery.js +0 -449
  76. package/dist/document-delivery.js.map +0 -1
  77. package/dist/document-join-store.d.ts +0 -80
  78. package/dist/document-join-store.d.ts.map +0 -1
  79. package/dist/document-join-store.js +0 -197
  80. package/dist/document-join-store.js.map +0 -1
@@ -1,210 +0,0 @@
1
- /**
2
- * DOD-DOC-INBOUND-2 — consuming an arriving ACK. This is what closes DELIVERY-2's loop.
3
- *
4
- * Without it a sent envelope's outcome is `admitted: null` forever: the worker knows the content
5
- * left and nothing more, so it re-sends on the ack timeout and eventually stalls the document at
6
- * the unacked ceiling.
7
- *
8
- * ── EVERY CHECK HERE GUARDS ONE OF TWO IRREVERSIBLE CONSEQUENCES ──────────────────────────────
9
- *
10
- * An ack SETTLES an envelope. Admitted, it stops being redelivered; rejected, the sender rolls back
11
- * local work and supersedes. Both are things an unauthenticated party must not be able to cause, so
12
- * the order is:
13
- *
14
- * 1. DECODE — refuse malformed input before interpreting any of it.
15
- * 2. KNOW the document — an ack for a document we do not hold settles nothing that exists.
16
- * 3. ACKER is the peer — a document is a pairwise agreement; nobody else's answer settles it.
17
- * 4. VERIFY — before any write. An unsigned ack that silenced a delivery would drop
18
- * the content from the pending set with neither operator ever learning it
19
- * was never applied — the silent divergence the two-layer design exists to
20
- * prevent.
21
- * 5. THE ENVELOPE IS OURS — we authored it, and it is in the log. An ack for a peer-authored
22
- * envelope settles a delivery that was never ours to make; an ack for an
23
- * envelope that is not there is a bug or a probe, and recording it would
24
- * put a claim about a nonexistent delivery into a permanent record.
25
- * 6. SETTLE — mark acked; on a rejection also record it durably on the publishing side,
26
- * because an operator needs to know WHY their work was refused after a
27
- * restart, not merely that it was.
28
- */
29
- import { decodeDocumentAck, buildDocumentAckTbs } from "@cello-protocol/protocol-types";
30
- export class DocumentAckInbound {
31
- #d;
32
- constructor(deps) {
33
- this.#d = deps;
34
- }
35
- receive(ownerAgentId, wire, nowMs, correlationId = "ack") {
36
- // 1. DECODE.
37
- let ack;
38
- try {
39
- ack = decodeDocumentAck(wire);
40
- }
41
- catch (err) {
42
- const detail = err instanceof Error ? err.message : String(err);
43
- this.#d.logger.warn("document.ack.malformed", { detail, correlationId });
44
- // One reason code with the upstream message in the detail — the decoder's named refusals and
45
- // raw CBOR errors both arrive here, and building a code out of the message turns the second
46
- // kind into English prose nothing can match on.
47
- return { ok: false, reason: "document_ack_malformed", detail };
48
- }
49
- // 2. KNOW the document.
50
- const doc = this.#d.store.getDocument(ownerAgentId, ack.document_id);
51
- if (!doc) {
52
- return {
53
- ok: false,
54
- reason: "document_unknown",
55
- detail: `no document ${ack.document_id.slice(0, 16)}… for this agent`,
56
- };
57
- }
58
- // 3. The acker must be a party. When the chain DERIVES, derived membership is the WHOLE
59
- // gate — a removed genesis peer's ack refuses like any outsider's (review H2). Only when the
60
- // chain cannot answer (bilateral legacy, no genesis record) does the genesis-peer column
61
- // stand in.
62
- const holders = this.#d.currentHolders(ownerAgentId, ack.document_id);
63
- const isParty = holders === null
64
- ? ack.acker_agent_id === doc.peerAgentId
65
- : holders.includes(ack.acker_agent_id);
66
- if (!isParty) {
67
- this.#d.logger.warn("document.ack.not_peer", {
68
- documentId: ack.document_id,
69
- ackerAgentId: ack.acker_agent_id,
70
- peerAgentId: doc.peerAgentId,
71
- correlationId,
72
- });
73
- return {
74
- ok: false,
75
- reason: "document_ack_not_peer",
76
- // The peer's identity is not disclosed to a party that has not authenticated.
77
- detail: "you are not a party to this document",
78
- };
79
- }
80
- // 4. VERIFY, before any write.
81
- if (!this.#d.verifySignature(ack.acker_agent_id, buildDocumentAckTbs(ack), ack.signature)) {
82
- this.#d.logger.error("document.ack.signature_invalid", {
83
- documentId: ack.document_id,
84
- ackerAgentId: ack.acker_agent_id,
85
- envelopeHash: ack.envelope_hash,
86
- correlationId,
87
- });
88
- return {
89
- ok: false,
90
- reason: "document_ack_signature_invalid",
91
- detail: `the ack claims to come from ${ack.acker_agent_id} but its signature does not verify ` +
92
- `against that agent — nothing was settled`,
93
- };
94
- }
95
- // 5. The envelope must be one WE authored, and it must exist.
96
- const row = this.#d.store
97
- .getEnvelopeLog(ownerAgentId, ack.document_id)
98
- .find((e) => e.envelopeHash === ack.envelope_hash);
99
- if (!row) {
100
- return {
101
- ok: false,
102
- reason: "document_ack_envelope_unknown",
103
- detail: `envelope ${ack.envelope_hash.slice(0, 16)}… is not in this document's log`,
104
- };
105
- }
106
- if (row.senderAgentId !== ownerAgentId) {
107
- return {
108
- ok: false,
109
- reason: "document_ack_not_author",
110
- detail: `envelope ${ack.envelope_hash.slice(0, 16)}… was authored by ${row.senderAgentId}, not by ` +
111
- `you — there is no delivery of ours for this ack to settle`,
112
- };
113
- }
114
- // 6. SETTLE ONCE. The wire type's own header states this contract: nothing binds an ack to the
115
- // acker's chain, so one acker can produce a valid ADMISSION and a valid REJECTION for the same
116
- // envelope. Applying the later one would let a peer that admitted an envelope later claim it
117
- // refused it — and the sender would roll back work the peer already holds. A second,
118
- // CONTRADICTING ack is an error; a second identical one is an ordinary redelivery.
119
- // SETTLE-ONCE, PER ACKER (review M4). The envelope-level column stays null until ALL
120
- // holders answer, so an envelope-scoped check let a holder that ADMITTED later send a
121
- // REJECTION mid-fan-out — processed, rounds advanced, work rolled back that they already
122
- // hold. The acker's own per-holder row is the record; the envelope-level check remains only
123
- // for the zero-row bilateral legacy.
124
- const holderPrior = this.#d.store.holderSettlement(ownerAgentId, ack.envelope_hash, ack.acker_agent_id);
125
- const alreadyAcked = holderPrior !== null || row.ackedAtMs != null;
126
- if (alreadyAcked) {
127
- // ENVELOPE-scoped. The document-scoped reader answers a different question, and using it
128
- // here would make a rejection of any OTHER envelope in this document read as a rejection of
129
- // this one — so an honest redelivered admission would be refused as a contradiction the
130
- // moment anything else in the document had ever been refused.
131
- const previouslyRejected = holderPrior !== null
132
- ? !holderPrior.admitted
133
- : this.#d.store.rejectionReceivedFor(ownerAgentId, ack.document_id, ack.envelope_hash);
134
- const contradicts = ack.admitted === previouslyRejected;
135
- if (contradicts) {
136
- this.#d.logger.error("document.ack.contradiction", {
137
- documentId: ack.document_id,
138
- envelopeHash: ack.envelope_hash,
139
- ackerAgentId: ack.acker_agent_id,
140
- nowSays: ack.admitted ? "admitted" : "rejected",
141
- correlationId,
142
- });
143
- return {
144
- ok: false,
145
- reason: "document_ack_contradiction",
146
- detail: `${ack.acker_agent_id} already settled envelope ${ack.envelope_hash.slice(0, 16)}… and ` +
147
- `now says the opposite — this envelope stays settled as it was, and both claims are ` +
148
- `retained`,
149
- };
150
- }
151
- // An identical redelivery. Nothing to do, and nothing to complain about.
152
- return { ok: true, admitted: ack.admitted, envelopeHash: ack.envelope_hash };
153
- }
154
- // FANOUT-1: the ACKING HOLDER settles THEIR row; the envelope-level record follows the ONE
155
- // deterministic terminal rule (`reconcileEnvelopeSettlement` — review M3), identical from
156
- // every reconciliation site. Bilateral legacy (zero rows) stays byte-identical: the
157
- // reconcile no-ops and markAcked below still runs through it.
158
- this.#d.store.ackHolderDelivery(ownerAgentId, ack.document_id, ack.envelope_hash, ack.acker_agent_id, nowMs);
159
- const before = row.ackedAtMs != null;
160
- this.#d.store.reconcileEnvelopeSettlement(ownerAgentId, ack.document_id, ack.envelope_hash, nowMs);
161
- const legacy = this.#d.store.holderSettlement(ownerAgentId, ack.envelope_hash, ack.acker_agent_id) === null;
162
- const first = legacy
163
- ? this.#d.store.markAcked(ownerAgentId, ack.document_id, ack.envelope_hash, nowMs)
164
- : !before;
165
- // ANNOUNCE THE SETTLE, before the admitted/rejected split — both outcomes end the delivery, and
166
- // a waiter that only heard about admissions would keep a session open through every rejection.
167
- this.#d.onSettled?.(ownerAgentId, ack.envelope_hash, ack.acker_agent_id, ack.admitted);
168
- if (!ack.admitted) {
169
- // Durable on the publishing side. A log line would not survive the restart after which the
170
- // operator asks why their work never landed — and this is also what bounds the retry on the
171
- // side that actually loops.
172
- this.#d.rejections.recordIncomingRejection(ownerAgentId, ack.document_id, {
173
- rejectionEnvelopeHash: ackRecordHash(ack.envelope_hash),
174
- rejectedEnvelopeHash: ack.envelope_hash,
175
- reason: ack.rejection_reason ?? "document_rejected",
176
- fromAgentId: ack.acker_agent_id,
177
- });
178
- return { ok: true, admitted: false, envelopeHash: ack.envelope_hash };
179
- }
180
- this.#d.logger.info("document.ack.admitted", {
181
- documentId: ack.document_id,
182
- envelopeHash: ack.envelope_hash,
183
- firstAck: first,
184
- correlationId,
185
- });
186
- return { ok: true, admitted: true, envelopeHash: ack.envelope_hash };
187
- }
188
- }
189
- /**
190
- * The identity of the RECEIVED-rejection row: the REFUSED ENVELOPE, and nothing else.
191
- *
192
- * ONE REFUSED ENVELOPE IS ONE ROUND, however many acks for it arrive.
193
- *
194
- * This used to mix in the acker's `acked_at_ms` on the reasoning that both fields are signed, so a
195
- * redelivered ack would collapse onto the same row. That reasoning had a false premise: acks are
196
- * not stored and redelivered — `sendAck` builds a fresh `DocumentAck` with `acked_at_ms: Date.now()`
197
- * every time it answers. So two acks for the SAME envelope carried two different timestamps and
198
- * became two rows, advancing the round twice.
199
- *
200
- * That is not hypothetical. Delivery re-sends on a 1s first backoff, so an ordinary in-flight
201
- * overlap — the sender re-sends while the first ack is still on the wire, both land — produced two
202
- * rounds from one refusal. `MAX_REJECTED_ROUNDS` is 3, so routine delivery overlap could stall a
203
- * shared document with no hostility involved. That is the same denial of service the 5b
204
- * already-rejected guard in `document-inbound.ts` was written to close, arriving through the other
205
- * side of the same loop.
206
- */
207
- function ackRecordHash(envelopeHash) {
208
- return envelopeHash;
209
- }
210
- //# sourceMappingURL=document-ack-inbound.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"document-ack-inbound.js","sourceRoot":"","sources":["../src/document-ack-inbound.ts"],"names":[],"mappings":"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"}
@@ -1,107 +0,0 @@
1
- /**
2
- * DOD-DOC-TOOLS-1 — telling the peer a document has ended.
3
- *
4
- * `DocumentLifecycle` ends a document locally without asking anyone, deliberately: a kill is a
5
- * safety verb, and a safety verb that needs the counterparty's cooperation is not one. But a peer
6
- * who is never told keeps publishing into a document that will never answer — their updates refused
7
- * at the far end forever, with nothing on their screen explaining why. So the notification is
8
- * REQUIRED to be attempted, ALLOWED to fail, and the operator is told which happened.
9
- *
10
- * ── WHY THIS IS A MODULE AND NOT A CLOSURE IN THE COMPOSITION ROOT ────────────────────────────
11
- *
12
- * It was one, and that is precisely how the surface tests passed while the feature did nothing.
13
- * `createDocumentLayer` takes `notifyPeer` as an injected seam, so the two-party test wired it to
14
- * `async () => ({ ok: true })` — which reported success, sent nothing, and agreed with whatever the
15
- * near side did. Kill "worked" on both sides of the assertion and on neither side of the wire.
16
- *
17
- * That is the same shape as the two defects the DELIVERY-2 review found: a stub on the far side
18
- * cannot disagree with you. So the construction lives here, and both the daemon and the test build
19
- * it from this function — the test can still substitute the TRANSPORT, which is the part that has
20
- * to be substituted, without also substituting the logic under test.
21
- *
22
- * ── WHY THE DOCUMENT IS FOUND BY SCANNING ─────────────────────────────────────────────────────
23
- *
24
- * `notifyPeer`'s signature carries no owner: lifecycle calls it knowing only the document id, and
25
- * widening that interface would push a multi-agent daemon concern into a unit that deliberately has
26
- * none. So the owner is resolved here, by asking each agent this daemon holds whether the document
27
- * is theirs. A document id is a hash committing to both parties and a nonce, so at most one agent
28
- * can answer.
29
- *
30
- * ── DOD-MP-CONTROL-N-1 — WHY THE TARGET IS DERIVED, NOT `peerAgentId` ─────────────────────────
31
- *
32
- * This addressed `doc.peerAgentId` and returned after one send. That column is the GENESIS
33
- * counterparty, frozen at creation, and multiplayer made it wrong in two ways at once: with three
34
- * holders only one was told, and after a removal the genesis counterparty can BE the removed
35
- * holder — so the frame went to the one party it must not reach while the remaining co-author was
36
- * never told, under a reported `peerNotified: true`. Found by the M14B live fleet smoke.
37
- *
38
- * The target is now the DERIVED participant set, the same source FANOUT-1 established for update
39
- * envelopes; control frames were simply never migrated to it. Deriving needs the genesis proposal,
40
- * the amendment chain and signature verification — all of which live above this module, so it
41
- * arrives as an injected `holders()` rather than by widening this unit into the replay engine.
42
- *
43
- * A derivation failure REFUSES. It must never fall back to `peerAgentId`: that is the old bug, and
44
- * it aims the frame at exactly the wrong party in the case that motivated the change.
45
- */
46
- import { type DocumentControlVerb } from "@cello-protocol/protocol-types";
47
- import type { DocumentStore } from "./document-store.js";
48
- export interface DocumentControlNotifierDeps {
49
- store: DocumentStore;
50
- /** The agents this daemon holds — name for signing and sending, owner key for scoping the store. */
51
- owners(): ReadonlyArray<{
52
- agentName: string;
53
- ownerAgentId: string;
54
- }>;
55
- /**
56
- * The CURRENT holders of this document OTHER than the owner, derived from the genesis proposal
57
- * plus the amendment chain — the same set that governs update delivery. A failure to derive is
58
- * returned, not swallowed: this unit refuses rather than guessing a recipient.
59
- */
60
- holders(ownerAgentId: string, documentId: string): {
61
- ok: true;
62
- holders: readonly string[];
63
- } | {
64
- ok: false;
65
- reason: string;
66
- };
67
- /** Sign as the named agent. A miss must REFUSE, not substitute another agent's key. */
68
- sign(agentName: string, tbs: Uint8Array): Promise<Uint8Array>;
69
- send(agentName: string, input: {
70
- peerAgentId: string;
71
- documentId: string;
72
- bytes: Uint8Array;
73
- correlationId: string;
74
- }): Promise<{
75
- ok: true;
76
- } | {
77
- ok: false;
78
- reason: string;
79
- }>;
80
- now(): number;
81
- /**
82
- * Optional, and only for reporting a LOCAL fault. This module used to swallow the signing error
83
- * entirely, so the one description of "your key could not be loaded" existed nowhere — not in the
84
- * return value, not in a log line — and the operator was sent to wait for a peer who was already
85
- * there.
86
- */
87
- logger?: {
88
- warn(event: string, ctx: Record<string, unknown>): void;
89
- };
90
- }
91
- export type NotifyPeer = (documentId: string, verb: DocumentControlVerb) => Promise<{
92
- ok: true;
93
- holdersNotified: Record<string, boolean>;
94
- /**
95
- * WHY each holder did not get it, per holder. The boolean alone throws the cause away, and
96
- * the operator's sentence then has to guess — it guessed "most likely offline" for a
97
- * `session_sealed`, which waiting can never fix. That is the exact substitution
98
- * `notifyGuidance` exists to prevent, reintroduced one layer up.
99
- */
100
- holderFailures: Record<string, string>;
101
- } | {
102
- ok: false;
103
- reason: string;
104
- detail?: string;
105
- }>;
106
- export declare function createDocumentControlNotifier(deps: DocumentControlNotifierDeps): NotifyPeer;
107
- //# sourceMappingURL=document-control-notifier.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"document-control-notifier.d.ts","sourceRoot":"","sources":["../src/document-control-notifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAGH,OAAO,EAKL,KAAK,mBAAmB,EACzB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,aAAa,CAAC;IACrB,oGAAoG;IACpG,MAAM,IAAI,aAAa,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrE;;;;OAIG;IACH,OAAO,CACL,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5E,uFAAuF;IACvF,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC9D,IAAI,CACF,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,UAAU,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,GAC3F,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD,GAAG,IAAI,MAAM,CAAC;IACd;;;;;OAKG;IACH,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;KAAE,CAAC;CACtE;AAED,MAAM,MAAM,UAAU,GAAG,CACvB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,mBAAmB,KACtB,OAAO,CACR;IACE,EAAE,EAAE,IAAI,CAAC;IACT,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CACjD,CAAC;AAEF,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,2BAA2B,GAAG,UAAU,CAwF3F"}
@@ -1,137 +0,0 @@
1
- /**
2
- * DOD-DOC-TOOLS-1 — telling the peer a document has ended.
3
- *
4
- * `DocumentLifecycle` ends a document locally without asking anyone, deliberately: a kill is a
5
- * safety verb, and a safety verb that needs the counterparty's cooperation is not one. But a peer
6
- * who is never told keeps publishing into a document that will never answer — their updates refused
7
- * at the far end forever, with nothing on their screen explaining why. So the notification is
8
- * REQUIRED to be attempted, ALLOWED to fail, and the operator is told which happened.
9
- *
10
- * ── WHY THIS IS A MODULE AND NOT A CLOSURE IN THE COMPOSITION ROOT ────────────────────────────
11
- *
12
- * It was one, and that is precisely how the surface tests passed while the feature did nothing.
13
- * `createDocumentLayer` takes `notifyPeer` as an injected seam, so the two-party test wired it to
14
- * `async () => ({ ok: true })` — which reported success, sent nothing, and agreed with whatever the
15
- * near side did. Kill "worked" on both sides of the assertion and on neither side of the wire.
16
- *
17
- * That is the same shape as the two defects the DELIVERY-2 review found: a stub on the far side
18
- * cannot disagree with you. So the construction lives here, and both the daemon and the test build
19
- * it from this function — the test can still substitute the TRANSPORT, which is the part that has
20
- * to be substituted, without also substituting the logic under test.
21
- *
22
- * ── WHY THE DOCUMENT IS FOUND BY SCANNING ─────────────────────────────────────────────────────
23
- *
24
- * `notifyPeer`'s signature carries no owner: lifecycle calls it knowing only the document id, and
25
- * widening that interface would push a multi-agent daemon concern into a unit that deliberately has
26
- * none. So the owner is resolved here, by asking each agent this daemon holds whether the document
27
- * is theirs. A document id is a hash committing to both parties and a nonce, so at most one agent
28
- * can answer.
29
- *
30
- * ── DOD-MP-CONTROL-N-1 — WHY THE TARGET IS DERIVED, NOT `peerAgentId` ─────────────────────────
31
- *
32
- * This addressed `doc.peerAgentId` and returned after one send. That column is the GENESIS
33
- * counterparty, frozen at creation, and multiplayer made it wrong in two ways at once: with three
34
- * holders only one was told, and after a removal the genesis counterparty can BE the removed
35
- * holder — so the frame went to the one party it must not reach while the remaining co-author was
36
- * never told, under a reported `peerNotified: true`. Found by the M14B live fleet smoke.
37
- *
38
- * The target is now the DERIVED participant set, the same source FANOUT-1 established for update
39
- * envelopes; control frames were simply never migrated to it. Deriving needs the genesis proposal,
40
- * the amendment chain and signature verification — all of which live above this module, so it
41
- * arrives as an injected `holders()` rather than by widening this unit into the replay engine.
42
- *
43
- * A derivation failure REFUSES. It must never fall back to `peerAgentId`: that is the old bug, and
44
- * it aims the frame at exactly the wrong party in the case that motivated the change.
45
- */
46
- import { randomUUID } from "node:crypto";
47
- import { encodeDocumentControl, buildDocumentControlTbs, DOCUMENT_CONTROL_VERSION, } from "@cello-protocol/protocol-types";
48
- export function createDocumentControlNotifier(deps) {
49
- return async (documentId, verb) => {
50
- for (const { agentName, ownerAgentId } of deps.owners()) {
51
- const doc = deps.store.getDocument(ownerAgentId, documentId);
52
- if (!doc)
53
- continue;
54
- const targets = deps.holders(ownerAgentId, documentId);
55
- if (!targets.ok) {
56
- deps.logger?.warn("document.control.holders_underivable", {
57
- documentId, verb, agentName, reason: targets.reason,
58
- });
59
- return { ok: false, reason: targets.reason };
60
- }
61
- if (targets.holders.length === 0) {
62
- // Distinct from a failed send, and reported rather than returned as a vacuous success: an
63
- // empty map behind `ok: true` reads as "everyone was told" to a caller that checks only ok.
64
- return { ok: false, reason: "document_no_holders" };
65
- }
66
- const control = {
67
- type: "document_control",
68
- control_version: DOCUMENT_CONTROL_VERSION,
69
- document_id: documentId,
70
- sender_agent_id: ownerAgentId,
71
- verb,
72
- sent_at_ms: deps.now(),
73
- signature: new Uint8Array(0),
74
- };
75
- let signature;
76
- try {
77
- signature = await deps.sign(agentName, buildDocumentControlTbs(control));
78
- }
79
- catch (err) {
80
- // REFUSED, not skipped and not sent unsigned. The peer must reject an unsigned control
81
- // frame, so shipping one would report a notification that cannot possibly land — and the
82
- // caller reports `peerNotified` straight to the operator.
83
- //
84
- // THE MESSAGE IS KEPT. A bare `catch {}` here threw away the only description of a LOCAL
85
- // fault — a key file that moved, a locked keychain, an agent with no provider — and the
86
- // operator was then told their peer had not heard them and to try again when the peer was
87
- // back. Waiting cannot fix a signing failure. The reason travels up so the caller can say
88
- // which of the two it is.
89
- const detail = err instanceof Error ? err.message : String(err);
90
- deps.logger?.warn("document.control.unsigned", { documentId, verb, agentName, reason: detail });
91
- return { ok: false, reason: "document_control_unsigned", detail };
92
- }
93
- control.signature = signature;
94
- // Signed ONCE. The TBS commits to the document, the sender and the verb — never to a
95
- // recipient — so every holder receives byte-identical evidence and cannot be handed a
96
- // frame that differs from their co-holders'.
97
- const bytes = encodeDocumentControl(control);
98
- const holdersNotified = {};
99
- const holderFailures = {};
100
- for (const holder of targets.holders) {
101
- // Sequential and independent: one unreachable holder must neither block nor abort the
102
- // others (availability is a first-class protocol concern), and a throw from the transport
103
- // is that holder's failure alone, not the fan-out's.
104
- try {
105
- const sent = await deps.send(agentName, {
106
- peerAgentId: holder,
107
- documentId,
108
- bytes,
109
- correlationId: randomUUID(),
110
- });
111
- holdersNotified[holder] = sent.ok;
112
- if (!sent.ok) {
113
- holderFailures[holder] = sent.reason;
114
- deps.logger?.warn("document.control.holder_not_notified", {
115
- documentId, verb, holderAgentId: holder, reason: sent.reason,
116
- });
117
- }
118
- }
119
- catch (err) {
120
- holdersNotified[holder] = false;
121
- holderFailures[holder] = err instanceof Error ? err.message : String(err);
122
- deps.logger?.warn("document.control.holder_not_notified", {
123
- documentId, verb, holderAgentId: holder,
124
- reason: err instanceof Error ? err.message : String(err),
125
- });
126
- }
127
- }
128
- // `ok` means SIGNED AND ATTEMPTED TO EVERY CURRENT HOLDER. Who actually took it is the map's
129
- // job — collapsing that into one boolean is how a partial fan-out reads as a clean one.
130
- return { ok: true, holdersNotified, holderFailures };
131
- }
132
- // No agent on this daemon holds it. Named as such rather than reported as a transport failure:
133
- // the two want different things from whoever reads the log.
134
- return { ok: false, reason: "document_unknown" };
135
- };
136
- }
137
- //# sourceMappingURL=document-control-notifier.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"document-control-notifier.js","sourceRoot":"","sources":["../src/document-control-notifier.ts"],"names":[],"mappings":"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"}