@cello-protocol/client 0.0.2

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 (53) hide show
  1. package/dist/agent-hash-queue.d.ts +206 -0
  2. package/dist/agent-hash-queue.d.ts.map +1 -0
  3. package/dist/agent-hash-queue.js +380 -0
  4. package/dist/agent-hash-queue.js.map +1 -0
  5. package/dist/backup-key-derivation.d.ts +37 -0
  6. package/dist/backup-key-derivation.d.ts.map +1 -0
  7. package/dist/backup-key-derivation.js +48 -0
  8. package/dist/backup-key-derivation.js.map +1 -0
  9. package/dist/client-backup.d.ts +144 -0
  10. package/dist/client-backup.d.ts.map +1 -0
  11. package/dist/client-backup.js +273 -0
  12. package/dist/client-backup.js.map +1 -0
  13. package/dist/client.d.ts +249 -0
  14. package/dist/client.d.ts.map +1 -0
  15. package/dist/client.js +4664 -0
  16. package/dist/client.js.map +1 -0
  17. package/dist/connection-policy.d.ts +163 -0
  18. package/dist/connection-policy.d.ts.map +1 -0
  19. package/dist/connection-policy.js +248 -0
  20. package/dist/connection-policy.js.map +1 -0
  21. package/dist/db-key-derivation.d.ts +26 -0
  22. package/dist/db-key-derivation.d.ts.map +1 -0
  23. package/dist/db-key-derivation.js +37 -0
  24. package/dist/db-key-derivation.js.map +1 -0
  25. package/dist/encrypted-file-signing-key-provider.d.ts +92 -0
  26. package/dist/encrypted-file-signing-key-provider.d.ts.map +1 -0
  27. package/dist/encrypted-file-signing-key-provider.js +251 -0
  28. package/dist/encrypted-file-signing-key-provider.js.map +1 -0
  29. package/dist/index.d.ts +13 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +8 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/mcp-server.d.ts +270 -0
  34. package/dist/mcp-server.d.ts.map +1 -0
  35. package/dist/mcp-server.js +1155 -0
  36. package/dist/mcp-server.js.map +1 -0
  37. package/dist/network-directory-node.d.ts +85 -0
  38. package/dist/network-directory-node.d.ts.map +1 -0
  39. package/dist/network-directory-node.js +584 -0
  40. package/dist/network-directory-node.js.map +1 -0
  41. package/dist/s3-cloud-storage-provider.d.ts +54 -0
  42. package/dist/s3-cloud-storage-provider.d.ts.map +1 -0
  43. package/dist/s3-cloud-storage-provider.js +78 -0
  44. package/dist/s3-cloud-storage-provider.js.map +1 -0
  45. package/dist/sqlcipher-client-store.d.ts +68 -0
  46. package/dist/sqlcipher-client-store.d.ts.map +1 -0
  47. package/dist/sqlcipher-client-store.js +382 -0
  48. package/dist/sqlcipher-client-store.js.map +1 -0
  49. package/dist/types.d.ts +408 -0
  50. package/dist/types.d.ts.map +1 -0
  51. package/dist/types.js +7 -0
  52. package/dist/types.js.map +1 -0
  53. package/package.json +48 -0
@@ -0,0 +1,248 @@
1
+ /**
2
+ * @cello-protocol/client — CELLO-CONNPOL-001
3
+ * Connection policy engine.
4
+ *
5
+ * Evaluates an incoming connection package against a SignalRequirementPolicy
6
+ * and a DirectoryContext to produce a ConnectionReport.
7
+ *
8
+ * ──── Phase P: Pseudocode ────
9
+ *
10
+ * evaluateConnectionPackage(validatedPackage, policy, context, current_timestamp_ms):
11
+ *
12
+ * 1. If policy.mode === 'closed'
13
+ * return { verdict: 'auto_reject', reason: 'policy_closed' }
14
+ *
15
+ * 2. If context.is_provisional === true
16
+ * return { verdict: 'auto_reject', reason: 'is_provisional' }
17
+ *
18
+ * 3. If validatedPackage.valid === false
19
+ * return { verdict: 'auto_reject', reason: 'pseudonym_binding_invalid' }
20
+ *
21
+ * 4. If policy.mode === 'open' && policy.review_mode === 'deterministic'
22
+ * return { verdict: 'auto_accept' }
23
+ *
24
+ * 5. If policy.mode === 'open' && policy.review_mode === 'inference'
25
+ * return { verdict: 'pending_agent_review', ... zero unmet requirements ... }
26
+ *
27
+ * 6. For mode 'selective' | 'guarded' (identical in M3):
28
+ * evaluate each requirement → collect met[] and unmet[]
29
+ * if review_mode === 'deterministic':
30
+ * unmet.length === 0 → auto_accept
31
+ * unmet.length > 0 → auto_insufficient
32
+ * if review_mode === 'inference':
33
+ * → pending_agent_review (always, with met/unmet summary)
34
+ *
35
+ * Requirement evaluation (evaluateRequirement):
36
+ *
37
+ * endorsement + min_count:
38
+ * count = endorsements.filter(e => e.validation_status === 'valid').length
39
+ * met iff count >= condition.count
40
+ * unmet: { signal_type: 'endorsement', condition, provided: count }
41
+ *
42
+ * pseudonym_age + min_age_days:
43
+ * age = floor((current_timestamp_ms - pseudonym_binding.created_at) / 86_400_000)
44
+ * met iff age >= condition.days
45
+ * unmet: { signal_type: 'pseudonym_age', condition, provided_days: age }
46
+ *
47
+ * registration_age + min_age_days:
48
+ * age = floor((current_timestamp_ms - context.registered_at) / 86_400_000)
49
+ * met iff age >= condition.days
50
+ * unmet: { signal_type: 'registration_age', condition, provided_days: age }
51
+ *
52
+ * attestation + attestation_type:
53
+ * for each type in condition.required: check at least one valid attestation with that type
54
+ * missing_types = types where no valid attestation exists
55
+ * met iff missing_types.length === 0
56
+ * unmet: { signal_type: 'attestation', condition, missing_types }
57
+ *
58
+ * any requirement with from_shared_contact condition:
59
+ * unsatisfiable in M3 → { signal_type, condition, unsupported_condition: true }
60
+ *
61
+ * References:
62
+ * CELLO-CONNPOL-001 story YAML
63
+ */
64
+ // ─── Default policies ─────────────────────────────────────────────────────────
65
+ export const OPEN_POLICY = {
66
+ mode: "open",
67
+ review_mode: "inference",
68
+ requirements: [],
69
+ };
70
+ export const SELECTIVE_DEFAULT = {
71
+ mode: "selective",
72
+ review_mode: "inference",
73
+ requirements: [{ signal_type: "endorsement", condition: { type: "min_count", count: 1 } }],
74
+ };
75
+ export const CLOSED_POLICY = {
76
+ mode: "closed",
77
+ review_mode: "deterministic",
78
+ requirements: [],
79
+ };
80
+ // ─── Engine ───────────────────────────────────────────────────────────────────
81
+ /**
82
+ * Evaluate an incoming connection package against a policy and directory context.
83
+ *
84
+ * Evaluation order is hard-coded and non-negotiable:
85
+ * 1. closed → auto_reject: policy_closed
86
+ * 2. is_provisional → auto_reject: is_provisional
87
+ * 3. package.valid === false → auto_reject: pseudonym_binding_invalid
88
+ * 4. open + deterministic → auto_accept
89
+ * 5. open + inference → pending_agent_review (zero requirements)
90
+ * 6. selective/guarded: evaluate requirements, then:
91
+ * - deterministic + all met → auto_accept
92
+ * - deterministic + any unmet → auto_insufficient
93
+ * - inference → pending_agent_review (with met/unmet lists)
94
+ */
95
+ export function evaluateConnectionPackage(validatedPackage, policy, context, current_timestamp_ms) {
96
+ // Step 1: closed mode — reject before any other check
97
+ if (policy.mode === "closed") {
98
+ return { verdict: "auto_reject", reason: "policy_closed" };
99
+ }
100
+ // Step 2: provisional agent — cannot receive connections
101
+ if (context.is_provisional) {
102
+ return { verdict: "auto_reject", reason: "is_provisional" };
103
+ }
104
+ // Step 3: invalid package — pseudonym binding failed
105
+ if (!validatedPackage.valid) {
106
+ return { verdict: "auto_reject", reason: "pseudonym_binding_invalid" };
107
+ }
108
+ // From here: validatedPackage is valid (valid: true branch)
109
+ const pkg = validatedPackage;
110
+ // Step 4: open + deterministic → auto_accept
111
+ if (policy.mode === "open" && policy.review_mode === "deterministic") {
112
+ return { verdict: "auto_accept" };
113
+ }
114
+ // Step 5: open + inference → pending_agent_review with zero requirements
115
+ if (policy.mode === "open" && policy.review_mode === "inference") {
116
+ return {
117
+ verdict: "pending_agent_review",
118
+ policy_summary: {
119
+ mode: policy.mode,
120
+ review_mode: "inference",
121
+ requirements_met: [],
122
+ requirements_unmet: [],
123
+ },
124
+ package_summary: buildPackageSummary(pkg, context, current_timestamp_ms),
125
+ is_round_2: false,
126
+ };
127
+ }
128
+ // Step 6: selective | guarded — evaluate requirements
129
+ const metRequirements = [];
130
+ const unmetRequirements = [];
131
+ for (const req of policy.requirements) {
132
+ const result = evaluateRequirement(req, pkg, context, current_timestamp_ms);
133
+ if (result === null) {
134
+ metRequirements.push(req);
135
+ }
136
+ else {
137
+ unmetRequirements.push(result);
138
+ }
139
+ }
140
+ if (policy.review_mode === "deterministic") {
141
+ if (unmetRequirements.length === 0) {
142
+ return { verdict: "auto_accept" };
143
+ }
144
+ return { verdict: "auto_insufficient", unmet_requirements: unmetRequirements };
145
+ }
146
+ // review_mode === 'inference'
147
+ return {
148
+ verdict: "pending_agent_review",
149
+ policy_summary: {
150
+ mode: policy.mode,
151
+ review_mode: "inference",
152
+ requirements_met: metRequirements,
153
+ requirements_unmet: unmetRequirements,
154
+ },
155
+ package_summary: buildPackageSummary(pkg, context, current_timestamp_ms),
156
+ is_round_2: false,
157
+ };
158
+ }
159
+ // ─── Requirement evaluation ───────────────────────────────────────────────────
160
+ /**
161
+ * Evaluate a single requirement.
162
+ *
163
+ * Returns null if met, or an UnmetRequirement if not.
164
+ */
165
+ function evaluateRequirement(req, pkg, context, current_timestamp_ms) {
166
+ const { condition } = req;
167
+ // from_shared_contact is M6-only — always unsatisfiable in M3
168
+ if (condition.type === "from_shared_contact") {
169
+ return { signal_type: req.signal_type, condition, unsupported_condition: true };
170
+ }
171
+ switch (req.signal_type) {
172
+ case "endorsement":
173
+ return evaluateEndorsementRequirement(condition, pkg.endorsements);
174
+ case "pseudonym_age":
175
+ return evaluatePseudonymAgeRequirement(condition, pkg.pseudonym_binding.created_at, current_timestamp_ms);
176
+ case "registration_age":
177
+ return evaluateRegistrationAgeRequirement(condition, context.registered_at, current_timestamp_ms);
178
+ case "attestation":
179
+ return evaluateAttestationRequirement(condition, pkg.attestations);
180
+ default:
181
+ // Unknown signal_type — treat as unsupported
182
+ return { signal_type: req.signal_type, condition, unsupported_condition: true };
183
+ }
184
+ }
185
+ function evaluateEndorsementRequirement(condition, endorsements) {
186
+ if (condition.type === "min_count") {
187
+ const validCount = endorsements.filter((e) => e.validation_status === "valid").length;
188
+ if (validCount >= condition.count) {
189
+ return null;
190
+ }
191
+ return { signal_type: "endorsement", condition, provided: validCount };
192
+ }
193
+ // Unrecognized condition for endorsement — unsupported
194
+ return { signal_type: "endorsement", condition, unsupported_condition: true };
195
+ }
196
+ function evaluatePseudonymAgeRequirement(condition, created_at, current_timestamp_ms) {
197
+ if (condition.type === "min_age_days") {
198
+ const ageDays = Math.floor((current_timestamp_ms - created_at) / 86_400_000);
199
+ if (ageDays >= condition.days) {
200
+ return null;
201
+ }
202
+ return { signal_type: "pseudonym_age", condition, provided_days: ageDays };
203
+ }
204
+ return { signal_type: "pseudonym_age", condition, unsupported_condition: true };
205
+ }
206
+ function evaluateRegistrationAgeRequirement(condition, registered_at, current_timestamp_ms) {
207
+ if (condition.type === "min_age_days") {
208
+ const ageDays = Math.floor((current_timestamp_ms - registered_at) / 86_400_000);
209
+ if (ageDays >= condition.days) {
210
+ return null;
211
+ }
212
+ return { signal_type: "registration_age", condition, provided_days: ageDays };
213
+ }
214
+ return { signal_type: "registration_age", condition, unsupported_condition: true };
215
+ }
216
+ function evaluateAttestationRequirement(condition, attestations) {
217
+ if (condition.type === "attestation_type") {
218
+ const validAttestationTypes = new Set(attestations
219
+ .filter((a) => a.validation_status === "valid")
220
+ .map((a) => a.attestation_type));
221
+ const missingTypes = condition.required.filter((t) => !validAttestationTypes.has(t));
222
+ if (missingTypes.length === 0) {
223
+ return null;
224
+ }
225
+ return { signal_type: "attestation", condition, missing_types: missingTypes };
226
+ }
227
+ return { signal_type: "attestation", condition, unsupported_condition: true };
228
+ }
229
+ // ─── Package summary builder ──────────────────────────────────────────────────
230
+ function buildPackageSummary(pkg, context, current_timestamp_ms) {
231
+ const pseudonymAgeDays = Math.floor((current_timestamp_ms - pkg.pseudonym_binding.created_at) / 86_400_000);
232
+ const registrationAgeDays = Math.floor((current_timestamp_ms - context.registered_at) / 86_400_000);
233
+ const validEndorsementCount = pkg.endorsements.filter((e) => e.validation_status === "valid").length;
234
+ const validAttestationTypes = [
235
+ ...new Set(pkg.attestations
236
+ .filter((a) => a.validation_status === "valid")
237
+ .map((a) => a.attestation_type)),
238
+ ];
239
+ return {
240
+ pseudonym_label: pkg.pseudonym_binding.pseudonym_label,
241
+ endorsement_count: validEndorsementCount,
242
+ attestation_types: validAttestationTypes,
243
+ pseudonym_age_days: pseudonymAgeDays,
244
+ registration_age_days: registrationAgeDays,
245
+ is_provisional: context.is_provisional,
246
+ };
247
+ }
248
+ //# sourceMappingURL=connection-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-policy.js","sourceRoot":"","sources":["../src/connection-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AA6EH,iFAAiF;AAEjF,MAAM,CAAC,MAAM,WAAW,GAA4B;IAClD,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,WAAW;IACxB,YAAY,EAAE,EAAE;CACjB,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAA4B;IACxD,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,WAAW;IACxB,YAAY,EAAE,CAAC,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;CAC3F,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAA4B;IACpD,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,eAAe;IAC5B,YAAY,EAAE,EAAE;CACjB,CAAC;AAEF,iFAAiF;AAEjF;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,yBAAyB,CACvC,gBAAyC,EACzC,MAA+B,EAC/B,OAAyB,EACzB,oBAA4B;IAE5B,sDAAsD;IACtD,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAC7D,CAAC;IAED,yDAAyD;IACzD,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAC3B,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC9D,CAAC;IAED,qDAAqD;IACrD,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;QAC5B,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAC;IACzE,CAAC;IAED,4DAA4D;IAC5D,MAAM,GAAG,GAAG,gBAAgB,CAAC;IAE7B,6CAA6C;IAC7C,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,WAAW,KAAK,eAAe,EAAE,CAAC;QACrE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;IACpC,CAAC;IAED,yEAAyE;IACzE,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,WAAW,KAAK,WAAW,EAAE,CAAC;QACjE,OAAO;YACL,OAAO,EAAE,sBAAsB;YAC/B,cAAc,EAAE;gBACd,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,WAAW,EAAE,WAAW;gBACxB,gBAAgB,EAAE,EAAE;gBACpB,kBAAkB,EAAE,EAAE;aACvB;YACD,eAAe,EAAE,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,oBAAoB,CAAC;YACxE,UAAU,EAAE,KAAK;SAClB,CAAC;IACJ,CAAC;IAED,sDAAsD;IACtD,MAAM,eAAe,GAAwB,EAAE,CAAC;IAChD,MAAM,iBAAiB,GAAuB,EAAE,CAAC;IAEjD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,oBAAoB,CAAC,CAAC;QAC5E,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,WAAW,KAAK,eAAe,EAAE,CAAC;QAC3C,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnC,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;QACpC,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,CAAC;IACjF,CAAC;IAED,8BAA8B;IAC9B,OAAO;QACL,OAAO,EAAE,sBAAsB;QAC/B,cAAc,EAAE;YACd,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,WAAW,EAAE,WAAW;YACxB,gBAAgB,EAAE,eAAe;YACjC,kBAAkB,EAAE,iBAAiB;SACtC;QACD,eAAe,EAAE,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,oBAAoB,CAAC;QACxE,UAAU,EAAE,KAAK;KAClB,CAAC;AACJ,CAAC;AAED,iFAAiF;AAEjF;;;;GAIG;AACH,SAAS,mBAAmB,CAC1B,GAAsB,EACtB,GAAsD,EACtD,OAAyB,EACzB,oBAA4B;IAE5B,MAAM,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC;IAE1B,8DAA8D;IAC9D,IAAI,SAAS,CAAC,IAAI,KAAK,qBAAqB,EAAE,CAAC;QAC7C,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,SAAS,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC;IAClF,CAAC;IAED,QAAQ,GAAG,CAAC,WAAW,EAAE,CAAC;QACxB,KAAK,aAAa;YAChB,OAAO,8BAA8B,CAAC,SAAS,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;QAErE,KAAK,eAAe;YAClB,OAAO,+BAA+B,CAAC,SAAS,EAAE,GAAG,CAAC,iBAAiB,CAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC;QAE5G,KAAK,kBAAkB;YACrB,OAAO,kCAAkC,CAAC,SAAS,EAAE,OAAO,CAAC,aAAa,EAAE,oBAAoB,CAAC,CAAC;QAEpG,KAAK,aAAa;YAChB,OAAO,8BAA8B,CAAC,SAAS,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;QAErE;YACE,6CAA6C;YAC7C,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,SAAS,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC;IACpF,CAAC;AACH,CAAC;AAED,SAAS,8BAA8B,CACrC,SAA0B,EAC1B,YAAoC;IAEpC,IAAI,SAAS,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QACnC,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;QACtF,IAAI,UAAU,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IACzE,CAAC;IACD,uDAAuD;IACvD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC;AAChF,CAAC;AAED,SAAS,+BAA+B,CACtC,SAA0B,EAC1B,UAAkB,EAClB,oBAA4B;IAE5B,IAAI,SAAS,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,oBAAoB,GAAG,UAAU,CAAC,GAAG,UAAU,CAAC,CAAC;QAC7E,IAAI,OAAO,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;IAC7E,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC;AAClF,CAAC;AAED,SAAS,kCAAkC,CACzC,SAA0B,EAC1B,aAAqB,EACrB,oBAA4B;IAE5B,IAAI,SAAS,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,oBAAoB,GAAG,aAAa,CAAC,GAAG,UAAU,CAAC,CAAC;QAChF,IAAI,OAAO,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;IAChF,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,SAAS,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC;AACrF,CAAC;AAED,SAAS,8BAA8B,CACrC,SAA0B,EAC1B,YAAoC;IAEpC,IAAI,SAAS,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;QAC1C,MAAM,qBAAqB,GAAG,IAAI,GAAG,CACnC,YAAY;aACT,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB,KAAK,OAAO,CAAC;aAC9C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAClC,CAAC;QACF,MAAM,YAAY,GAAG,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACrF,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC;IAChF,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC;AAChF,CAAC;AAED,iFAAiF;AAEjF,SAAS,mBAAmB,CAC1B,GAAsD,EACtD,OAAyB,EACzB,oBAA4B;IAS5B,MAAM,gBAAgB,GAAG,IAAI,CAAC,KAAK,CACjC,CAAC,oBAAoB,GAAG,GAAG,CAAC,iBAAiB,CAAC,UAAU,CAAC,GAAG,UAAU,CACvE,CAAC;IACF,MAAM,mBAAmB,GAAG,IAAI,CAAC,KAAK,CACpC,CAAC,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,GAAG,UAAU,CAC5D,CAAC;IACF,MAAM,qBAAqB,GAAG,GAAG,CAAC,YAAY,CAAC,MAAM,CACnD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB,KAAK,OAAO,CACvC,CAAC,MAAM,CAAC;IACT,MAAM,qBAAqB,GAAG;QAC5B,GAAG,IAAI,GAAG,CACR,GAAG,CAAC,YAAY;aACb,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB,KAAK,OAAO,CAAC;aAC9C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAClC;KACF,CAAC;IAEF,OAAO;QACL,eAAe,EAAE,GAAG,CAAC,iBAAiB,CAAC,eAAe;QACtD,iBAAiB,EAAE,qBAAqB;QACxC,iBAAiB,EAAE,qBAAqB;QACxC,kBAAkB,EAAE,gBAAgB;QACpC,qBAAqB,EAAE,mBAAmB;QAC1C,cAAc,EAAE,OAAO,CAAC,cAAc;KACvC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * db-key-derivation.ts — HKDF derivation of the local database encryption key.
3
+ *
4
+ * PERSIST-009: db_key = HKDF-SHA256(ikm=identity_key, salt=none, info='local-db-key' || agent_id, length=32)
5
+ *
6
+ * Security invariants (PERSIST-009 SI-002):
7
+ * - identity_key is passed in by the caller; it is NEVER stored here.
8
+ * - The returned db_key is NEVER logged or persisted by this function.
9
+ * - This function has no side effects — pure transformation only.
10
+ *
11
+ * RFC reference: RFC 5869 (HKDF). Node.js implementation: crypto.hkdfSync.
12
+ */
13
+ /**
14
+ * Derive a 32-byte database encryption key from the agent's identity_key.
15
+ *
16
+ * @param identityKey - The agent's long-term root key (32 bytes). Never stored or logged.
17
+ * @param agentId - The stable agent identifier. Binds the key to a specific agent.
18
+ * @returns - A 32-byte Uint8Array suitable for use as a SQLCipher PRAGMA key.
19
+ *
20
+ * Security: The db_key is deterministic — same inputs always produce the same output.
21
+ * This is intentional: the client must re-derive the key on every startup without
22
+ * storing it, using only the identity_key (which is stored separately, protected
23
+ * by the OS keychain or equivalent).
24
+ */
25
+ export declare function deriveDbKey(identityKey: Uint8Array, agentId: string): Uint8Array;
26
+ //# sourceMappingURL=db-key-derivation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-key-derivation.d.ts","sourceRoot":"","sources":["../src/db-key-derivation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAYhF"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * db-key-derivation.ts — HKDF derivation of the local database encryption key.
3
+ *
4
+ * PERSIST-009: db_key = HKDF-SHA256(ikm=identity_key, salt=none, info='local-db-key' || agent_id, length=32)
5
+ *
6
+ * Security invariants (PERSIST-009 SI-002):
7
+ * - identity_key is passed in by the caller; it is NEVER stored here.
8
+ * - The returned db_key is NEVER logged or persisted by this function.
9
+ * - This function has no side effects — pure transformation only.
10
+ *
11
+ * RFC reference: RFC 5869 (HKDF). Node.js implementation: crypto.hkdfSync.
12
+ */
13
+ import { hkdfSync } from "node:crypto";
14
+ /**
15
+ * Derive a 32-byte database encryption key from the agent's identity_key.
16
+ *
17
+ * @param identityKey - The agent's long-term root key (32 bytes). Never stored or logged.
18
+ * @param agentId - The stable agent identifier. Binds the key to a specific agent.
19
+ * @returns - A 32-byte Uint8Array suitable for use as a SQLCipher PRAGMA key.
20
+ *
21
+ * Security: The db_key is deterministic — same inputs always produce the same output.
22
+ * This is intentional: the client must re-derive the key on every startup without
23
+ * storing it, using only the identity_key (which is stored separately, protected
24
+ * by the OS keychain or equivalent).
25
+ */
26
+ export function deriveDbKey(identityKey, agentId) {
27
+ // info = 'local-db-key' || NUL || agentId (UTF-8 encoded).
28
+ // The null-byte separator prevents a theoretical prefix-collision where two
29
+ // different (literal, agentId) pairs could produce the same concatenation.
30
+ const infoStr = `local-db-key\x00${agentId}`;
31
+ const info = Buffer.from(infoStr, "utf8");
32
+ // HKDF-SHA256: salt=none (empty Buffer), length=32 bytes
33
+ // RFC 5869 §2.2: when salt is not provided, a string of HashLen zeros is used.
34
+ const derived = hkdfSync("sha256", identityKey, Buffer.alloc(0), info, 32);
35
+ return new Uint8Array(derived);
36
+ }
37
+ //# sourceMappingURL=db-key-derivation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-key-derivation.js","sourceRoot":"","sources":["../src/db-key-derivation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,WAAW,CAAC,WAAuB,EAAE,OAAe;IAClE,2DAA2D;IAC3D,4EAA4E;IAC5E,2EAA2E;IAC3E,MAAM,OAAO,GAAG,mBAAmB,OAAO,EAAE,CAAC;IAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAE1C,yDAAyD;IACzD,+EAA+E;IAC/E,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IAE3E,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * EncryptedFileSigningKeyProvider — file-backed SigningKeyProvider for CELLO_ENV=local/dev.
3
+ *
4
+ * PERSIST-010: Reads the Ed25519 private key seed from an encrypted key file at the
5
+ * configured path, decrypts it into memory for signing, and zeroes the in-memory key
6
+ * buffer after each sign() call (SI-002).
7
+ *
8
+ * The private key NEVER crosses the provider boundary (SI-001). The only exported
9
+ * values are the public key (via getPublicKey()) and signatures (via sign()).
10
+ *
11
+ * Key file format (same as FileKeyProvider in @cello-protocol/crypto):
12
+ * Bytes 0–3: Magic [0xCE, 0x11, 0x0E, 0x01]
13
+ * Byte 4: Version (0x01)
14
+ * Bytes 5–36: 32-byte Ed25519 seed
15
+ *
16
+ * This file is NOT exported from packages/client/src/index.ts.
17
+ * It is only imported by the composition root (server.ts).
18
+ *
19
+ * RFC reference: Ed25519 signing per RFC 8032.
20
+ */
21
+ import type { SigningKeyProvider, SigningPublicKey, SigningSignature, SignOptions } from "@cello-protocol/interfaces";
22
+ import type { Logger } from "@cello-protocol/interfaces";
23
+ export interface EncryptedFileSigningKeyProviderOptions {
24
+ /** Stable agent identifier — used in log events. */
25
+ agentId: string;
26
+ /** Structured logger injected from the composition root. */
27
+ logger: Logger;
28
+ }
29
+ /**
30
+ * SigningKeyProvider backed by an encrypted key file on disk.
31
+ *
32
+ * Lifecycle: EncryptedFileSigningKeyProvider.load(path, opts) → ready to sign.
33
+ *
34
+ * On each sign() call, the seed is read from the file into a temporary buffer,
35
+ * used for signing, and the buffer is zeroed immediately after — even on throw.
36
+ * This ensures the private key exists in process memory only for the minimum
37
+ * time required to produce the signature.
38
+ *
39
+ * Security invariants:
40
+ * SI-001: No method returns/exports the private key.
41
+ * SI-002: Key buffer zeroed after EVERY sign() call, even on throw (try/finally).
42
+ * SI-003: sign() failure propagates — never falls back to weaker signing.
43
+ */
44
+ export declare class EncryptedFileSigningKeyProvider implements SigningKeyProvider {
45
+ #private;
46
+ private constructor();
47
+ /**
48
+ * Load the signing key from the key file at the given path.
49
+ *
50
+ * Validates the file format and derives the public key. The seed itself
51
+ * is not retained — it is re-read from the file on each sign() call.
52
+ *
53
+ * Throws SigningKeyProviderError if:
54
+ * - File not found (reason: 'key_file_not_found')
55
+ * - File corrupt (reason: 'key_file_corrupt')
56
+ *
57
+ * @param path - Absolute path to the encrypted key file (SIGNING_KEY_PATH)
58
+ * @param opts - Configuration including agentId and logger
59
+ */
60
+ static load(path: string, opts: EncryptedFileSigningKeyProviderOptions): Promise<EncryptedFileSigningKeyProvider>;
61
+ /** Return the 32-byte Ed25519 public key. */
62
+ getPublicKey(): Promise<SigningPublicKey>;
63
+ /**
64
+ * Sign data with the Ed25519 private key.
65
+ *
66
+ * Security: On each call, the seed is re-read from the key file into a
67
+ * working buffer, used for signing, and the buffer is zeroed in a finally
68
+ * block — even if signing throws (SI-002).
69
+ *
70
+ * The public key is derived once at load() and cached — it is not sensitive.
71
+ */
72
+ sign(data: Uint8Array, opts?: SignOptions): Promise<SigningSignature>;
73
+ /**
74
+ * @internal Test seam — returns true if the working buffer is currently all-zeros.
75
+ * This does NOT expose key material. Tests use this to verify SI-002 (zeroing)
76
+ * without ever seeing the private key bytes.
77
+ */
78
+ isKeyBufferZeroedForTesting(): boolean;
79
+ /**
80
+ * @internal Test seam — corrupts the provider state to trigger sign() failures
81
+ * BEFORE the key file is read. Used to test SI-003 (no fallback).
82
+ * Note: does NOT exercise the try/finally zeroing path — use throwAfterLoadForTesting() for that.
83
+ */
84
+ corruptForTesting(): void;
85
+ /**
86
+ * @internal Test seam — causes sign() to throw AFTER the key has been loaded into
87
+ * the working buffer but BEFORE ed25519.sign() is called. Used to verify SI-002:
88
+ * the finally block zeroes the key buffer even when sign() throws mid-operation.
89
+ */
90
+ throwAfterLoadForTesting(): void;
91
+ }
92
+ //# sourceMappingURL=encrypted-file-signing-key-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encrypted-file-signing-key-provider.d.ts","sourceRoot":"","sources":["../src/encrypted-file-signing-key-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEtH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAWzD,MAAM,WAAW,sCAAsC;IACrD,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;CAChB;AAKD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,+BAAgC,YAAW,kBAAkB;;IAiBxE,OAAO;IAYP;;;;;;;;;;;;OAYG;WACU,IAAI,CACf,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,sCAAsC,GAC3C,OAAO,CAAC,+BAA+B,CAAC;IAwC3C,6CAA6C;IACvC,YAAY,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAI/C;;;;;;;;OAQG;IACG,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAkE3E;;;;OAIG;IACH,2BAA2B,IAAI,OAAO;IAItC;;;;OAIG;IACH,iBAAiB,IAAI,IAAI;IAIzB;;;;OAIG;IACH,wBAAwB,IAAI,IAAI;CAGjC"}