@cello-protocol/daemon 0.0.52 → 0.0.54
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/dist/agent-handlers.d.ts +57 -0
- package/dist/agent-handlers.d.ts.map +1 -0
- package/dist/agent-handlers.js +360 -0
- package/dist/agent-handlers.js.map +1 -0
- package/dist/agent-id-migration.d.ts.map +1 -1
- package/dist/agent-id-migration.js +1 -0
- package/dist/agent-id-migration.js.map +1 -1
- package/dist/agent-loader.d.ts +3 -4
- package/dist/agent-loader.d.ts.map +1 -1
- package/dist/agent-loader.js +3 -4
- package/dist/agent-loader.js.map +1 -1
- package/dist/cbor-blob-migration.d.ts +8 -0
- package/dist/cbor-blob-migration.d.ts.map +1 -0
- package/dist/cbor-blob-migration.js +115 -0
- package/dist/cbor-blob-migration.js.map +1 -0
- package/dist/close-session-handler.d.ts +41 -0
- package/dist/close-session-handler.d.ts.map +1 -0
- package/dist/close-session-handler.js +399 -0
- package/dist/close-session-handler.js.map +1 -0
- package/dist/consortium-bootstrap.d.ts +83 -0
- package/dist/consortium-bootstrap.d.ts.map +1 -0
- package/dist/consortium-bootstrap.js +120 -0
- package/dist/consortium-bootstrap.js.map +1 -0
- package/dist/contact-handlers.d.ts +56 -0
- package/dist/contact-handlers.d.ts.map +1 -0
- package/dist/contact-handlers.js +303 -0
- package/dist/contact-handlers.js.map +1 -0
- package/dist/content-park-client.d.ts.map +1 -1
- package/dist/content-park-client.js +7 -7
- package/dist/content-park-client.js.map +1 -1
- package/dist/content-park.d.ts +15 -0
- package/dist/content-park.d.ts.map +1 -0
- package/dist/content-park.js +227 -0
- package/dist/content-park.js.map +1 -0
- package/dist/daemon.d.ts +0 -11
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +407 -4552
- package/dist/daemon.js.map +1 -1
- package/dist/db-identity-store.d.ts +31 -35
- package/dist/db-identity-store.d.ts.map +1 -1
- package/dist/db-identity-store.js +62 -66
- package/dist/db-identity-store.js.map +1 -1
- package/dist/directory-bootstrap.d.ts +27 -31
- package/dist/directory-bootstrap.d.ts.map +1 -1
- package/dist/directory-bootstrap.js +18 -21
- package/dist/directory-bootstrap.js.map +1 -1
- package/dist/frame-values.d.ts +3 -0
- package/dist/frame-values.d.ts.map +1 -0
- package/dist/frame-values.js +73 -0
- package/dist/frame-values.js.map +1 -0
- package/dist/identity-migration.d.ts +8 -8
- package/dist/identity-migration.d.ts.map +1 -1
- package/dist/identity-migration.js +30 -31
- package/dist/identity-migration.js.map +1 -1
- package/dist/inbound-seal-request.d.ts +17 -0
- package/dist/inbound-seal-request.d.ts.map +1 -0
- package/dist/inbound-seal-request.js +155 -0
- package/dist/inbound-seal-request.js.map +1 -0
- package/dist/inbound-sessions.d.ts +54 -0
- package/dist/inbound-sessions.d.ts.map +1 -0
- package/dist/inbound-sessions.js +603 -0
- package/dist/inbound-sessions.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -10
- package/dist/index.js.map +1 -1
- package/dist/initiate-session-handler.d.ts +22 -0
- package/dist/initiate-session-handler.d.ts.map +1 -0
- package/dist/initiate-session-handler.js +150 -0
- package/dist/initiate-session-handler.js.map +1 -0
- package/dist/network-directory-node.d.ts +6 -6
- package/dist/network-directory-node.d.ts.map +1 -1
- package/dist/network-directory-node.js +29 -28
- package/dist/network-directory-node.js.map +1 -1
- package/dist/notification-handlers.d.ts +32 -0
- package/dist/notification-handlers.d.ts.map +1 -0
- package/dist/notification-handlers.js +73 -0
- package/dist/notification-handlers.js.map +1 -0
- package/dist/outbound-sessions.d.ts +54 -0
- package/dist/outbound-sessions.d.ts.map +1 -0
- package/dist/outbound-sessions.js +429 -0
- package/dist/outbound-sessions.js.map +1 -0
- package/dist/park-envelope.d.ts.map +1 -1
- package/dist/park-envelope.js +3 -2
- package/dist/park-envelope.js.map +1 -1
- package/dist/register-handler.d.ts +35 -0
- package/dist/register-handler.d.ts.map +1 -0
- package/dist/register-handler.js +179 -0
- package/dist/register-handler.js.map +1 -0
- package/dist/registration-manager.d.ts.map +1 -1
- package/dist/registration-manager.js +3 -6
- package/dist/registration-manager.js.map +1 -1
- package/dist/relay-receipt-store.d.ts.map +1 -1
- package/dist/relay-receipt-store.js +1 -2
- package/dist/relay-receipt-store.js.map +1 -1
- package/dist/retry-queue.d.ts +18 -60
- package/dist/retry-queue.d.ts.map +1 -1
- package/dist/retry-queue.js +46 -97
- package/dist/retry-queue.js.map +1 -1
- package/dist/seal-coordinator.d.ts +47 -0
- package/dist/seal-coordinator.d.ts.map +1 -0
- package/dist/seal-coordinator.js +551 -0
- package/dist/seal-coordinator.js.map +1 -0
- package/dist/seal-flows.d.ts +50 -0
- package/dist/seal-flows.d.ts.map +1 -0
- package/dist/seal-flows.js +454 -0
- package/dist/seal-flows.js.map +1 -0
- package/dist/seal-frontier-verify.d.ts +9 -9
- package/dist/seal-frontier-verify.d.ts.map +1 -1
- package/dist/seal-frontier-verify.js +11 -12
- package/dist/seal-frontier-verify.js.map +1 -1
- package/dist/seal-leaf.d.ts +58 -0
- package/dist/seal-leaf.d.ts.map +1 -0
- package/dist/seal-leaf.js +111 -0
- package/dist/seal-leaf.js.map +1 -0
- package/dist/seal-receipt-upgrade.d.ts +1 -1
- package/dist/seal-receipt-upgrade.js +1 -1
- package/dist/seal-upgrade.d.ts +10 -10
- package/dist/seal-upgrade.d.ts.map +1 -1
- package/dist/seal-upgrade.js +23 -24
- package/dist/seal-upgrade.js.map +1 -1
- package/dist/session-assignment-parser.d.ts +16 -19
- package/dist/session-assignment-parser.d.ts.map +1 -1
- package/dist/session-assignment-parser.js +30 -29
- package/dist/session-assignment-parser.js.map +1 -1
- package/dist/session-ceremony.d.ts +3 -6
- package/dist/session-ceremony.d.ts.map +1 -1
- package/dist/session-ceremony.js +7 -9
- package/dist/session-ceremony.js.map +1 -1
- package/dist/session-content-handlers.d.ts +25 -0
- package/dist/session-content-handlers.d.ts.map +1 -0
- package/dist/session-content-handlers.js +436 -0
- package/dist/session-content-handlers.js.map +1 -0
- package/dist/session-name.d.ts +35 -0
- package/dist/session-name.d.ts.map +1 -0
- package/dist/session-name.js +60 -0
- package/dist/session-name.js.map +1 -0
- package/dist/session-node-manager.d.ts +26 -62
- package/dist/session-node-manager.d.ts.map +1 -1
- package/dist/session-node-manager.js +115 -92
- package/dist/session-node-manager.js.map +1 -1
- package/dist/session-read-handlers.d.ts +38 -0
- package/dist/session-read-handlers.d.ts.map +1 -0
- package/dist/session-read-handlers.js +233 -0
- package/dist/session-read-handlers.js.map +1 -0
- package/dist/session-relay-client.d.ts +8 -8
- package/dist/session-relay-client.d.ts.map +1 -1
- package/dist/session-relay-client.js +44 -46
- package/dist/session-relay-client.js.map +1 -1
- package/dist/session-tree.d.ts +11 -23
- package/dist/session-tree.d.ts.map +1 -1
- package/dist/session-tree.js +14 -26
- package/dist/session-tree.js.map +1 -1
- package/dist/signaling-connect.d.ts +2 -5
- package/dist/signaling-connect.d.ts.map +1 -1
- package/dist/signaling-connect.js +7 -12
- package/dist/signaling-connect.js.map +1 -1
- package/dist/sqlcipher-db.d.ts +18 -19
- package/dist/sqlcipher-db.d.ts.map +1 -1
- package/dist/sqlcipher-db.js +30 -30
- package/dist/sqlcipher-db.js.map +1 -1
- package/dist/telegram-doorbell.d.ts +38 -0
- package/dist/telegram-doorbell.d.ts.map +1 -0
- package/dist/telegram-doorbell.js +139 -0
- package/dist/telegram-doorbell.js.map +1 -0
- package/dist/types.d.ts +86 -79
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -1
- package/dist/types.js.map +1 -1
- package/dist/vocabulary.d.ts +30 -30
- package/dist/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary.js +41 -49
- package/dist/vocabulary.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The seal cluster: bilateral seal, unilateral escalation, and the returning-absent-party upgrade.
|
|
3
|
+
*
|
|
4
|
+
* Five pieces of mutable state and the four signaling listeners that drive them. They were already
|
|
5
|
+
* seal-private inside startDaemon — nothing else touched them — but "private" was a convention you
|
|
6
|
+
* had to verify by reading 6,000 lines. Here it is the module boundary.
|
|
7
|
+
*
|
|
8
|
+
* What the cluster actually needs from the daemon is five things: the session store, a logger, the
|
|
9
|
+
* per-agent FROST persistence, the per-agent key provider, and a way to recover parked content
|
|
10
|
+
* before ratifying a seal. Notably NOT the signaling hub — a SignalingManager is passed in per
|
|
11
|
+
* listener registration, so the coordinator never reaches for the daemon's nervous system.
|
|
12
|
+
*
|
|
13
|
+
* The state objects (the waiter maps, the in-progress sets) are returned as-is rather than hidden
|
|
14
|
+
* behind accessors: cello_close_session drives them directly from ~30 sites and stays in daemon.ts
|
|
15
|
+
* (it inlines the cursor gate, governance, transcript append and retry enqueue — see the refactor
|
|
16
|
+
* plan's DO-NOT-CUT list). Preserving behavior beats tightening encapsulation in the same move;
|
|
17
|
+
* the encapsulation is a later, separate change with its own tests.
|
|
18
|
+
*/
|
|
19
|
+
import { Buffer } from "node:buffer";
|
|
20
|
+
import { attemptSealUpgrade as attemptSealUpgradeImpl, verifyUpgradeConfirmedCert } from "./seal-upgrade.js";
|
|
21
|
+
import { upgradeAbsentToRecovered, hasAbsentParticipant } from "./seal-receipt-upgrade.js";
|
|
22
|
+
import { verifyUnilateralCertificate, verifyBilateralSealCertificate } from "./session-ceremony.js";
|
|
23
|
+
import { reDeriveFrontiers, findInflatedFrontier, checkUnilateralFrontier } from "./seal-frontier-verify.js";
|
|
24
|
+
import { frameValueToHex, normalizeLegibility } from "./frame-values.js";
|
|
25
|
+
export function createSealCoordinator(deps) {
|
|
26
|
+
const { logger, sessionNodeManager, getPersistence, getKeyProvider, recoverContent } = deps;
|
|
27
|
+
// DOD-LOOP-1: daemon-level seal bookkeeping is keyed by (agentName, sessionId), NOT sessionId
|
|
28
|
+
// alone — two of the operator's agents can hold both ends of the same session_id on one daemon
|
|
29
|
+
// (loopback), and each end seals independently. Keying by session_id alone would let A's close
|
|
30
|
+
// block B's (false seal_interrupted_in_progress) and make their seal waiters collide.
|
|
31
|
+
const sealKey = (agentName, sessionId) => `${agentName}\x1f${sessionId}`;
|
|
32
|
+
// M7-SESSION-001: tracks seal-interrupted flows currently in progress.
|
|
33
|
+
// Prevents duplicate concurrent seal-interrupted attempts for the same (agent, session) (AC-011).
|
|
34
|
+
const sealInterruptedInProgress = new Set();
|
|
35
|
+
// M7 DOD-SPINE-7: relay-mediated bilateral seal. cello_close_session registers a waiter
|
|
36
|
+
// per session_id (hex); the directory's session_sealed frame — delivered over the agent's
|
|
37
|
+
// signaling stream after FROST notarization, once BOTH parties have submitted their SEAL
|
|
38
|
+
// ctrl leaf — resolves it with the sealed_root.
|
|
39
|
+
// M7-SESSION-004: the bilateral seal resolves with the sealed_root AND the legibility
|
|
40
|
+
// certificate (receipt-not-assent, per-party frontiers, attestation modes, final_message).
|
|
41
|
+
const pendingSealWaiters = new Map();
|
|
42
|
+
// M7 DOD-SPINE-7: register the session_sealed completion handler on a signaling manager — per-agent
|
|
43
|
+
// in production (the directory routes session_sealed to the session-owning agent's authenticated
|
|
44
|
+
// stream), or the shared manager on the test path. Function declaration so getAgentSignaling
|
|
45
|
+
// (defined earlier, called at runtime) can wire it per-agent.
|
|
46
|
+
function registerSessionSealedListener(signaling, agentName, agentPubkeyHex) {
|
|
47
|
+
return signaling.registerInboundHandler((frame) => {
|
|
48
|
+
if (frame["type"] !== "session_sealed")
|
|
49
|
+
return;
|
|
50
|
+
const sidHex = frameValueToHex(frame["session_id"]);
|
|
51
|
+
const rootHex = frameValueToHex(frame["sealed_root"]);
|
|
52
|
+
if (!sidHex || !rootHex)
|
|
53
|
+
return;
|
|
54
|
+
void (async () => {
|
|
55
|
+
const toU8 = (v) => v instanceof Uint8Array ? v : Buffer.isBuffer(v) ? new Uint8Array(v) : null;
|
|
56
|
+
// M7 legibility-TBS-binding: when THIS party is the seal's signer (the initiator, whose
|
|
57
|
+
// group key produced the FROST signature), verify the signature over the legibility-bound
|
|
58
|
+
// TBS. A tampered legibility (answered / content_frontier_seq / attestation_mode, carried
|
|
59
|
+
// unsigned on the frame) changes the hash → the signature fails → the seal is REJECTED. The
|
|
60
|
+
// non-initiator does not hold the signer's key, so it accepts (verified:false): the frame
|
|
61
|
+
// arrived over the authenticated Noise channel, and the binding lets any out-of-band holder
|
|
62
|
+
// of the initiator's primary verify an exported cert.
|
|
63
|
+
if (frame["signature_type"] === "frost") {
|
|
64
|
+
const sessionIdBytes = toU8(frame["session_id"]);
|
|
65
|
+
const sealedRootBytes = toU8(frame["sealed_root"]);
|
|
66
|
+
const frostSig = toU8(frame["frost_signature"]);
|
|
67
|
+
const signerPubkey = toU8(frame["signer_pubkey"]);
|
|
68
|
+
const leafCount = typeof frame["leaf_count"] === "number" ? frame["leaf_count"] : null;
|
|
69
|
+
const ctRaw = frame["close_timestamp"];
|
|
70
|
+
const closeTs = typeof ctRaw === "number" ? ctRaw : typeof ctRaw === "bigint" ? Number(ctRaw) : null;
|
|
71
|
+
if (!sessionIdBytes || !sealedRootBytes || !frostSig || !signerPubkey || leafCount === null || closeTs === null) {
|
|
72
|
+
logger.error("session.sealed.signature.invalid", { sessionId: sidHex, reason: "missing_certificate_fields" });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const record = sessionNodeManager.getSessionRecord(agentName, sidHex);
|
|
76
|
+
const verdict = await verifyBilateralSealCertificate({ persistence: getPersistence(agentName), agentPubkeyHex, logger, counterpartyPrimaryHex: record?.counterparty_primary_pubkey ?? null }, {
|
|
77
|
+
sessionId: sessionIdBytes,
|
|
78
|
+
sealedRoot: sealedRootBytes,
|
|
79
|
+
leafCount,
|
|
80
|
+
closeTimestamp: closeTs,
|
|
81
|
+
frostSignature: frostSig,
|
|
82
|
+
signerPubkey,
|
|
83
|
+
signatureType: "frost",
|
|
84
|
+
legibility: frame["legibility"] && typeof frame["legibility"] === "object"
|
|
85
|
+
? frame["legibility"]
|
|
86
|
+
: null,
|
|
87
|
+
});
|
|
88
|
+
if (!verdict.ok) {
|
|
89
|
+
// tamper-evidence: do NOT mark sealed, do NOT resolve the waiter as success.
|
|
90
|
+
logger.error("session.sealed.signature.invalid", { sessionId: sidHex, reason: verdict.reason });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// F2-a: on verified:false, surface WHY (signer_key_not_held / no_frost_share / …) so this
|
|
94
|
+
// event can never be mistaken for a tolerated failed check. A real failure took the early
|
|
95
|
+
// return above (session.sealed.signature.invalid) and never reaches here.
|
|
96
|
+
logger.info("session.sealed.signature.checked", {
|
|
97
|
+
sessionId: sidHex,
|
|
98
|
+
verified: verdict.verified,
|
|
99
|
+
...(verdict.verified ? {} : { reason: verdict.reason }),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
// M7-SESSION-004 (AC-005): normalise the wire legibility (Uint8Array pubkeys → hex) into a
|
|
103
|
+
// JSON-safe certificate and persist it with the sealed record so it survives a restart and
|
|
104
|
+
// is readable via cello_get_sealed_receipt — receipt-not-assent, per-party frontiers,
|
|
105
|
+
// attestation modes, and final_message.answered.
|
|
106
|
+
const legibility = normalizeLegibility(frame["legibility"]);
|
|
107
|
+
logger.info("session.sealed.received", {
|
|
108
|
+
sessionId: sidHex,
|
|
109
|
+
sealedRoot: rootHex,
|
|
110
|
+
hasLegibility: legibility !== undefined,
|
|
111
|
+
finalMessageAnswered: legibility && typeof legibility === "object" && "final_message" in legibility
|
|
112
|
+
? legibility.final_message?.answered
|
|
113
|
+
: undefined,
|
|
114
|
+
});
|
|
115
|
+
// DOD-LEG-2 (SI-002): independently re-derive each party's content_frontier_seq from the
|
|
116
|
+
// signed leaves the directory shipped, and REJECT the certificate if any published frontier
|
|
117
|
+
// is inflated beyond what the signed leaves support. The client does NOT trust the directory
|
|
118
|
+
// for the frontier VALUE — only for transporting signed bytes it re-checks itself. When no
|
|
119
|
+
// frontier_leaves are present (a pre-LEG-2 directory), the guard is skipped (backward-compat).
|
|
120
|
+
const frontierLeavesRaw = frame["frontier_leaves"];
|
|
121
|
+
if (legibility !== undefined) {
|
|
122
|
+
const rawParticipants = legibility.participants ?? [];
|
|
123
|
+
// Any party claiming to have received content (frontier > 0) MUST be backed by signed leaves.
|
|
124
|
+
const anyClaimedFrontier = rawParticipants.some((p) => typeof p.content_frontier_seq === "number" && p.content_frontier_seq > 0);
|
|
125
|
+
const haveLeaves = Array.isArray(frontierLeavesRaw) && frontierLeavesRaw.length > 0;
|
|
126
|
+
// HIGH (fail-closed): a malicious directory must not bypass the guard by OMITTING the leaves
|
|
127
|
+
// while still publishing a frontier. No leaves + a claimed frontier → reject.
|
|
128
|
+
if (anyClaimedFrontier && !haveLeaves) {
|
|
129
|
+
logger.error("seal.certificate.frontier.unverifiable", { sessionId: sidHex, reason: "frontier_leaves_missing" });
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
// LOW (robustness): a malformed/malicious legibility (null pubkey or non-numeric frontier)
|
|
133
|
+
// must be rejected, never crash the guard.
|
|
134
|
+
for (const p of rawParticipants) {
|
|
135
|
+
if (typeof p.pubkey !== "string" || typeof p.content_frontier_seq !== "number") {
|
|
136
|
+
logger.error("seal.certificate.frontier.unverifiable", { sessionId: sidHex, reason: "participant_malformed" });
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const participants = rawParticipants;
|
|
141
|
+
if (haveLeaves) {
|
|
142
|
+
const toU8 = (v) => (v instanceof Uint8Array ? v : new Uint8Array(v));
|
|
143
|
+
const leaves = frontierLeavesRaw.map((l) => {
|
|
144
|
+
const o = l;
|
|
145
|
+
return {
|
|
146
|
+
structure1_cbor: toU8(o["structure1_cbor"]),
|
|
147
|
+
sender_pubkey: toU8(o["sender_pubkey"]),
|
|
148
|
+
sender_signature: toU8(o["sender_signature"]),
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
// Session-bound re-derivation (BLOCKING fix): leaves must be from THIS session, so a
|
|
152
|
+
// malicious directory cannot replay a party's leaves from another session to inflate.
|
|
153
|
+
const rederived = reDeriveFrontiers(leaves, toU8(frame["session_id"]));
|
|
154
|
+
if (!rederived.ok) {
|
|
155
|
+
logger.error("seal.certificate.frontier.unverifiable", { sessionId: sidHex, reason: rederived.reason });
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const inflated = findInflatedFrontier(participants, rederived.frontiers);
|
|
159
|
+
if (inflated) {
|
|
160
|
+
// The directory published a frontier higher than the signed leaves support — refuse the
|
|
161
|
+
// seal (do NOT persist, do NOT resolve the close as success), exactly like a bad signature.
|
|
162
|
+
logger.error("seal.certificate.frontier.unverifiable", {
|
|
163
|
+
sessionId: sidHex,
|
|
164
|
+
party: inflated.party,
|
|
165
|
+
publishedFrontier: inflated.publishedFrontier,
|
|
166
|
+
derivedFrontier: inflated.derivedFrontier,
|
|
167
|
+
});
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
logger.info("seal.certificate.frontier.verified", {
|
|
171
|
+
sessionId: sidHex,
|
|
172
|
+
parties: participants.length,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (legibility !== undefined) {
|
|
177
|
+
try {
|
|
178
|
+
sessionNodeManager.recordSealCertificate(agentName, sidHex, rootHex, JSON.stringify(legibility));
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
logger.warn("seal.certificate.persist.failed", {
|
|
182
|
+
sessionId: sidHex,
|
|
183
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const waiter = pendingSealWaiters.get(sealKey(agentName, sidHex));
|
|
188
|
+
if (waiter) {
|
|
189
|
+
pendingSealWaiters.delete(sealKey(agentName, sidHex));
|
|
190
|
+
waiter({ rootHex, legibility });
|
|
191
|
+
}
|
|
192
|
+
// Mark the session sealed + tear the node down (idempotent — safe if already gone).
|
|
193
|
+
void sessionNodeManager.destroySessionNode(agentName, sidHex, "sealed");
|
|
194
|
+
})();
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
// SESSION-002 (DOD-SEAL): cello_close_session escalates to a UNILATERAL seal when the
|
|
198
|
+
// counterparty never co-closes. The waiter is resolved by the seal_unilateral_confirmed
|
|
199
|
+
// listener AFTER it verifies the certificate signature (channel-independent), or by
|
|
200
|
+
// seal_unilateral_too_early (grace not elapsed). Keyed by session_id hex.
|
|
201
|
+
// F20: `remainingSeconds` carries the directory's seal_unilateral_too_early countdown so the
|
|
202
|
+
// close result can tell the operator WHEN a unilateral seal becomes available.
|
|
203
|
+
// M8B FINDING-3 (cascade-2): a successful unilateral seal now carries the legibility certificate
|
|
204
|
+
// (normalized, JSON-safe) so cello_close_session returns it inline — the same RESPONSE SHAPE as the
|
|
205
|
+
// bilateral close (not cryptographic parity: this cert is directory-attested, see FINDING-5). undefined
|
|
206
|
+
// when the directory shipped none (a pre-cascade-2 directory): the seal still completes, but no
|
|
207
|
+
// retrievable receipt is produced (the pre-fix behavior).
|
|
208
|
+
const pendingUnilateralWaiters = new Map();
|
|
209
|
+
// SESSION-002: per-agent listener for the unilateral certificate. Verifies the FROST
|
|
210
|
+
// signature over the rebuilt TBS against the agent's own primary_pubkey BEFORE resolving
|
|
211
|
+
// the close as sealed (SI-003: a channel-swapped sealed_root fails the signature check).
|
|
212
|
+
function registerUnilateralConfirmedListener(signaling, agentName, agentPubkeyHex) {
|
|
213
|
+
return signaling.registerInboundHandler((frame) => {
|
|
214
|
+
const ftype = frame["type"];
|
|
215
|
+
if (ftype !== "seal_unilateral_confirmed" && ftype !== "seal_unilateral_too_early")
|
|
216
|
+
return;
|
|
217
|
+
const sidHex = frameValueToHex(frame["session_id"]);
|
|
218
|
+
if (!sidHex)
|
|
219
|
+
return;
|
|
220
|
+
const waiter = pendingUnilateralWaiters.get(sidHex);
|
|
221
|
+
if (!waiter)
|
|
222
|
+
return;
|
|
223
|
+
if (ftype === "seal_unilateral_too_early") {
|
|
224
|
+
pendingUnilateralWaiters.delete(sidHex);
|
|
225
|
+
// F20: thread the directory's remaining_seconds through so the close guidance can
|
|
226
|
+
// say when the unilateral seal becomes available.
|
|
227
|
+
const rs = frame["remaining_seconds"];
|
|
228
|
+
waiter({
|
|
229
|
+
ok: false,
|
|
230
|
+
reason: "seal_unilateral_too_early",
|
|
231
|
+
...(typeof rs === "number" ? { remainingSeconds: rs } : {}),
|
|
232
|
+
});
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
void (async () => {
|
|
236
|
+
const toU8 = (v) => v instanceof Uint8Array ? v : Buffer.isBuffer(v) ? new Uint8Array(v) : null;
|
|
237
|
+
const sessionId = toU8(frame["session_id"]);
|
|
238
|
+
const sealedRoot = toU8(frame["sealed_root"]);
|
|
239
|
+
const frostSig = toU8(frame["frost_signature"]);
|
|
240
|
+
const leafCount = typeof frame["leaf_count"] === "number" ? frame["leaf_count"] : null;
|
|
241
|
+
const tsRaw = frame["close_timestamp"];
|
|
242
|
+
const closeTs = typeof tsRaw === "number" ? tsRaw : typeof tsRaw === "bigint" ? Number(tsRaw) : null;
|
|
243
|
+
const sigType = frame["signature_type"];
|
|
244
|
+
if (!sessionId || !sealedRoot || !frostSig || leafCount === null || closeTs === null ||
|
|
245
|
+
(sigType !== "frost" && sigType !== "single")) {
|
|
246
|
+
logger.warn("session.unilateral.certificate.invalid", { sessionId: sidHex, reason: "malformed_certificate" });
|
|
247
|
+
pendingUnilateralWaiters.delete(sidHex);
|
|
248
|
+
waiter({ ok: false, reason: "malformed_certificate" });
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const result = await verifyUnilateralCertificate({ persistence: getPersistence(agentName), agentPubkeyHex, logger }, { sessionId, sealedRoot, leafCount, closeTimestamp: closeTs, frostSignature: frostSig, signatureType: sigType });
|
|
252
|
+
pendingUnilateralWaiters.delete(sidHex);
|
|
253
|
+
if (!result.ok) {
|
|
254
|
+
// SI-003: do NOT mark sealed when the certificate signature does not verify.
|
|
255
|
+
logger.warn("session.unilateral.certificate.invalid", { sessionId: sidHex, reason: result.reason, signatureType: sigType });
|
|
256
|
+
waiter({ ok: false, reason: `certificate_invalid:${result.reason}` });
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
logger.info("session.unilateral.certificate.verified", { sessionId: sidHex, signatureType: sigType, party: "present" });
|
|
260
|
+
// M8B FINDING-3 (cascade-2): normalise + PERSIST the legibility certificate the directory
|
|
261
|
+
// now ships on this frame — the SAME store the bilateral session_sealed handler writes
|
|
262
|
+
// (recordSealCertificate → read back by cello_get_sealed_receipt). Without this a unilateral
|
|
263
|
+
// close returned ok+root but sealed_receipt_not_found forever: the whole point of sealing
|
|
264
|
+
// when your counterparty vanished is walking away with a DURABLE, RETRIEVABLE receipt.
|
|
265
|
+
// Persist BEFORE destroying the node (bilateral ordering) so the sessions row still exists
|
|
266
|
+
// for the UPDATE. undefined legibility (a pre-cascade-2 directory) → nothing persisted; the
|
|
267
|
+
// seal still completes — surfaced loudly so this can never masquerade as a produced receipt.
|
|
268
|
+
const rootHex = Buffer.from(sealedRoot).toString("hex");
|
|
269
|
+
const legibility = normalizeLegibility(frame["legibility"]);
|
|
270
|
+
// M8B FINDING-5 (SI-002): before persisting, INDEPENDENTLY re-derive each CLIENT-VERIFIABLE
|
|
271
|
+
// ('live', the present party) frontier from the signed frontier_leaves and OVERRIDE an inflated
|
|
272
|
+
// directory-published value DOWN to the provable one (the directory cannot forge signed leaves,
|
|
273
|
+
// so the re-derived value is truth). We OVERRIDE, never reject — the unilateral seal has a
|
|
274
|
+
// directory-side dedup guard that makes a client rejection unrecoverable (a retry close is
|
|
275
|
+
// silently ignored → no receipt ever, worse than FINDING-3; cascade-2 reviewer Critical 2). The
|
|
276
|
+
// absent party's frontier stays directory-attested (its remainder is not re-derivable here). No
|
|
277
|
+
// frontier_leaves (pre-FINDING-5 directory) → the cert stays directory-attested (FINDING-3).
|
|
278
|
+
// A receipt is ALWAYS persisted; the close never dead-ends here.
|
|
279
|
+
if (legibility !== undefined) {
|
|
280
|
+
const rawParticipants = legibility.participants ?? [];
|
|
281
|
+
const guardParticipants = rawParticipants
|
|
282
|
+
.filter((p) => typeof p.pubkey === "string" && typeof p.attestation_mode === "string")
|
|
283
|
+
.map((p) => ({
|
|
284
|
+
pubkey: p.pubkey,
|
|
285
|
+
content_frontier_seq: typeof p.content_frontier_seq === "number" ? p.content_frontier_seq : 0,
|
|
286
|
+
attestation_mode: p.attestation_mode,
|
|
287
|
+
}));
|
|
288
|
+
const frontierLeavesRaw = frame["frontier_leaves"];
|
|
289
|
+
const toU8f = (v) => (v instanceof Uint8Array ? v : new Uint8Array(v));
|
|
290
|
+
const parsedFrontierLeaves = Array.isArray(frontierLeavesRaw) && frontierLeavesRaw.length > 0
|
|
291
|
+
? frontierLeavesRaw.map((l) => {
|
|
292
|
+
const o = l;
|
|
293
|
+
return {
|
|
294
|
+
structure1_cbor: toU8f(o["structure1_cbor"]),
|
|
295
|
+
sender_pubkey: toU8f(o["sender_pubkey"]),
|
|
296
|
+
sender_signature: toU8f(o["sender_signature"]),
|
|
297
|
+
};
|
|
298
|
+
})
|
|
299
|
+
: undefined;
|
|
300
|
+
const check = checkUnilateralFrontier(guardParticipants, parsedFrontierLeaves, sessionId);
|
|
301
|
+
// Apply any frontier corrections to the object we persist — override inflated 'live'
|
|
302
|
+
// frontier(s) DOWN to the provable value, so the stored receipt never claims more than the
|
|
303
|
+
// signed leaves support. Runs for both 'corrected' (valid leaves) and 'leaves_invalid'
|
|
304
|
+
// (forged leaves → corrected to 0). Never rejects → the close never dead-ends.
|
|
305
|
+
if (check.corrections.size > 0) {
|
|
306
|
+
for (const p of rawParticipants) {
|
|
307
|
+
if (typeof p.pubkey === "string" && check.corrections.has(p.pubkey.toLowerCase())) {
|
|
308
|
+
p.content_frontier_seq = check.corrections.get(p.pubkey.toLowerCase());
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (check.status === "corrected") {
|
|
313
|
+
logger.error("seal.certificate.frontier.overridden", {
|
|
314
|
+
sessionId: sidHex,
|
|
315
|
+
corrections: [...check.corrections.entries()].map(([party, seq]) => ({ party, correctedTo: seq })),
|
|
316
|
+
path: "unilateral",
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
else if (check.status === "verified") {
|
|
320
|
+
logger.info("seal.certificate.frontier.verified", {
|
|
321
|
+
sessionId: sidHex,
|
|
322
|
+
parties: guardParticipants.filter((p) => p.attestation_mode === "live").length,
|
|
323
|
+
path: "unilateral",
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
else if (check.status === "leaves_invalid") {
|
|
327
|
+
// Forged / cross-session leaves — a tamper signal. The 'live' frontier(s) have been
|
|
328
|
+
// corrected to 0 above (zero trustworthy evidence); persist that (never reject → never
|
|
329
|
+
// dead-end). Surfaced loudly for audit.
|
|
330
|
+
logger.error("seal.certificate.frontier.leaves_invalid", {
|
|
331
|
+
sessionId: sidHex,
|
|
332
|
+
reason: check.reason,
|
|
333
|
+
corrections: [...check.corrections.entries()].map(([party, seq]) => ({ party, correctedTo: seq })),
|
|
334
|
+
path: "unilateral",
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
// directory_attested: no frontier_leaves shipped (pre-FINDING-5 directory). Frontiers stay
|
|
339
|
+
// DIRECTORY-attested (already marked per-participant) — visible/auditable, never silently
|
|
340
|
+
// presented as client-verified.
|
|
341
|
+
logger.warn("seal.certificate.frontier.directory_attested", {
|
|
342
|
+
sessionId: sidHex,
|
|
343
|
+
reason: "no_frontier_leaves",
|
|
344
|
+
path: "unilateral",
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (legibility !== undefined) {
|
|
349
|
+
try {
|
|
350
|
+
sessionNodeManager.recordSealCertificate(agentName, sidHex, rootHex, JSON.stringify(legibility));
|
|
351
|
+
logger.info("session.unilateral.receipt.persisted", {
|
|
352
|
+
sessionId: sidHex,
|
|
353
|
+
sealedRoot: rootHex,
|
|
354
|
+
finalMessageAnswered: legibility && typeof legibility === "object" && "final_message" in legibility
|
|
355
|
+
? legibility.final_message?.answered
|
|
356
|
+
: undefined,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
logger.warn("seal.certificate.persist.failed", {
|
|
361
|
+
sessionId: sidHex,
|
|
362
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
// The seal is valid, but no receipt is retrievable — a directory that predates cascade-2.
|
|
368
|
+
logger.warn("session.unilateral.receipt.absent", { sessionId: sidHex, reason: "no_legibility_on_frame" });
|
|
369
|
+
}
|
|
370
|
+
void sessionNodeManager.destroySessionNode(agentName, sidHex, "sealed");
|
|
371
|
+
waiter({ ok: true, sealedRootHex: rootHex, legibility });
|
|
372
|
+
})();
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
// ─── DOD-UP-1: returning-absent-party seal upgrade (unilateral → bilateral) ───
|
|
376
|
+
// Per-session idempotency guard so a notification burst (reconnect re-delivery) cannot launch
|
|
377
|
+
// concurrent upgrade attempts. Keyed `${agentName}:${sessionIdHex}`; cleared after each attempt.
|
|
378
|
+
const sealUpgradeInFlight = new Set();
|
|
379
|
+
/**
|
|
380
|
+
* DOD-UP-1: B (the absent party) ratifies a unilateral seal it learns about on reconnect.
|
|
381
|
+
*
|
|
382
|
+
* THE KERNEL: B signs the ratification ONLY after it has recovered + integrity-verified the
|
|
383
|
+
* content behind the sealed root. We (0) verify the unilateral cert so R1 is provably authentic
|
|
384
|
+
* (SI-003 — a channel-swapped root fails); (1) recover any parked content from the relay; (2) gate
|
|
385
|
+
* on getSealUpgradeReadiness — refuse content_unrecoverable (session unknown) or content_tamper
|
|
386
|
+
* (cross-check mismatch, AC-003); only then (3) sign the ack over R1 with B's OWN K_local and send
|
|
387
|
+
* seal_upgrade_request. B never co-signs content it could not verify.
|
|
388
|
+
*/
|
|
389
|
+
// Thin wrapper over the extracted, unit-tested seal-upgrade.ts module (the KERNEL + AC-008 + H1/M1
|
|
390
|
+
// hardening live there so the refusal/reject bodies run under adversarial tests). This wrapper owns
|
|
391
|
+
// only the per-session in-flight guard and the real-dep injection.
|
|
392
|
+
async function attemptSealUpgrade(signaling, agentName, agentPubkeyHex, sidHex, frame) {
|
|
393
|
+
const key = `${agentName}:${sidHex}`;
|
|
394
|
+
if (sealUpgradeInFlight.has(key))
|
|
395
|
+
return;
|
|
396
|
+
sealUpgradeInFlight.add(key);
|
|
397
|
+
try {
|
|
398
|
+
const result = await attemptSealUpgradeImpl({
|
|
399
|
+
logger, agentName, agentPubkeyHex,
|
|
400
|
+
getReadiness: (a, s) => sessionNodeManager.getSealUpgradeReadiness(a, s),
|
|
401
|
+
getContentLeafCount: (a, s) => sessionNodeManager.getSessionTree(a, s).size(),
|
|
402
|
+
recoverContent: (a) => recoverContent(a),
|
|
403
|
+
getKeyProvider: (a) => getKeyProvider(a),
|
|
404
|
+
sendRaw: (f) => signaling.sendRaw(f),
|
|
405
|
+
}, sidHex, frame);
|
|
406
|
+
// M8B FINDING-6 (3b): the ABSENT party (B) persists its UNILATERAL receipt from the
|
|
407
|
+
// notification's legibility — but ONLY after the KERNEL content-recovery/verify gate passed.
|
|
408
|
+
// `result.sent` is true iff attemptSealUpgradeImpl recovered + integrity-verified the content
|
|
409
|
+
// behind R1 and sent the ratification request; a tampered/unrecoverable/incomplete case returns
|
|
410
|
+
// sent:false → NO receipt (never a receipt for content B could not verify). B may hold no local
|
|
411
|
+
// `sessions` row (recordSealCertificateEnsuringRow inserts a stub, counterparty = A's pubkey).
|
|
412
|
+
// The notification carries no frontier_leaves (FINDING-5 ships those only on the present party's
|
|
413
|
+
// confirm frame), so B's legibility is directory-attested — consistent with FINDING-5's
|
|
414
|
+
// directory_attested path; B trusts its own KERNEL-verified content, not a re-derivation.
|
|
415
|
+
if (result.sent) {
|
|
416
|
+
try {
|
|
417
|
+
// ONE-WAY RATCHET (cascade-2 FINDING-6 review): a re-delivered seal_unilateral_notification
|
|
418
|
+
// (reconnect burst) re-runs this path and would re-persist the notification's ORIGINAL
|
|
419
|
+
// legibility (counterparty 'absent'). If a prior upgrade already flipped the stored receipt
|
|
420
|
+
// to 'recovered' (no 'absent' participant), re-persisting would REGRESS it — and no later
|
|
421
|
+
// event restores 'recovered' (the directory dedups the duplicate as already_bilateral →
|
|
422
|
+
// seal_upgrade_rejected, which only logs). So skip the re-persist once the cert is upgraded.
|
|
423
|
+
const existing = sessionNodeManager.getSealCertificate(agentName, sidHex);
|
|
424
|
+
if (existing && !hasAbsentParticipant(existing.legibility)) {
|
|
425
|
+
logger.debug("session.unilateral.receipt.persist.skipped", { sessionId: sidHex, reason: "already_upgraded" });
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
const legibility = normalizeLegibility(frame["legibility"]);
|
|
429
|
+
const rootHex = frameValueToHex(frame["sealed_root"]);
|
|
430
|
+
const counterpartyHex = frameValueToHex(frame["present_pubkey"]); // A — the present party
|
|
431
|
+
if (legibility !== undefined && rootHex && counterpartyHex) {
|
|
432
|
+
sessionNodeManager.recordSealCertificateEnsuringRow(agentName, sidHex, counterpartyHex, rootHex, JSON.stringify(legibility));
|
|
433
|
+
logger.info("session.unilateral.receipt.persisted", { sessionId: sidHex, sealedRoot: rootHex, party: "absent" });
|
|
434
|
+
}
|
|
435
|
+
else if (legibility === undefined) {
|
|
436
|
+
logger.warn("session.unilateral.receipt.absent", { sessionId: sidHex, reason: "no_legibility_on_notification" });
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
logger.warn("seal.certificate.persist.failed", { sessionId: sidHex, reason: error instanceof Error ? error.message : String(error) });
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
finally {
|
|
446
|
+
// Clear the guard so a later reconnect can retry if the request never reached the directory;
|
|
447
|
+
// the directory dedups a repeat with already_bilateral.
|
|
448
|
+
sealUpgradeInFlight.delete(key);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
// Thin wrapper: verify the dual-attestation cert (module, AC-008 + H1), then APPLY — mark bilateral
|
|
452
|
+
// ONLY on ok. Never trust the directory's "bilateral" claim.
|
|
453
|
+
async function verifyAndApplyUpgradeConfirmed(agentName, agentPubkeyHex, sidHex, frame) {
|
|
454
|
+
const result = await verifyUpgradeConfirmedCert({
|
|
455
|
+
logger, agentName, agentPubkeyHex, persistence: getPersistence(agentName),
|
|
456
|
+
getCounterpartyHex: (a, s) => sessionNodeManager.getSessionRecord(a, s)?.counterparty_pubkey ?? null,
|
|
457
|
+
}, sidHex, frame);
|
|
458
|
+
if (!result.ok)
|
|
459
|
+
return; // cert.invalid already logged inside; do NOT accept as bilateral.
|
|
460
|
+
logger.info("session.seal.upgraded", { sessionId: sidHex, agentName, party: result.party });
|
|
461
|
+
// M8B FINDING-6 (3a): the seal is now BILATERAL (verified) — upgrade THIS party's own stored
|
|
462
|
+
// receipt so the counterparty recorded 'absent' becomes 'recovered'. Client-side: the directory
|
|
463
|
+
// ships no bilateral legibility at upgrade time (the seal leaves aren't persisted there), so each
|
|
464
|
+
// party rebuilds from the cert it already holds — the present party's from its unilateral close
|
|
465
|
+
// (FINDING-3), the returning party's from the notification it persisted after the KERNEL gate
|
|
466
|
+
// (3b). The seal signatures do not bind the (unsigned) legibility, so mutating it is sound; only
|
|
467
|
+
// the attestation flips (frontiers unchanged — never overstated). No-op if no cert is stored yet.
|
|
468
|
+
const stored = sessionNodeManager.getSealCertificate(agentName, sidHex);
|
|
469
|
+
if (stored) {
|
|
470
|
+
try {
|
|
471
|
+
const upgraded = upgradeAbsentToRecovered(stored.legibility);
|
|
472
|
+
sessionNodeManager.recordSealCertificate(agentName, sidHex, stored.sealed_root, JSON.stringify(upgraded));
|
|
473
|
+
logger.info("session.seal.receipt.upgraded", { sessionId: sidHex, agentName, party: result.party });
|
|
474
|
+
}
|
|
475
|
+
catch (error) {
|
|
476
|
+
logger.warn("seal.certificate.persist.failed", { sessionId: sidHex, reason: error instanceof Error ? error.message : String(error) });
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
void sessionNodeManager.destroySessionNode(agentName, sidHex, "sealed");
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* DOD-UP-1: per-agent listener for the absent-party seal upgrade. On reconnect the directory
|
|
483
|
+
* delivers a queued seal_unilateral_notification to B (the absent party) — that triggers the
|
|
484
|
+
* ratification attempt. The directory's seal_upgrade_confirmed / seal_upgrade_rejected responses
|
|
485
|
+
* are observed here too (B marks the session bilaterally sealed / logs the refusal).
|
|
486
|
+
*/
|
|
487
|
+
function registerUnilateralUpgradeListener(signaling, agentName, agentPubkeyHex) {
|
|
488
|
+
return signaling.registerInboundHandler((frame) => {
|
|
489
|
+
const ftype = frame["type"];
|
|
490
|
+
if (ftype === "seal_upgrade_confirmed") {
|
|
491
|
+
const sidHex = frameValueToHex(frame["session_id"]);
|
|
492
|
+
if (!sidHex)
|
|
493
|
+
return;
|
|
494
|
+
// AC-008: do NOT accept "bilateral" on the directory's word — verify the dual attestation.
|
|
495
|
+
void verifyAndApplyUpgradeConfirmed(agentName, agentPubkeyHex, sidHex, frame);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
if (ftype === "seal_upgrade_rejected") {
|
|
499
|
+
const sidHex = frameValueToHex(frame["session_id"]);
|
|
500
|
+
if (!sidHex)
|
|
501
|
+
return;
|
|
502
|
+
logger.warn("session.seal.upgrade.rejected", { sessionId: sidHex, agentName, reason: frame["reason"] });
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
if (ftype !== "seal_unilateral_notification")
|
|
506
|
+
return;
|
|
507
|
+
const sidHex = frameValueToHex(frame["session_id"]);
|
|
508
|
+
if (!sidHex)
|
|
509
|
+
return;
|
|
510
|
+
// Only the ABSENT party receives this frame — B reacts by attempting the ratification.
|
|
511
|
+
void attemptSealUpgrade(signaling, agentName, agentPubkeyHex, sidHex, frame);
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Wire the WHOLE seal listener set onto an authenticated signaling stream.
|
|
516
|
+
*
|
|
517
|
+
* A BUNDLE, deliberately — the three listeners are NOT exported individually, because being able
|
|
518
|
+
* to register two of the three is what caused the bug this replaced. The daemon's VISITING stream
|
|
519
|
+
* registered `session_sealed` + `seal_unilateral_confirmed` but not the upgrade listener, and that
|
|
520
|
+
* looked intentional. It was not: the directory drains its DURABLE notification queue on any
|
|
521
|
+
* stream that authenticates — visiting included — and DELETES each row once sent. So a
|
|
522
|
+
* `seal_unilateral_notification` pushed down a visiting stream hit no handler, was dropped, and
|
|
523
|
+
* its durable row was gone. The absent party never ratified, and the seal stayed unilateral
|
|
524
|
+
* forever: silent, permanent loss of a notarized receipt.
|
|
525
|
+
*
|
|
526
|
+
* Every stream that can carry a seal frame gets every seal listener. Making the partial case
|
|
527
|
+
* impossible to express is the fix; a comment asking future callers to remember all three is not.
|
|
528
|
+
*
|
|
529
|
+
* Safe to call on any stream, including transient ones: `attemptSealUpgrade` holds a per-session
|
|
530
|
+
* in-flight guard, the receipt persist is behind the one-way ratchet, and the directory dedups a
|
|
531
|
+
* repeated ratification with `already_bilateral`.
|
|
532
|
+
*/
|
|
533
|
+
function registerSealListeners(signaling, agentName, agentPubkeyHex) {
|
|
534
|
+
const unregisterSealed = registerSessionSealedListener(signaling, agentName, agentPubkeyHex);
|
|
535
|
+
const unregisterUnilateral = registerUnilateralConfirmedListener(signaling, agentName, agentPubkeyHex);
|
|
536
|
+
const unregisterUpgrade = registerUnilateralUpgradeListener(signaling, agentName, agentPubkeyHex);
|
|
537
|
+
return () => {
|
|
538
|
+
unregisterSealed();
|
|
539
|
+
unregisterUnilateral();
|
|
540
|
+
unregisterUpgrade();
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
return {
|
|
544
|
+
sealKey,
|
|
545
|
+
sealInterruptedInProgress,
|
|
546
|
+
pendingSealWaiters,
|
|
547
|
+
pendingUnilateralWaiters,
|
|
548
|
+
registerSealListeners,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
//# sourceMappingURL=seal-coordinator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seal-coordinator.js","sourceRoot":"","sources":["../src/seal-coordinator.ts"],"names":[],"mappings":"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"}
|