@cello-protocol/daemon 0.0.211 → 0.0.213

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 (215) hide show
  1. package/dist/agent-admin-handlers.d.ts.map +1 -1
  2. package/dist/agent-admin-handlers.js +3 -0
  3. package/dist/agent-admin-handlers.js.map +1 -1
  4. package/dist/agent-handlers.d.ts.map +1 -1
  5. package/dist/agent-handlers.js +44 -0
  6. package/dist/agent-handlers.js.map +1 -1
  7. package/dist/agent-id-migration.d.ts.map +1 -1
  8. package/dist/agent-id-migration.js +11 -2
  9. package/dist/agent-id-migration.js.map +1 -1
  10. package/dist/assignment-verify.d.ts.map +1 -1
  11. package/dist/assignment-verify.js +2 -2
  12. package/dist/assignment-verify.js.map +1 -1
  13. package/dist/attendance-wiring.d.ts +2 -21
  14. package/dist/attendance-wiring.d.ts.map +1 -1
  15. package/dist/attendance-wiring.js +57 -251
  16. package/dist/attendance-wiring.js.map +1 -1
  17. package/dist/away-detection.d.ts +16 -21
  18. package/dist/away-detection.d.ts.map +1 -1
  19. package/dist/away-detection.js +34 -23
  20. package/dist/away-detection.js.map +1 -1
  21. package/dist/boot-agents.d.ts.map +1 -1
  22. package/dist/close-session-handler.d.ts.map +1 -1
  23. package/dist/close-session-handler.js +79 -6
  24. package/dist/close-session-handler.js.map +1 -1
  25. package/dist/co-attendance.d.ts +1 -1
  26. package/dist/co-attendance.js +1 -1
  27. package/dist/consortium-fingerprint.d.ts +115 -0
  28. package/dist/consortium-fingerprint.d.ts.map +1 -0
  29. package/dist/consortium-fingerprint.js +175 -0
  30. package/dist/consortium-fingerprint.js.map +1 -0
  31. package/dist/content-park.d.ts.map +1 -1
  32. package/dist/content-park.js +80 -0
  33. package/dist/content-park.js.map +1 -1
  34. package/dist/daemon-handle.d.ts +1 -1
  35. package/dist/daemon-handle.d.ts.map +1 -1
  36. package/dist/daemon-status-report.d.ts +10 -2
  37. package/dist/daemon-status-report.d.ts.map +1 -1
  38. package/dist/daemon-status-report.js +6 -3
  39. package/dist/daemon-status-report.js.map +1 -1
  40. package/dist/daemon.d.ts.map +1 -1
  41. package/dist/daemon.js +57 -27
  42. package/dist/daemon.js.map +1 -1
  43. package/dist/directory-bootstrap.d.ts +3 -1
  44. package/dist/directory-bootstrap.d.ts.map +1 -1
  45. package/dist/directory-bootstrap.js +3 -1
  46. package/dist/directory-bootstrap.js.map +1 -1
  47. package/dist/directory-connect.d.ts.map +1 -1
  48. package/dist/directory-connect.js +10 -2
  49. package/dist/directory-connect.js.map +1 -1
  50. package/dist/disconnect-cleanup.d.ts +7 -0
  51. package/dist/disconnect-cleanup.d.ts.map +1 -1
  52. package/dist/disconnect-cleanup.js +16 -1
  53. package/dist/disconnect-cleanup.js.map +1 -1
  54. package/dist/document-flag.d.ts +58 -0
  55. package/dist/document-flag.d.ts.map +1 -0
  56. package/dist/document-flag.js +70 -0
  57. package/dist/document-flag.js.map +1 -0
  58. package/dist/document-gate-wiring.d.ts +61 -0
  59. package/dist/document-gate-wiring.d.ts.map +1 -0
  60. package/dist/document-gate-wiring.js +119 -0
  61. package/dist/document-gate-wiring.js.map +1 -0
  62. package/dist/document-notify.d.ts +0 -1
  63. package/dist/document-notify.d.ts.map +1 -1
  64. package/dist/document-notify.js +0 -1
  65. package/dist/document-notify.js.map +1 -1
  66. package/dist/document-store.d.ts +0 -1
  67. package/dist/document-store.d.ts.map +1 -1
  68. package/dist/document-store.js +0 -1
  69. package/dist/document-store.js.map +1 -1
  70. package/dist/document-write-path.d.ts.map +1 -1
  71. package/dist/document-write-path.js +2 -9
  72. package/dist/document-write-path.js.map +1 -1
  73. package/dist/inbound-sessions.d.ts +3 -1
  74. package/dist/inbound-sessions.d.ts.map +1 -1
  75. package/dist/inbound-sessions.js +12 -4
  76. package/dist/inbound-sessions.js.map +1 -1
  77. package/dist/index.d.ts +5 -1
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +13 -1
  80. package/dist/index.js.map +1 -1
  81. package/dist/initiate-session-handler.d.ts.map +1 -1
  82. package/dist/initiate-session-handler.js +5 -1
  83. package/dist/initiate-session-handler.js.map +1 -1
  84. package/dist/ipc-server.d.ts.map +1 -1
  85. package/dist/ipc-server.js +52 -2
  86. package/dist/ipc-server.js.map +1 -1
  87. package/dist/park-envelope.d.ts +68 -0
  88. package/dist/park-envelope.d.ts.map +1 -1
  89. package/dist/park-envelope.js +86 -1
  90. package/dist/park-envelope.js.map +1 -1
  91. package/dist/park-recovery.d.ts +46 -0
  92. package/dist/park-recovery.d.ts.map +1 -1
  93. package/dist/park-recovery.js +100 -1
  94. package/dist/park-recovery.js.map +1 -1
  95. package/dist/relay-only.d.ts.map +1 -1
  96. package/dist/relay-only.js +7 -4
  97. package/dist/relay-only.js.map +1 -1
  98. package/dist/relay-receipt-store.d.ts +75 -17
  99. package/dist/relay-receipt-store.d.ts.map +1 -1
  100. package/dist/relay-receipt-store.js +109 -27
  101. package/dist/relay-receipt-store.js.map +1 -1
  102. package/dist/seal-carried-close.d.ts +37 -0
  103. package/dist/seal-carried-close.d.ts.map +1 -0
  104. package/dist/seal-carried-close.js +179 -0
  105. package/dist/seal-carried-close.js.map +1 -0
  106. package/dist/seal-certified-root-check.d.ts +37 -0
  107. package/dist/seal-certified-root-check.d.ts.map +1 -0
  108. package/dist/seal-certified-root-check.js +127 -0
  109. package/dist/seal-certified-root-check.js.map +1 -0
  110. package/dist/seal-coordinator.d.ts +6 -1
  111. package/dist/seal-coordinator.d.ts.map +1 -1
  112. package/dist/seal-coordinator.js +2 -1
  113. package/dist/seal-coordinator.js.map +1 -1
  114. package/dist/seal-escalation.d.ts.map +1 -1
  115. package/dist/seal-escalation.js +54 -0
  116. package/dist/seal-escalation.js.map +1 -1
  117. package/dist/seal-local-terminus.d.ts +41 -0
  118. package/dist/seal-local-terminus.d.ts.map +1 -0
  119. package/dist/seal-local-terminus.js +170 -0
  120. package/dist/seal-local-terminus.js.map +1 -0
  121. package/dist/seal-relay-silence.d.ts +51 -0
  122. package/dist/seal-relay-silence.d.ts.map +1 -0
  123. package/dist/seal-relay-silence.js +76 -0
  124. package/dist/seal-relay-silence.js.map +1 -0
  125. package/dist/seal-settle.d.ts +38 -0
  126. package/dist/seal-settle.d.ts.map +1 -0
  127. package/dist/seal-settle.js +33 -0
  128. package/dist/seal-settle.js.map +1 -0
  129. package/dist/session-assignment-parser.d.ts.map +1 -1
  130. package/dist/session-assignment-parser.js +5 -0
  131. package/dist/session-assignment-parser.js.map +1 -1
  132. package/dist/session-ceremony.d.ts +21 -14
  133. package/dist/session-ceremony.d.ts.map +1 -1
  134. package/dist/session-ceremony.js +25 -15
  135. package/dist/session-ceremony.js.map +1 -1
  136. package/dist/session-content-context.d.ts +11 -2
  137. package/dist/session-content-context.d.ts.map +1 -1
  138. package/dist/session-content-ingest.d.ts +0 -3
  139. package/dist/session-content-ingest.d.ts.map +1 -1
  140. package/dist/session-content-ingest.js +18 -111
  141. package/dist/session-content-ingest.js.map +1 -1
  142. package/dist/session-content-send.d.ts +5 -0
  143. package/dist/session-content-send.d.ts.map +1 -1
  144. package/dist/session-content-send.js +58 -0
  145. package/dist/session-content-send.js.map +1 -1
  146. package/dist/session-delivery-acks.d.ts +195 -0
  147. package/dist/session-delivery-acks.d.ts.map +1 -0
  148. package/dist/session-delivery-acks.js +674 -0
  149. package/dist/session-delivery-acks.js.map +1 -0
  150. package/dist/session-leaf-records.d.ts +28 -1
  151. package/dist/session-leaf-records.d.ts.map +1 -1
  152. package/dist/session-leaf-records.js +86 -1
  153. package/dist/session-leaf-records.js.map +1 -1
  154. package/dist/session-lifecycle.js +12 -12
  155. package/dist/session-lifecycle.js.map +1 -1
  156. package/dist/session-node-manager.d.ts +49 -22
  157. package/dist/session-node-manager.d.ts.map +1 -1
  158. package/dist/session-node-manager.js +113 -42
  159. package/dist/session-node-manager.js.map +1 -1
  160. package/dist/session-node-types.d.ts +18 -12
  161. package/dist/session-node-types.d.ts.map +1 -1
  162. package/dist/session-node-types.js +1 -9
  163. package/dist/session-node-types.js.map +1 -1
  164. package/dist/session-read-handlers.d.ts.map +1 -1
  165. package/dist/session-read-handlers.js +125 -4
  166. package/dist/session-read-handlers.js.map +1 -1
  167. package/dist/session-records.d.ts +17 -0
  168. package/dist/session-records.d.ts.map +1 -1
  169. package/dist/session-records.js +32 -0
  170. package/dist/session-records.js.map +1 -1
  171. package/dist/session-relay-client.d.ts +103 -6
  172. package/dist/session-relay-client.d.ts.map +1 -1
  173. package/dist/session-relay-client.js +380 -41
  174. package/dist/session-relay-client.js.map +1 -1
  175. package/dist/session-relay.d.ts +24 -2
  176. package/dist/session-relay.d.ts.map +1 -1
  177. package/dist/session-relay.js +23 -8
  178. package/dist/session-relay.js.map +1 -1
  179. package/dist/session-schema.d.ts.map +1 -1
  180. package/dist/session-schema.js +43 -0
  181. package/dist/session-schema.js.map +1 -1
  182. package/dist/session-seal-leaf-store.d.ts +6 -0
  183. package/dist/session-seal-leaf-store.d.ts.map +1 -1
  184. package/dist/session-seal-leaf-store.js +21 -4
  185. package/dist/session-seal-leaf-store.js.map +1 -1
  186. package/dist/session-seal.d.ts +30 -32
  187. package/dist/session-seal.d.ts.map +1 -1
  188. package/dist/session-seal.js +145 -116
  189. package/dist/session-seal.js.map +1 -1
  190. package/dist/session-views.d.ts +1 -0
  191. package/dist/session-views.d.ts.map +1 -1
  192. package/dist/session-views.js +81 -1
  193. package/dist/session-views.js.map +1 -1
  194. package/dist/signaling-connect.d.ts +1 -0
  195. package/dist/signaling-connect.d.ts.map +1 -1
  196. package/dist/signaling-connect.js +0 -1
  197. package/dist/signaling-connect.js.map +1 -1
  198. package/dist/status-handler.d.ts +9 -1
  199. package/dist/status-handler.d.ts.map +1 -1
  200. package/dist/status-handler.js +5 -2
  201. package/dist/status-handler.js.map +1 -1
  202. package/dist/test-handlers.js +4 -3
  203. package/dist/test-handlers.js.map +1 -1
  204. package/dist/trust-signal-store.d.ts +4 -2
  205. package/dist/trust-signal-store.d.ts.map +1 -1
  206. package/dist/trust-signal-store.js +4 -2
  207. package/dist/trust-signal-store.js.map +1 -1
  208. package/dist/types.d.ts +64 -0
  209. package/dist/types.d.ts.map +1 -1
  210. package/dist/types.js.map +1 -1
  211. package/dist/vocabulary.d.ts +22 -4
  212. package/dist/vocabulary.d.ts.map +1 -1
  213. package/dist/vocabulary.js +66 -21
  214. package/dist/vocabulary.js.map +1 -1
  215. package/package.json +5 -5
@@ -1,8 +1,8 @@
1
1
  /**
2
- * CELLO Daemon — RelayReceiptStore (M8B DOD-RELAYSIG-1)
2
+ * CELLO Daemon — RelayReceiptStore (DOD-M15-ORDERPROOF-1, was M8B DOD-RELAYSIG-1)
3
3
  *
4
4
  * The relay is the ordering/witness authority: it assigns a canonical sequence number to each submitted
5
- * content-hash leaf and signs an ACK over it (PERSIST-012, relay-node `buildRelayAckTbs`). This store is the
5
+ * content-hash leaf and signs an ATTESTATION over it (relay-node `buildRelayAckTbs`). This store is the
6
6
  * client's IMMUTABLE record of those signed attestations — durable evidence that the relay assigned a
7
7
  * specific sequence to a specific hash at a specific time. The receipt is what lets the client later prove
8
8
  * (or dispute) the relay's ordering, and is the building block the client carries to the directory at seal
@@ -10,6 +10,18 @@
10
10
  *
11
11
  * Verification is an Ed25519 check that MUST stay byte-compatible with the relay's signer.
12
12
  *
13
+ * ⚠️ **THE KEY IT IS CHECKED AGAINST IS THE POINT — 069-ORDERPROOF.** This module used to verify the
14
+ * signature against `relay_id`, a key carried inside the very frame being checked, and its own comment
15
+ * called that "SELF-CONSISTENCY". It is not a check: anyone who can write the frame can mint a key, sign
16
+ * any ordering they like with it, and put the key in the field we verify against. The key now comes from
17
+ * `relay_id` on the **directory-signed `SessionAssignment`**, which is inside the FROST-signed TBS the
18
+ * client already verifies — something neither the relay nor either participant can rewrite.
19
+ *
20
+ * ⚠️ **AND ABSENT IS NOT FINE.** Missing, malformed and mismatched are ONE outcome (`refused`), because
21
+ * a party holding no attestation for a message is in exactly the state a corrupted one leaves them in.
22
+ * Whoever can cause the absence must not get a softer answer than whoever can cause the corruption — and
23
+ * the party who can cause the absence is the relay, the party the check exists to constrain.
24
+ *
13
25
  * IMMUTABILITY (SI-003): the unique unit of a relay attestation is a POSITION — (agent, session, sequence)
14
26
  * — not a content hash. The same plaintext ("ok") legitimately produces the same content hash at DIFFERENT
15
27
  * sequences (per-session relay counter; repeated content within a session), so the table is keyed on the
@@ -20,31 +32,92 @@
20
32
  * Crypto: Ed25519 (RFC 8032), SHA-256 (FIPS 180-4). TBS = SHA-256(hash_bytes || seq_BE4 || ts_BE8).
21
33
  */
22
34
  import { verify, buildRelayAckTbs } from "@cello-protocol/crypto";
35
+ import { decodeStructure1 } from "@cello-protocol/protocol-types";
23
36
  /**
24
- * Verify a relay ACK signature against the (relayId-derived) relay pubkey. Returns true iff the signature
25
- * is exactly 64 bytes and Ed25519-verifies over TBS = SHA-256(hash_bytes || seq_BE4 || ts_BE8) — the same
26
- * TBS the relay signs (`buildRelayAckTbs`). A forged sequence number changes the TBS, so its signature fails.
37
+ * Verify a relay ordering attestation against a relay pubkey the CALLER supplies — and the caller
38
+ * must have got it from the directory-signed assignment, never from the frame.
39
+ *
40
+ * Returns true iff every field is well-formed and the 64-byte Ed25519 signature verifies over
41
+ * TBS = SHA-256(DOMAIN ‖ session_id ‖ content_hash ‖ seq_BE4 ‖ running_root ‖ ts_BE8) — the same
42
+ * statement the relay signs. A forged sequence, a swapped session or a rewritten prefix all change
43
+ * the TBS, so each fails.
44
+ *
45
+ * A wrong-length field returns FALSE rather than throwing: these bytes come off a wire the relay
46
+ * controls, and a malformed length must refuse this attestation, not take down the frame handler.
27
47
  */
28
- export function verifyRelayAck(contentHash, sequenceNumber, timestamp, signature, relayPubkey) {
48
+ export function verifyRelayAck(sessionId, contentHash, sequenceNumber, runningRoot, timestamp, signature, relayPubkey) {
29
49
  if (signature.length !== 64)
30
50
  return false;
31
- const tbs = buildRelayAckTbs(contentHash, sequenceNumber, timestamp);
51
+ if (sessionId.length !== 16 || contentHash.length !== 32 || runningRoot.length !== 32)
52
+ return false;
53
+ const tbs = buildRelayAckTbs(sessionId, contentHash, sequenceNumber, runningRoot, timestamp);
32
54
  return verify(relayPubkey, tbs, signature);
33
55
  }
34
56
  /**
35
- * Evaluate a relay ACK: decide whether it yields a storable, signature-verified receipt. This is the
36
- * verify-gates-store decision in isolation (so it is unit-testable without the relay stream) a forged
37
- * signature returns `invalid_signature` and MUST NOT be stored (DoD: "a forged sequence is rejected").
57
+ * Read back the leaf we just submitted, so the relay's attestation has something to be checked
58
+ * against the step BEFORE `evaluateRelayAck`, and the one that decides whether the check can run
59
+ * at all.
60
+ *
61
+ * ⚠️ "COULD NOT RUN THE CHECK" IS NOT "THE CHECK PASSED" — 069-ORDERPROOF.
62
+ *
63
+ * Both failures here used to be waved through by the caller, which settled the send `ok` having
64
+ * verified nothing: the absent-versus-verified collapse this unit exists to remove, reproduced at
65
+ * the one point where nothing was left to verify with. These are our OWN just-signed bytes, so a
66
+ * hostile relay cannot steer into either — unreachable in practice, which is a different claim from
67
+ * safe to wave through, and the distinction is the one that decays under later edits.
68
+ *
69
+ * `"none"` is the honest third answer: no submit was made, so no attestation is owed and none is
70
+ * missing. It is the only one of the three that does not reject.
71
+ */
72
+ export function readSubmittedLeaf(structure1Cbor, seq) {
73
+ if (seq < 0)
74
+ return { kind: "none" };
75
+ if (!structure1Cbor)
76
+ return { kind: "unreadable", event: "relay.receipt.leaf_absent" };
77
+ // Structure 1 = [version, content_hash(32), sender_pubkey(32), session_id(16), last_seen_seq, ts],
78
+ // plus last_seen_hash(32) at index 6 on a v2 claim (020-ACKHASH). content_hash is index 1 and
79
+ // session_id index 3 in both.
80
+ const s1 = decodeStructure1(structure1Cbor);
81
+ // The reason is carried because "we cannot read what we just wrote" and "we wrote a layout we
82
+ // cannot name" are different faults with the same symptom.
83
+ if (!s1.ok)
84
+ return { kind: "unreadable", event: "relay.receipt.undecodable_leaf", reason: s1.reason };
85
+ return { kind: "ok", contentHash: s1.fields.contentHash, sessionId: s1.fields.sessionId };
86
+ }
87
+ /**
88
+ * Evaluate a relay ordering attestation: decide whether it yields a storable, signature-verified
89
+ * receipt. This is the verify-gates-store decision in isolation, so it is unit-testable without the
90
+ * relay stream.
91
+ *
92
+ * `expectedRelayPubkeyHex` is the anchor and it is REQUIRED in substance: `undefined` refuses. It
93
+ * must come from `relay_id` on an assignment whose FROST signature has been verified.
38
94
  */
39
95
  export function evaluateRelayAck(params) {
40
- const { contentHash, sessionIdHex, agentPubkeyHex, relayId, relaySignature, timestamp, sequenceNumber } = params;
41
- if (!relayId || !relaySignature || timestamp === undefined)
42
- return { kind: "unsigned" };
43
- if (!/^[0-9a-fA-F]{64}$/.test(relayId))
44
- return { kind: "bad_relay_id" };
45
- const relayPubkey = new Uint8Array(Buffer.from(relayId, "hex"));
46
- if (!verifyRelayAck(contentHash, sequenceNumber, timestamp, relaySignature, relayPubkey)) {
47
- return { kind: "invalid_signature" };
96
+ const { sessionId, contentHash, runningRoot, sessionIdHex, agentPubkeyHex, expectedRelayPubkeyHex, relayId, relaySignature, timestamp, sequenceNumber, } = params;
97
+ if (!expectedRelayPubkeyHex || !/^[0-9a-fA-F]{64}$/.test(expectedRelayPubkeyHex)) {
98
+ return { kind: "refused", cause: "no_anchor" };
99
+ }
100
+ /**
101
+ * THE FRAME'S OWN CLAIM IS COMPARED, NEVER TRUSTED. The signature below is verified against the
102
+ * directory's key regardless, so this comparison adds no security on its own — what it adds is a
103
+ * NAMED cause. A relay that has been replaced mid-session produces a valid signature under a key
104
+ * the directory never named, and "this is not the relay you were assigned" is the sentence an
105
+ * operator can act on. `undefined` is not a mismatch: a relay omitting the field does not get to
106
+ * skip the check, it simply gets checked against the anchor with no label.
107
+ */
108
+ if (relayId !== undefined) {
109
+ if (!/^[0-9a-fA-F]{64}$/.test(relayId))
110
+ return { kind: "refused", cause: "bad_relay_id" };
111
+ if (relayId.toLowerCase() !== expectedRelayPubkeyHex.toLowerCase()) {
112
+ return { kind: "refused", cause: "relay_not_assigned" };
113
+ }
114
+ }
115
+ if (!relaySignature || timestamp === undefined || !runningRoot) {
116
+ return { kind: "refused", cause: "attestation_absent" };
117
+ }
118
+ const relayPubkey = new Uint8Array(Buffer.from(expectedRelayPubkeyHex, "hex"));
119
+ if (!verifyRelayAck(sessionId, contentHash, sequenceNumber, runningRoot, timestamp, relaySignature, relayPubkey)) {
120
+ return { kind: "refused", cause: "signature_invalid" };
48
121
  }
49
122
  return {
50
123
  kind: "store",
@@ -52,11 +125,12 @@ export function evaluateRelayAck(params) {
52
125
  hashHex: Buffer.from(contentHash).toString("hex"),
53
126
  agentPubkeyHex,
54
127
  sessionIdHex,
55
- relayId,
56
- relayPubkeyHex: relayId,
128
+ relayId: expectedRelayPubkeyHex,
129
+ relayPubkeyHex: expectedRelayPubkeyHex,
57
130
  sequenceNumber,
58
131
  timestamp,
59
132
  signatureHex: Buffer.from(relaySignature).toString("hex"),
133
+ runningRootHex: Buffer.from(runningRoot).toString("hex"),
60
134
  },
61
135
  };
62
136
  }
@@ -71,6 +145,9 @@ const CREATE_RELAY_RECEIPTS_SQL = `
71
145
  relay_timestamp INTEGER NOT NULL,
72
146
  signature_hex TEXT NOT NULL,
73
147
  stored_at INTEGER NOT NULL,
148
+ -- 069-ORDERPROOF: the running root the relay signed alongside the position (nullable — rows
149
+ -- written before this order have none, and they stay readable and sealable exactly as they are).
150
+ running_root_hex TEXT,
74
151
  -- FED-OPTIONB-SEAL-001: per-leaf carry bytes for the unilateral-seal offline rebuild (nullable).
75
152
  structure2_cbor BLOB,
76
153
  structure1_cbor BLOB,
@@ -100,6 +177,10 @@ export class RelayReceiptStore {
100
177
  ["structure2_cbor", "BLOB"],
101
178
  ["structure1_cbor", "BLOB"],
102
179
  ["leaf_kind", "INTEGER"],
180
+ // 069-ORDERPROOF. NULLABLE, like the three above: a session that predates this order keeps
181
+ // every receipt it already holds, opens, reads and seals unchanged, and simply has no root
182
+ // recorded for those positions.
183
+ ["running_root_hex", "TEXT"],
103
184
  ]) {
104
185
  if (!cols.has(name)) {
105
186
  this.#db.exec(`ALTER TABLE relay_ack_receipts ADD COLUMN ${name} ${decl}`);
@@ -116,11 +197,11 @@ export class RelayReceiptStore {
116
197
  const existing = this.get(receipt.agentPubkeyHex, receipt.sessionIdHex, receipt.sequenceNumber);
117
198
  const info = this.#db
118
199
  .prepare(`INSERT OR IGNORE INTO relay_ack_receipts
119
- (agent_pubkey, session_id, sequence_number, hash_hex, relay_id, relay_pubkey_hex, relay_timestamp, signature_hex, stored_at, structure2_cbor, structure1_cbor, leaf_kind)
120
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
200
+ (agent_pubkey, session_id, sequence_number, hash_hex, relay_id, relay_pubkey_hex, relay_timestamp, signature_hex, stored_at, structure2_cbor, structure1_cbor, leaf_kind, running_root_hex)
201
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
121
202
  .run(receipt.agentPubkeyHex, receipt.sessionIdHex, receipt.sequenceNumber, receipt.hashHex, receipt.relayId, receipt.relayPubkeyHex, receipt.timestamp, receipt.signatureHex, storedAtMs,
122
203
  // FED-OPTIONB-SEAL-001: carry bytes (null when absent — receipt-only rows).
123
- receipt.structure2Cbor ? Buffer.from(receipt.structure2Cbor) : null, receipt.structure1Cbor ? Buffer.from(receipt.structure1Cbor) : null, receipt.leafKind ?? null);
204
+ receipt.structure2Cbor ? Buffer.from(receipt.structure2Cbor) : null, receipt.structure1Cbor ? Buffer.from(receipt.structure1Cbor) : null, receipt.leafKind ?? null, receipt.runningRootHex ?? null);
124
205
  const wrote = Number(info.changes) > 0;
125
206
  if (!wrote && existing && existing.hashHex !== receipt.hashHex) {
126
207
  // EQUIVOCATION: a relay re-attesting a DIFFERENT hash at an already-recorded position. The first
@@ -138,7 +219,7 @@ export class RelayReceiptStore {
138
219
  /** The stored receipt at an attestation position (agent, session, sequence), or undefined. */
139
220
  get(agentPubkeyHex, sessionIdHex, sequenceNumber) {
140
221
  const row = this.#db
141
- .prepare(`SELECT agent_pubkey, session_id, sequence_number, hash_hex, relay_id, relay_pubkey_hex, relay_timestamp, signature_hex
222
+ .prepare(`SELECT agent_pubkey, session_id, sequence_number, hash_hex, relay_id, relay_pubkey_hex, relay_timestamp, signature_hex, running_root_hex
142
223
  FROM relay_ack_receipts WHERE agent_pubkey = ? AND session_id = ? AND sequence_number = ?`)
143
224
  .get(agentPubkeyHex, sessionIdHex, sequenceNumber);
144
225
  return row ? this.#rowToReceipt(row) : undefined;
@@ -147,11 +228,11 @@ export class RelayReceiptStore {
147
228
  getAll(agentPubkeyHex, sessionIdHex) {
148
229
  const rows = sessionIdHex
149
230
  ? this.#db
150
- .prepare(`SELECT agent_pubkey, session_id, sequence_number, hash_hex, relay_id, relay_pubkey_hex, relay_timestamp, signature_hex
231
+ .prepare(`SELECT agent_pubkey, session_id, sequence_number, hash_hex, relay_id, relay_pubkey_hex, relay_timestamp, signature_hex, running_root_hex
151
232
  FROM relay_ack_receipts WHERE agent_pubkey = ? AND session_id = ? ORDER BY sequence_number ASC`)
152
233
  .all(agentPubkeyHex, sessionIdHex)
153
234
  : this.#db
154
- .prepare(`SELECT agent_pubkey, session_id, sequence_number, hash_hex, relay_id, relay_pubkey_hex, relay_timestamp, signature_hex
235
+ .prepare(`SELECT agent_pubkey, session_id, sequence_number, hash_hex, relay_id, relay_pubkey_hex, relay_timestamp, signature_hex, running_root_hex
155
236
  FROM relay_ack_receipts WHERE agent_pubkey = ? ORDER BY session_id, sequence_number ASC`)
156
237
  .all(agentPubkeyHex);
157
238
  return rows.map((r) => this.#rowToReceipt(r));
@@ -164,7 +245,7 @@ export class RelayReceiptStore {
164
245
  */
165
246
  getSealLeaves(agentPubkeyHex, sessionIdHex) {
166
247
  const rows = this.#db
167
- .prepare(`SELECT agent_pubkey, session_id, sequence_number, hash_hex, relay_id, relay_pubkey_hex, relay_timestamp, signature_hex, structure2_cbor, structure1_cbor, leaf_kind
248
+ .prepare(`SELECT agent_pubkey, session_id, sequence_number, hash_hex, relay_id, relay_pubkey_hex, relay_timestamp, signature_hex, running_root_hex, structure2_cbor, structure1_cbor, leaf_kind
168
249
  FROM relay_ack_receipts
169
250
  WHERE agent_pubkey = ? AND session_id = ? AND structure2_cbor IS NOT NULL AND structure1_cbor IS NOT NULL AND leaf_kind IS NOT NULL
170
251
  ORDER BY sequence_number ASC`)
@@ -186,6 +267,7 @@ export class RelayReceiptStore {
186
267
  sequenceNumber: row.sequence_number,
187
268
  timestamp: row.relay_timestamp,
188
269
  signatureHex: row.signature_hex,
270
+ ...(typeof row.running_root_hex === "string" ? { runningRootHex: row.running_root_hex } : {}),
189
271
  };
190
272
  }
191
273
  }
@@ -1 +1 @@
1
- {"version":3,"file":"relay-receipt-store.js","sourceRoot":"","sources":["../src/relay-receipt-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"relay-receipt-store.js","sourceRoot":"","sources":["../src/relay-receipt-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAsDlE;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAC5B,SAAqB,EACrB,WAAuB,EACvB,cAAsB,EACtB,WAAuB,EACvB,SAAiB,EACjB,SAAqB,EACrB,WAAuB;IAEvB,IAAI,SAAS,CAAC,MAAM,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAC1C,IAAI,SAAS,CAAC,MAAM,KAAK,EAAE,IAAI,WAAW,CAAC,MAAM,KAAK,EAAE,IAAI,WAAW,CAAC,MAAM,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IACpG,MAAM,GAAG,GAAG,gBAAgB,CAAC,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;IAC7F,OAAO,MAAM,CAAC,WAAW,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;AAC7C,CAAC;AAyBD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,iBAAiB,CAC/B,cAAsC,EACtC,GAAW;IAEX,IAAI,GAAG,GAAG,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACrC,IAAI,CAAC,cAAc;QAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,2BAA2B,EAAE,CAAC;IACvF,mGAAmG;IACnG,8FAA8F;IAC9F,8BAA8B;IAC9B,MAAM,EAAE,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAC5C,8FAA8F;IAC9F,2DAA2D;IAC3D,IAAI,CAAC,EAAE,CAAC,EAAE;QAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,gCAAgC,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC;IACtG,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;AAC5F,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAYhC;IACC,MAAM,EACJ,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EACjE,sBAAsB,EAAE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,cAAc,GAC3E,GAAG,MAAM,CAAC;IAEX,IAAI,CAAC,sBAAsB,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,sBAAsB,CAAC,EAAE,CAAC;QACjF,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IACjD,CAAC;IACD;;;;;;;OAOG;IACH,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC;QAC1F,IAAI,OAAO,CAAC,WAAW,EAAE,KAAK,sBAAsB,CAAC,WAAW,EAAE,EAAE,CAAC;YACnE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,IAAI,CAAC,cAAc,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,WAAW,EAAE,CAAC;QAC/D,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC1D,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC,CAAC;IAC/E,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,CAAC,EAAE,CAAC;QACjH,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC;IACzD,CAAC;IAED,OAAO;QACL,IAAI,EAAE,OAAO;QACb,OAAO,EAAE;YACP,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;YACjD,cAAc;YACd,YAAY;YACZ,OAAO,EAAE,sBAAsB;YAC/B,cAAc,EAAE,sBAAsB;YACtC,cAAc;YACd,SAAS;YACT,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;YACzD,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;SACzD;KACF,CAAC;AACJ,CAAC;AAED,MAAM,yBAAyB,GAAG;;;;;;;;;;;;;;;;;;;;CAoBjC,CAAC;AAEF,MAAM,OAAO,iBAAiB;IACnB,GAAG,CAAiB;IACpB,OAAO,CAAS;IAEzB,YAAY,EAAkB,EAAE,MAAc;QAC5C,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;QACd,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACzC,IAAI,CAAC,wBAAwB,EAAE,CAAC;IAClC,CAAC;IAED;;;;;;OAMG;IACH,wBAAwB;QACtB,MAAM,IAAI,GAAG,IAAI,GAAG,CACjB,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,uCAAuC,CAAC,CAAC,GAAG,EAA8B,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAChH,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI;YACzB,CAAC,iBAAiB,EAAE,MAAM,CAAC;YAC3B,CAAC,iBAAiB,EAAE,MAAM,CAAC;YAC3B,CAAC,WAAW,EAAE,SAAS,CAAC;YACxB,2FAA2F;YAC3F,2FAA2F;YAC3F,gCAAgC;YAChC,CAAC,kBAAkB,EAAE,MAAM,CAAC;SACpB,EAAE,CAAC;YACX,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,6CAA6C,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;YAC7E,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,OAAqB,EAAE,UAAkB;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,cAAc,EAAE,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;QAChG,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG;aAClB,OAAO,CACN;;wDAEgD,CACjD;aACA,GAAG,CACF,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,YAAY,EACpB,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,YAAY,EACpB,UAAU;QACV,4EAA4E;QAC5E,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,EACnE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,EACnE,OAAO,CAAC,QAAQ,IAAI,IAAI,EACxB,OAAO,CAAC,cAAc,IAAI,IAAI,CAC/B,CAAC;QACJ,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,IAAI,QAAQ,IAAI,QAAQ,CAAC,OAAO,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC;YAC/D,iGAAiG;YACjG,6FAA6F;YAC7F,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,4BAA4B,EAAE;gBAC9C,UAAU,EAAE,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;gBAC/C,YAAY,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;gBAC/C,GAAG,EAAE,OAAO,CAAC,cAAc;gBAC3B,iBAAiB,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;gBAChD,oBAAoB,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;aACnD,CAAC,CAAC;QACL,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,8FAA8F;IAC9F,GAAG,CAAC,cAAsB,EAAE,YAAoB,EAAE,cAAsB;QACtE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG;aACjB,OAAO,CACN;qGAC6F,CAC9F;aACA,GAAG,CAAC,cAAc,EAAE,YAAY,EAAE,cAAc,CAAwC,CAAC;QAC5F,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACnD,CAAC;IAED,wGAAwG;IACxG,MAAM,CAAC,cAAsB,EAAE,YAAqB;QAClD,MAAM,IAAI,GAAG,YAAY;YACvB,CAAC,CAAC,IAAI,CAAC,GAAG;iBACL,OAAO,CACN;8GACkG,CACnG;iBACA,GAAG,CAAC,cAAc,EAAE,YAAY,CAAC;YACtC,CAAC,CAAC,IAAI,CAAC,GAAG;iBACL,OAAO,CACN;uGAC2F,CAC5F;iBACA,GAAG,CAAC,cAAc,CAAC,CAAC;QAC3B,OAAQ,IAAuC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IACpF,CAAC;IAED;;;;;OAKG;IACH,aAAa,CAAC,cAAsB,EAAE,YAAoB;QACxD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG;aAClB,OAAO,CACN;;;uCAG+B,CAChC;aACA,GAAG,CAAC,cAAc,EAAE,YAAY,CAAmC,CAAC;QACvE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtB,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;YACxB,cAAc,EAAE,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC;YACvC,cAAc,EAAE,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC;YACvC,QAAQ,EAAE,CAAC,CAAC,SAAmB;SAChC,CAAC,CAAC,CAAC;IACN,CAAC;IAED,aAAa,CAAC,GAA4B;QACxC,OAAO;YACL,OAAO,EAAE,GAAG,CAAC,QAAkB;YAC/B,cAAc,EAAE,GAAG,CAAC,YAAsB;YAC1C,YAAY,EAAE,GAAG,CAAC,UAAoB;YACtC,OAAO,EAAE,GAAG,CAAC,QAAkB;YAC/B,cAAc,EAAE,GAAG,CAAC,gBAA0B;YAC9C,cAAc,EAAE,GAAG,CAAC,eAAyB;YAC7C,SAAS,EAAE,GAAG,CAAC,eAAyB;YACxC,YAAY,EAAE,GAAG,CAAC,aAAuB;YACzC,GAAG,CAAC,OAAO,GAAG,CAAC,gBAAgB,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,GAAG,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9F,CAAC;IACJ,CAAC;CACF;AAED,sFAAsF;AACtF,SAAS,IAAI,CAAC,CAAU;IACtB,IAAI,CAAC,YAAY,UAAU;QAAE,OAAO,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,UAAU,CAAC,CAAW,CAAC,CAAC;IAC3D,IAAI,CAAC,YAAY,WAAW;QAAE,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;IACvD,OAAO,IAAI,UAAU,EAAE,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { type SealCarryLeaf } from "./session-seal-leaf-store.js";
2
+ import type { SessionSealContext } from "./session-seal.js";
3
+ import type { DaemonDatabase } from "./sqlcipher-db.js";
4
+ /** What the carried close needs from the seal path, and nothing more. */
5
+ export interface CarriedCloseDeps {
6
+ readonly ctx: SessionSealContext;
7
+ readonly db: DaemonDatabase | null;
8
+ readonly getSealCarry: (agentPubkeyHex: string, sessionIdHex: string) => SealCarryLeaf[];
9
+ }
10
+ /**
11
+ * ═══ 070-CARRIEDSEAL — THE CLOSE THAT DOES NOT NEED ANYONE ELSE TO BE AWAKE ═══
12
+ *
13
+ * Returns the escalation values for a seal closed over evidence this side already holds, or
14
+ * `null` when this is not that situation and the caller should fail exactly as it did before.
15
+ *
16
+ * ⚠️ `null` IS THE DEFAULT, AND THAT IS THE SAFETY PROPERTY. Every way out of here that is not a
17
+ * successfully written terminus returns `null`, so the caller's original refusal stands with its
18
+ * original reason. A bug in this method costs the receipt this order was written to save; it
19
+ * cannot turn a relay's ruling into a seal.
20
+ *
21
+ * The first gate is the important one: `RELAY_GAVE_NO_ANSWER` separates a relay that was not there
22
+ * from one that considered the leaf and declined it. Routing around a ruling is the thing a trust
23
+ * layer must never do, and "the submit failed" alone does not tell the two apart.
24
+ */
25
+ export declare function closeOverCarriedEvidence(deps: CarriedCloseDeps, agentName: string, sessionId: string, cause: string, correlationId?: string,
26
+ /**
27
+ * The relay's standing verdict about THIS AGENT, when there is a relay client to ask. The reason
28
+ * string cannot carry it — see `relayRefusedUs` — so it is passed alongside.
29
+ */
30
+ standingRefusal?: {
31
+ reason: string;
32
+ } | null): Promise<{
33
+ ok: true;
34
+ sequenceNumber: number;
35
+ reportedRootHex: string;
36
+ } | null>;
37
+ //# sourceMappingURL=seal-carried-close.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seal-carried-close.d.ts","sourceRoot":"","sources":["../src/seal-carried-close.ts"],"names":[],"mappings":"AAUA,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,8BAA8B,CAAC;AACxF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,kBAAkB,CAAC;IACjC,QAAQ,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,YAAY,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,aAAa,EAAE,CAAC;CAC1F;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,gBAAgB,EACtB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,aAAa,CAAC,EAAE,MAAM;AACtB;;;GAGG;AACH,eAAe,CAAC,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAC1C,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAuJ/E"}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * `DOD-M15-CARRIEDSEAL-1` — THE CLOSE THAT DOES NOT NEED ANYONE ELSE TO BE AWAKE.
3
+ *
4
+ * Extracted from `session-seal.ts` rather than written beside it, for the reason that file's own
5
+ * ratchet exists: a seal path that keeps growing in one place is how the second implementation of a
6
+ * seal gets written next to the first. There is exactly one caller.
7
+ */
8
+ import { decodeStructure1 } from "@cello-protocol/protocol-types";
9
+ import { buildLocalSealTerminus } from "./seal-local-terminus.js";
10
+ import { RELAY_GAVE_NO_ANSWER, relayRefusedUs } from "./seal-relay-silence.js";
11
+ import { SessionSealLeafStore } from "./session-seal-leaf-store.js";
12
+ /**
13
+ * ═══ 070-CARRIEDSEAL — THE CLOSE THAT DOES NOT NEED ANYONE ELSE TO BE AWAKE ═══
14
+ *
15
+ * Returns the escalation values for a seal closed over evidence this side already holds, or
16
+ * `null` when this is not that situation and the caller should fail exactly as it did before.
17
+ *
18
+ * ⚠️ `null` IS THE DEFAULT, AND THAT IS THE SAFETY PROPERTY. Every way out of here that is not a
19
+ * successfully written terminus returns `null`, so the caller's original refusal stands with its
20
+ * original reason. A bug in this method costs the receipt this order was written to save; it
21
+ * cannot turn a relay's ruling into a seal.
22
+ *
23
+ * The first gate is the important one: `RELAY_GAVE_NO_ANSWER` separates a relay that was not there
24
+ * from one that considered the leaf and declined it. Routing around a ruling is the thing a trust
25
+ * layer must never do, and "the submit failed" alone does not tell the two apart.
26
+ */
27
+ export async function closeOverCarriedEvidence(deps, agentName, sessionId, cause, correlationId,
28
+ /**
29
+ * The relay's standing verdict about THIS AGENT, when there is a relay client to ask. The reason
30
+ * string cannot carry it — see `relayRefusedUs` — so it is passed alongside.
31
+ */
32
+ standingRefusal) {
33
+ if (!RELAY_GAVE_NO_ANSWER.has(cause))
34
+ return null;
35
+ if (relayRefusedUs(standingRefusal)) {
36
+ /**
37
+ * THE RELAY ANSWERED, AND THE ANSWER WAS NO. Sealing over carried evidence here would be
38
+ * routing around a ruling — the relay authenticated this agent and then refused it, and that
39
+ * refusal is a decision about us rather than an absence of anyone to ask.
40
+ */
41
+ deps.ctx.logger.warn("session.seal.local_terminus.relay_refused", {
42
+ agentName, sessionId, cause, relayReason: standingRefusal?.reason, correlationId,
43
+ impact: "the relay REFUSED this agent rather than being unreachable, so this side did not sign its own closing leaf. No receipt was produced and the session is untouched — closing around a refusal would put a record on the chain that the witness declined to witness.",
44
+ guidance: "Fix what the relay refused (cello status names it — an expired credential, a rate limit, or a capacity cap), then close again. If the relay is genuinely gone rather than refusing, the close will proceed on its own.",
45
+ });
46
+ return null;
47
+ }
48
+ const kp = deps.ctx.getKeyProvider(agentName);
49
+ if (!kp) {
50
+ // Not a silent skip: without this agent's key nothing here can be signed, and the operator's
51
+ // action ("start the agent") is different from every other reason a close fails.
52
+ deps.ctx.logger.warn("session.seal.local_terminus.key_unavailable", {
53
+ agentName, sessionId, cause, correlationId,
54
+ impact: "the relay could not be reached and this agent's key is not loaded, so the closing leaf could not be signed here either. No receipt was produced and nothing about the session changed.",
55
+ guidance: "Start the agent (cello_start_agent) and close again.",
56
+ });
57
+ return null;
58
+ }
59
+ const ownPubkey = new Uint8Array(await kp.getPublicKey());
60
+ const ownPubkeyHex = Buffer.from(ownPubkey).toString("hex");
61
+ const carry = deps.getSealCarry(ownPubkeyHex, sessionId);
62
+ /**
63
+ * The session id comes out of the LEAVES, not out of the caller's argument. The directory checks
64
+ * the terminus against the same session the carried leaves name, so reading it from anywhere
65
+ * else would let the two disagree — and this path exists precisely for the case where no live
66
+ * relay registration is around to be consulted.
67
+ */
68
+ const first = carry[0] ? decodeStructure1(carry[0].structure1Cbor) : null;
69
+ if (!first?.ok) {
70
+ /**
71
+ * TWO DIFFERENT FACTS, AND THEY SEND THE OPERATOR TO DIFFERENT PLACES. An empty carry means the
72
+ * conversation was never witnessed; an undecodable first leaf means the record IS here and is
73
+ * damaged. Reporting the second as the first sends someone looking for something that is not
74
+ * missing.
75
+ */
76
+ const empty = carry.length === 0;
77
+ deps.ctx.logger.info("session.seal.local_terminus.no_carry", {
78
+ agentName, sessionId, cause, carryLength: carry.length,
79
+ reason: empty ? "seal_carry_empty" : "seal_carry_unreadable", correlationId,
80
+ impact: empty
81
+ ? "no witnessed leaf is held for this session, so there is no record to close over and no receipt to produce."
82
+ : "this session's first recorded leaf is on disk and cannot be decoded, so the record cannot be chained onto. The conversation is NOT missing — cello_transcript still shows it — but no receipt can be produced from this side.",
83
+ });
84
+ return null;
85
+ }
86
+ /**
87
+ * ⚠️ NO GENESIS, NO TERMINUS — and this used to be `?? new Uint8Array(32)`.
88
+ *
89
+ * The genesis is the session's agreed starting point, and it is what BOTH chain links fall back to
90
+ * for a party that has not spoken yet. Substituting thirty-two zero bytes does not fail: the
91
+ * directory's walk skips `prev_own_hash` for a sender's first leaf and never checks
92
+ * `last_seen_hash` at all, so the leaf notarizes and the counterparty — verifying against the real
93
+ * starting point — is the one who finds a link that does not reconcile. A silent substitution
94
+ * whose cost lands on the other party.
95
+ *
96
+ * The send path already refuses for exactly this reason ("no seed means no send… the honest
97
+ * outcome is to refuse rather than sign a chain anchored to nothing"). So does this.
98
+ */
99
+ const genesis = deps.ctx.leafRecords.sessionGenesisPrevRoot(agentName, sessionId);
100
+ if (!genesis) {
101
+ deps.ctx.logger.warn("session.seal.local_terminus.no_genesis", {
102
+ agentName, sessionId, cause, correlationId,
103
+ impact: "this session has no recorded starting point on this machine, so a closing leaf written here could not link to anything and its place in the conversation could never be proven. Nothing was signed and the session is untouched.",
104
+ guidance: "Close WITH your counterparty instead — a bilateral close does not need this value. cello_transcript still shows the conversation either way.",
105
+ });
106
+ return null;
107
+ }
108
+ const built = await buildLocalSealTerminus({
109
+ carry,
110
+ ownPubkeyHex,
111
+ ownPubkey,
112
+ sessionIdBytes: first.fields.sessionId,
113
+ genesis,
114
+ finalRootHex: deps.ctx.getSessionTreeRootHex(agentName, sessionId),
115
+ closeTimestamp: Date.now(),
116
+ sign: async (bytes) => new Uint8Array(await kp.sign(bytes)),
117
+ });
118
+ if (!built.ok) {
119
+ /**
120
+ * ⚠️ AN ALREADY-PLACED TERMINUS IS A SUCCESS BEING REPORTED AS A FAILURE — review MEDIUM-3.
121
+ *
122
+ * `seal_carry_own_ctrl_present` means a previous close already wrote our closing leaf. Returning
123
+ * `null` sent the caller to its original refusal, so an operator who closed successfully and then
124
+ * closed again — or whose reconnect retried for them — was told the RELAY was unavailable, for a
125
+ * session whose closing leaf is on their own disk. The terminus builder's own docblock says the
126
+ * caller must reuse the one that is there; nobody did.
127
+ *
128
+ * Reused rather than rebuilt: the leaf on disk is the one whose bytes were signed, and rebuilding
129
+ * would produce a different timestamp and therefore a different record.
130
+ */
131
+ if (built.reason === "seal_carry_own_ctrl_present") {
132
+ const existing = carry.find((l) => l.leafKind === 0x02 && l.senderPubkeyHex === ownPubkeyHex);
133
+ const priorS1 = existing ? decodeStructure1(existing.structure1Cbor) : null;
134
+ if (existing && priorS1?.ok) {
135
+ const priorRootHex = deps.ctx.getSessionTree(agentName, sessionId)
136
+ .rootWithAppendedHex(Buffer.from(priorS1.fields.contentHash).toString("hex"));
137
+ deps.ctx.logger.info("session.seal.local_terminus.reused", {
138
+ agentName, sessionId, cause, sequenceNumber: existing.sequenceNumber, correlationId,
139
+ impact: "this side had already signed its closing leaf for this session, so the earlier one is carried again rather than a second being written. A second closing leaf would make the session unsealable by any directory, permanently.",
140
+ });
141
+ return { ok: true, sequenceNumber: existing.sequenceNumber, reportedRootHex: priorRootHex };
142
+ }
143
+ }
144
+ deps.ctx.logger.warn("session.seal.local_terminus.refused", {
145
+ agentName, sessionId, cause, reason: built.reason, correlationId,
146
+ impact: "the relay could not be reached and the record this side holds cannot be closed over as it stands, so no receipt was produced.",
147
+ });
148
+ return null;
149
+ }
150
+ if (!deps.ctx.sealLeafStore && deps.db) {
151
+ deps.ctx.sealLeafStore = new SessionSealLeafStore(deps.db, deps.ctx.logger);
152
+ }
153
+ const wrote = deps.ctx.sealLeafStore?.store(ownPubkeyHex, sessionId, built.leaf, Date.now()) ?? false;
154
+ if (!wrote) {
155
+ /**
156
+ * The store is INSERT OR IGNORE on the position, so a false means something already occupies
157
+ * it. Refusing is the only safe answer: carrying a leaf that is not the one on disk would
158
+ * present a record this daemon does not actually hold.
159
+ */
160
+ deps.ctx.logger.warn("session.seal.local_terminus.position_taken", {
161
+ agentName, sessionId, sequenceNumber: built.leaf.sequenceNumber, correlationId,
162
+ impact: "a leaf is already recorded at the position the closing leaf would take, so it was not written and no receipt was produced.",
163
+ });
164
+ return null;
165
+ }
166
+ // The reported root is the certified-root domain — content hashes, this leaf appended. The same
167
+ // derivation the relay path uses one method down, so both close paths report the same value for
168
+ // the same leaf set.
169
+ const reportedRootHex = deps.ctx.getSessionTree(agentName, sessionId).rootWithAppendedHex(built.contentHashHex);
170
+ deps.ctx.logger.info("session.seal.local_terminus.placed", {
171
+ agentName, sessionId, cause,
172
+ sequenceNumber: built.leaf.sequenceNumber,
173
+ carryLength: carry.length,
174
+ correlationId,
175
+ impact: "the relay did not answer, so this side signed its own closing leaf over the record it already holds. The seal proceeds on carried evidence and needs no relay.",
176
+ });
177
+ return { ok: true, sequenceNumber: built.leaf.sequenceNumber, reportedRootHex };
178
+ }
179
+ //# sourceMappingURL=seal-carried-close.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seal-carried-close.js","sourceRoot":"","sources":["../src/seal-carried-close.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { SealCarryLeaf } from "./session-seal-leaf-store.js";
2
+ /**
3
+ * REBUILD THE CERTIFIED ROOT FROM THIS DAEMON'S OWN LEAVES — `DOD-M15-SEALWIRE-1` bullet 2.
4
+ *
5
+ * The receipt used to prove only that the directory signed SOMETHING: the client took the sealed
6
+ * root off the wire, confirmed the directory's signature over those bytes, stored it, and threw
7
+ * away the root it had computed a step earlier. At co-signing time that means **your key signs a
8
+ * root you never checked.**
9
+ *
10
+ * Bullet 1 moved the certified root into the content-hash domain, which is the domain this daemon
11
+ * can actually rebuild — each carry leaf's `content_hash` is the leaf hash (RFC 6962 §2.1 "hash"
12
+ * leaves are used as-is), and the carry is ordered by the relay's canonical `sequence_number`,
13
+ * which is the order the directory rebuilds in.
14
+ *
15
+ * ─── Why this returns "cannot judge" instead of always answering ───────────────────────────
16
+ *
17
+ * A root comparison that is WRONG makes every session unsealable, and force-abandon — no receipt —
18
+ * becomes the only exit. That failure is worse than the one being guarded, and this file already
19
+ * carries two comments saying so about other gates.
20
+ *
21
+ * The carry is this daemon's view, and it is not guaranteed complete at the instant a certificate
22
+ * arrives: the counterparty's SEAL ctrl leaf is what TRIGGERS the seal, so it may not have been
23
+ * witnessed here yet. So completeness is checked FIRST, against the certificate's own leaf count.
24
+ * A short carry means this daemon cannot judge — which is a different answer from "the roots
25
+ * disagree", and conflating them would turn a local timing gap into an accusation.
26
+ */
27
+ export declare function verifyCertifiedRoot(getSealCarry: (agentPubkeyHex: string, sessionIdHex: string) => SealCarryLeaf[], agentPubkeyHex: string, sessionIdHex: string, certifiedRoot: Uint8Array, certifiedLeafCount: number): {
28
+ verdict: "match";
29
+ } | {
30
+ verdict: "mismatch";
31
+ ownRootHex: string | null;
32
+ detail: string;
33
+ } | {
34
+ verdict: "cannot_judge";
35
+ reason: string;
36
+ };
37
+ //# sourceMappingURL=seal-certified-root-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seal-certified-root-check.d.ts","sourceRoot":"","sources":["../src/seal-certified-root-check.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,aAAa,EAAE,EAC/E,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,UAAU,EACzB,kBAAkB,EAAE,MAAM,GACzB;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CA6FzI"}