@cello-protocol/daemon 0.0.3 → 0.0.4

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 (153) hide show
  1. package/dist/agent-loader.d.ts +41 -0
  2. package/dist/agent-loader.d.ts.map +1 -0
  3. package/dist/agent-loader.js +94 -0
  4. package/dist/agent-loader.js.map +1 -0
  5. package/dist/bin/cello-daemon.d.ts +13 -0
  6. package/dist/bin/cello-daemon.d.ts.map +1 -0
  7. package/dist/bin/cello-daemon.js +170 -0
  8. package/dist/bin/cello-daemon.js.map +1 -0
  9. package/dist/cello-node-transport-dialer.d.ts +59 -0
  10. package/dist/cello-node-transport-dialer.d.ts.map +1 -0
  11. package/dist/cello-node-transport-dialer.js +108 -0
  12. package/dist/cello-node-transport-dialer.js.map +1 -0
  13. package/dist/challenge-verifier.d.ts +12 -0
  14. package/dist/challenge-verifier.d.ts.map +1 -0
  15. package/dist/challenge-verifier.js +11 -0
  16. package/dist/challenge-verifier.js.map +1 -0
  17. package/dist/connect-or-start.d.ts +25 -0
  18. package/dist/connect-or-start.d.ts.map +1 -0
  19. package/dist/connect-or-start.js +117 -0
  20. package/dist/connect-or-start.js.map +1 -0
  21. package/dist/content-park-client.d.ts +49 -0
  22. package/dist/content-park-client.d.ts.map +1 -0
  23. package/dist/content-park-client.js +196 -0
  24. package/dist/content-park-client.js.map +1 -0
  25. package/dist/daemon.d.ts +65 -0
  26. package/dist/daemon.d.ts.map +1 -0
  27. package/dist/daemon.js +3202 -0
  28. package/dist/daemon.js.map +1 -0
  29. package/dist/directory-bootstrap.d.ts +55 -0
  30. package/dist/directory-bootstrap.d.ts.map +1 -0
  31. package/dist/directory-bootstrap.js +102 -0
  32. package/dist/directory-bootstrap.js.map +1 -0
  33. package/dist/file-manifest-provider.d.ts +18 -0
  34. package/dist/file-manifest-provider.d.ts.map +1 -0
  35. package/dist/file-manifest-provider.js +72 -0
  36. package/dist/file-manifest-provider.js.map +1 -0
  37. package/dist/index.d.ts +18 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +18 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/ipc-client.d.ts +31 -0
  42. package/dist/ipc-client.d.ts.map +1 -0
  43. package/dist/ipc-client.js +112 -0
  44. package/dist/ipc-client.js.map +1 -0
  45. package/dist/ipc-server.d.ts +49 -0
  46. package/dist/ipc-server.d.ts.map +1 -0
  47. package/dist/ipc-server.js +268 -0
  48. package/dist/ipc-server.js.map +1 -0
  49. package/dist/lock-file.d.ts +27 -0
  50. package/dist/lock-file.d.ts.map +1 -0
  51. package/dist/lock-file.js +84 -0
  52. package/dist/lock-file.js.map +1 -0
  53. package/dist/manifest-loader.d.ts +33 -0
  54. package/dist/manifest-loader.d.ts.map +1 -0
  55. package/dist/manifest-loader.js +70 -0
  56. package/dist/manifest-loader.js.map +1 -0
  57. package/dist/manifest-poll-scheduler.d.ts +31 -0
  58. package/dist/manifest-poll-scheduler.d.ts.map +1 -0
  59. package/dist/manifest-poll-scheduler.js +59 -0
  60. package/dist/manifest-poll-scheduler.js.map +1 -0
  61. package/dist/manifest-version-store-file.d.ts +18 -0
  62. package/dist/manifest-version-store-file.d.ts.map +1 -0
  63. package/dist/manifest-version-store-file.js +40 -0
  64. package/dist/manifest-version-store-file.js.map +1 -0
  65. package/dist/manifest-version-store.d.ts +14 -0
  66. package/dist/manifest-version-store.d.ts.map +1 -0
  67. package/dist/manifest-version-store.js +13 -0
  68. package/dist/manifest-version-store.js.map +1 -0
  69. package/dist/network-directory-node.d.ts +94 -0
  70. package/dist/network-directory-node.d.ts.map +1 -0
  71. package/dist/network-directory-node.js +626 -0
  72. package/dist/network-directory-node.js.map +1 -0
  73. package/dist/nonce-dedup.d.ts +68 -0
  74. package/dist/nonce-dedup.d.ts.map +1 -0
  75. package/dist/nonce-dedup.js +204 -0
  76. package/dist/nonce-dedup.js.map +1 -0
  77. package/dist/notification-dispatcher.d.ts +65 -0
  78. package/dist/notification-dispatcher.d.ts.map +1 -0
  79. package/dist/notification-dispatcher.js +138 -0
  80. package/dist/notification-dispatcher.js.map +1 -0
  81. package/dist/registration-context.d.ts +69 -0
  82. package/dist/registration-context.d.ts.map +1 -0
  83. package/dist/registration-context.js +118 -0
  84. package/dist/registration-context.js.map +1 -0
  85. package/dist/registration-manager.d.ts +72 -0
  86. package/dist/registration-manager.d.ts.map +1 -0
  87. package/dist/registration-manager.js +267 -0
  88. package/dist/registration-manager.js.map +1 -0
  89. package/dist/registration-persistence.d.ts +131 -0
  90. package/dist/registration-persistence.d.ts.map +1 -0
  91. package/dist/registration-persistence.js +233 -0
  92. package/dist/registration-persistence.js.map +1 -0
  93. package/dist/retry-queue.d.ts +144 -0
  94. package/dist/retry-queue.d.ts.map +1 -0
  95. package/dist/retry-queue.js +444 -0
  96. package/dist/retry-queue.js.map +1 -0
  97. package/dist/seal-frontier-verify.d.ts +58 -0
  98. package/dist/seal-frontier-verify.d.ts.map +1 -0
  99. package/dist/seal-frontier-verify.js +87 -0
  100. package/dist/seal-frontier-verify.js.map +1 -0
  101. package/dist/seal-legibility-tbs.d.ts +25 -0
  102. package/dist/seal-legibility-tbs.d.ts.map +1 -0
  103. package/dist/seal-legibility-tbs.js +78 -0
  104. package/dist/seal-legibility-tbs.js.map +1 -0
  105. package/dist/seal-upgrade.d.ts +90 -0
  106. package/dist/seal-upgrade.d.ts.map +1 -0
  107. package/dist/seal-upgrade.js +178 -0
  108. package/dist/seal-upgrade.js.map +1 -0
  109. package/dist/session-assignment-parser.d.ts +22 -0
  110. package/dist/session-assignment-parser.d.ts.map +1 -0
  111. package/dist/session-assignment-parser.js +139 -0
  112. package/dist/session-assignment-parser.js.map +1 -0
  113. package/dist/session-ceremony.d.ts +156 -0
  114. package/dist/session-ceremony.d.ts.map +1 -0
  115. package/dist/session-ceremony.js +447 -0
  116. package/dist/session-ceremony.js.map +1 -0
  117. package/dist/session-connection-gater.d.ts +91 -0
  118. package/dist/session-connection-gater.d.ts.map +1 -0
  119. package/dist/session-connection-gater.js +146 -0
  120. package/dist/session-connection-gater.js.map +1 -0
  121. package/dist/session-node-manager.d.ts +585 -0
  122. package/dist/session-node-manager.d.ts.map +1 -0
  123. package/dist/session-node-manager.js +2609 -0
  124. package/dist/session-node-manager.js.map +1 -0
  125. package/dist/session-relay-client.d.ts +101 -0
  126. package/dist/session-relay-client.d.ts.map +1 -0
  127. package/dist/session-relay-client.js +520 -0
  128. package/dist/session-relay-client.js.map +1 -0
  129. package/dist/session-tree.d.ts +80 -0
  130. package/dist/session-tree.d.ts.map +1 -0
  131. package/dist/session-tree.js +123 -0
  132. package/dist/session-tree.js.map +1 -0
  133. package/dist/signaling-connect.d.ts +83 -0
  134. package/dist/signaling-connect.d.ts.map +1 -0
  135. package/dist/signaling-connect.js +266 -0
  136. package/dist/signaling-connect.js.map +1 -0
  137. package/dist/transcript-cipher.d.ts +31 -0
  138. package/dist/transcript-cipher.d.ts.map +1 -0
  139. package/dist/transcript-cipher.js +74 -0
  140. package/dist/transcript-cipher.js.map +1 -0
  141. package/dist/transport-composition.d.ts +31 -0
  142. package/dist/transport-composition.d.ts.map +1 -0
  143. package/dist/transport-composition.js +55 -0
  144. package/dist/transport-composition.js.map +1 -0
  145. package/dist/transport-selector.d.ts +189 -0
  146. package/dist/transport-selector.d.ts.map +1 -0
  147. package/dist/transport-selector.js +195 -0
  148. package/dist/transport-selector.js.map +1 -0
  149. package/dist/types.d.ts +265 -0
  150. package/dist/types.d.ts.map +1 -0
  151. package/dist/types.js +33 -0
  152. package/dist/types.js.map +1 -0
  153. package/package.json +1 -1
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Agent identity loader for the CELLO daemon.
3
+ *
4
+ * Pseudocode:
5
+ * 1. loadAgents(celloDir, logger):
6
+ * a. Check if ~/.cello/agents/ exists
7
+ * b. If not, check if ~/.cello/key exists (legacy single-agent mode)
8
+ * - If legacy key exists, load it as agent 'default'
9
+ * c. If agents/ exists, enumerate subdirectories
10
+ * - For each subdir, check if a 'key' file exists
11
+ * - If yes, attempt to load it via FileKeyProvider.load()
12
+ * - If load fails (corrupt), log agent.load.failed and skip
13
+ * - Subdirectories without a 'key' file are silently skipped
14
+ * d. Return array of {name, pubkey} for successfully loaded agents
15
+ *
16
+ * Key file format: CELLO binary format (magic bytes + version + 32-byte Ed25519 seed)
17
+ * See core/crypto/src/ed25519.ts FileKeyProvider.load() for the canonical parser.
18
+ */
19
+ import type { KeyProvider } from "@cello-protocol/crypto";
20
+ import type { Logger } from "./types.js";
21
+ export interface LoadedAgent {
22
+ name: string;
23
+ pubkey: string;
24
+ /**
25
+ * The agent's K_local signing key, retained so the daemon can produce
26
+ * K_local-signed control leaves (e.g. the SEAL-INTERRUPTED leaf in the
27
+ * seal-interrupted bilateral flow). The private scalar never leaves this
28
+ * provider — only signatures are emitted.
29
+ */
30
+ keyProvider: KeyProvider;
31
+ }
32
+ export interface FailedAgent {
33
+ name: string;
34
+ error: string;
35
+ }
36
+ export interface AgentLoadResult {
37
+ loaded: LoadedAgent[];
38
+ failed: FailedAgent[];
39
+ }
40
+ export declare function loadAgents(celloDir: string, logger: Logger): Promise<AgentLoadResult>;
41
+ //# sourceMappingURL=agent-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-loader.d.ts","sourceRoot":"","sources":["../src/agent-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAwC3F"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Agent identity loader for the CELLO daemon.
3
+ *
4
+ * Pseudocode:
5
+ * 1. loadAgents(celloDir, logger):
6
+ * a. Check if ~/.cello/agents/ exists
7
+ * b. If not, check if ~/.cello/key exists (legacy single-agent mode)
8
+ * - If legacy key exists, load it as agent 'default'
9
+ * c. If agents/ exists, enumerate subdirectories
10
+ * - For each subdir, check if a 'key' file exists
11
+ * - If yes, attempt to load it via FileKeyProvider.load()
12
+ * - If load fails (corrupt), log agent.load.failed and skip
13
+ * - Subdirectories without a 'key' file are silently skipped
14
+ * d. Return array of {name, pubkey} for successfully loaded agents
15
+ *
16
+ * Key file format: CELLO binary format (magic bytes + version + 32-byte Ed25519 seed)
17
+ * See core/crypto/src/ed25519.ts FileKeyProvider.load() for the canonical parser.
18
+ */
19
+ import { readdir, stat, access } from "node:fs/promises";
20
+ import { join } from "node:path";
21
+ import { FileKeyProvider } from "@cello-protocol/crypto";
22
+ export async function loadAgents(celloDir, logger) {
23
+ const agentsDir = join(celloDir, "agents");
24
+ const loaded = [];
25
+ const failed = [];
26
+ const agentsDirExists = await directoryExists(agentsDir);
27
+ if (!agentsDirExists) {
28
+ // Legacy backwards compat: if ~/.cello/key exists and ~/.cello/agents/ doesn't
29
+ const legacyKeyPath = join(celloDir, "key");
30
+ if (await fileExists(legacyKeyPath)) {
31
+ const result = await loadSingleAgent("default", legacyKeyPath, logger);
32
+ if (result.ok) {
33
+ loaded.push(result.agent);
34
+ }
35
+ else {
36
+ failed.push({ name: "default", error: result.error });
37
+ }
38
+ }
39
+ return { loaded, failed };
40
+ }
41
+ // Enumerate subdirectories of agents/
42
+ const entries = await readdir(agentsDir, { withFileTypes: true });
43
+ for (const entry of entries) {
44
+ if (!entry.isDirectory())
45
+ continue;
46
+ const keyPath = join(agentsDir, entry.name, "key");
47
+ if (!(await fileExists(keyPath))) {
48
+ continue;
49
+ }
50
+ const result = await loadSingleAgent(entry.name, keyPath, logger);
51
+ if (result.ok) {
52
+ loaded.push(result.agent);
53
+ }
54
+ else {
55
+ failed.push({ name: entry.name, error: result.error });
56
+ }
57
+ }
58
+ return { loaded, failed };
59
+ }
60
+ async function loadSingleAgent(name, keyPath, logger) {
61
+ try {
62
+ const keyProvider = await FileKeyProvider.load(keyPath);
63
+ const pubkeyBytes = await keyProvider.getPublicKey();
64
+ const pubkey = Buffer.from(pubkeyBytes).toString("hex");
65
+ return { ok: true, agent: { name, pubkey, keyProvider } };
66
+ }
67
+ catch (err) {
68
+ const errorMsg = err instanceof Error ? err.message : String(err);
69
+ logger.error("agent.load.failed", {
70
+ agentName: name,
71
+ error: errorMsg,
72
+ });
73
+ return { ok: false, error: errorMsg };
74
+ }
75
+ }
76
+ async function directoryExists(path) {
77
+ try {
78
+ const s = await stat(path);
79
+ return s.isDirectory();
80
+ }
81
+ catch {
82
+ return false;
83
+ }
84
+ }
85
+ async function fileExists(path) {
86
+ try {
87
+ await access(path);
88
+ return true;
89
+ }
90
+ catch {
91
+ return false;
92
+ }
93
+ }
94
+ //# sourceMappingURL=agent-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-loader.js","sourceRoot":"","sources":["../src/agent-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AA0BzD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB,EAAE,MAAc;IAC/D,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,MAAM,MAAM,GAAkB,EAAE,CAAC;IAEjC,MAAM,eAAe,GAAG,MAAM,eAAe,CAAC,SAAS,CAAC,CAAC;IAEzD,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,+EAA+E;QAC/E,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC5C,IAAI,MAAM,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,SAAS,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;YACvE,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC5B,CAAC;IAED,sCAAsC;IACtC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAClE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;YAAE,SAAS;QAEnC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACnD,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;YACjC,SAAS;QACX,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAClE,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC;AAMD,KAAK,UAAU,eAAe,CAC5B,IAAY,EACZ,OAAe,EACf,MAAc;IAEd,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxD,MAAM,WAAW,GAAG,MAAM,WAAW,CAAC,YAAY,EAAE,CAAC;QACrD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACxD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,CAAC;IAC5D,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,QAAQ,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClE,MAAM,CAAC,KAAK,CAAC,mBAAmB,EAAE;YAChC,SAAS,EAAE,IAAI;YACf,KAAK,EAAE,QAAQ;SAChB,CAAC,CAAC;QACH,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IACxC,CAAC;AACH,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,IAAY;IACzC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,IAAY;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * cello-daemon — the long-running CELLO daemon process.
4
+ *
5
+ * This binary is spawned by `cello login` as a detached background process.
6
+ * It manages agent identities, IPC connections, and (future) directory signaling.
7
+ *
8
+ * Environment variables:
9
+ * CELLO_DIR Override ~/.cello directory (default: ~/.cello)
10
+ * CELLO_VERSION Version string for lock file (default: from package.json)
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=cello-daemon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cello-daemon.d.ts","sourceRoot":"","sources":["../../src/bin/cello-daemon.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG"}
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * cello-daemon — the long-running CELLO daemon process.
4
+ *
5
+ * This binary is spawned by `cello login` as a detached background process.
6
+ * It manages agent identities, IPC connections, and (future) directory signaling.
7
+ *
8
+ * Environment variables:
9
+ * CELLO_DIR Override ~/.cello directory (default: ~/.cello)
10
+ * CELLO_VERSION Version string for lock file (default: from package.json)
11
+ */
12
+ import { homedir } from "node:os";
13
+ import { join } from "node:path";
14
+ import { startDaemon } from "../daemon.js";
15
+ import { createDirectoryEndpointResolver } from "../directory-bootstrap.js";
16
+ import { FileManifestProvider } from "../file-manifest-provider.js";
17
+ import { FileManifestVersionStore } from "../manifest-version-store-file.js";
18
+ import { RandomizedPollScheduler } from "../manifest-poll-scheduler.js";
19
+ import { ManifestDirectoryChallengeVerifier } from "@cello-protocol/transport";
20
+ const MAX_CONNECTIONS = 16;
21
+ // Composition root: stdout JSON logger
22
+ const logger = {
23
+ debug(event, context) {
24
+ const line = JSON.stringify({ level: "debug", event, ...context, ts: new Date().toISOString() });
25
+ process.stdout.write(line + "\n");
26
+ },
27
+ info(event, context) {
28
+ const line = JSON.stringify({ level: "info", event, ...context, ts: new Date().toISOString() });
29
+ process.stdout.write(line + "\n");
30
+ },
31
+ warn(event, context) {
32
+ const line = JSON.stringify({ level: "warn", event, ...context, ts: new Date().toISOString() });
33
+ process.stdout.write(line + "\n");
34
+ },
35
+ error(event, context) {
36
+ const line = JSON.stringify({ level: "error", event, ...context, ts: new Date().toISOString() });
37
+ process.stdout.write(line + "\n");
38
+ },
39
+ };
40
+ const celloDir = process.env.CELLO_DIR || join(homedir(), ".cello");
41
+ const socketPath = join(celloDir, "daemon.sock");
42
+ const lockFilePath = join(celloDir, "daemon.lock");
43
+ const version = process.env.CELLO_VERSION || "0.0.1";
44
+ /**
45
+ * Build the optional consortium-manifest deps from the environment (M7 J-AUTH).
46
+ *
47
+ * CELLO_CONSORTIUM_MANIFEST absolute path to the manifest JSON
48
+ * CELLO_CONSORTIUM_ROOT_KEYS comma-separated officer root pubkeys (hex)
49
+ * CELLO_CONSORTIUM_THRESHOLD minimum officer signatures (integer)
50
+ *
51
+ * When the manifest path is unset, returns {} — step-6 stays off (M6 compat).
52
+ * One FileManifestProvider instance is shared between startDaemon's loadAndVerify
53
+ * call and the ManifestDirectoryChallengeVerifier, so step-6 reads the same cached,
54
+ * verified manifest.
55
+ */
56
+ function buildManifestDeps(logger) {
57
+ const manifestPath = process.env.CELLO_CONSORTIUM_MANIFEST;
58
+ if (!manifestPath)
59
+ return {};
60
+ const rootKeysRaw = process.env.CELLO_CONSORTIUM_ROOT_KEYS ?? "";
61
+ const manifestRootKeys = rootKeysRaw.split(",").map((k) => k.trim()).filter((k) => k.length > 0);
62
+ const manifestThreshold = Number.parseInt(process.env.CELLO_CONSORTIUM_THRESHOLD ?? "", 10);
63
+ if (manifestRootKeys.length === 0 || Number.isNaN(manifestThreshold)) {
64
+ throw new Error("CELLO_CONSORTIUM_MANIFEST is set but CELLO_CONSORTIUM_ROOT_KEYS / CELLO_CONSORTIUM_THRESHOLD are missing or invalid");
65
+ }
66
+ const manifestProvider = new FileManifestProvider({ path: manifestPath });
67
+ const challengeVerifier = new ManifestDirectoryChallengeVerifier(manifestProvider);
68
+ // Anti-rollback (DOD-AUTH-2 / TUF): persist the last-verified manifest version under
69
+ // ~/.cello so the daemon refuses a manifest whose version regressed across restarts.
70
+ // The daemon (startDaemon) reads getLastSeenVersion() before accepting a manifest and
71
+ // persistVersion() on success; a version < trusted → directory.auth.manifest.version.rollback.
72
+ const manifestVersionStore = new FileManifestVersionStore(join(celloDir, "manifest-version.json"));
73
+ // DOD-AUTH-2: background manifest poll. The directory is re-polled on a randomized
74
+ // 6–12h interval (thundering-herd avoidance) and a newer signed manifest is adopted.
75
+ // The interval is env-injectable so the live binary test can poll sub-second instead
76
+ // of waiting hours; production leaves these unset → the 6–12h default window.
77
+ // Both-or-neither, positive, min <= max — a partial/invalid override fails LOUDLY
78
+ // rather than silently reverting to 6–12h or producing a negative (tight-loop) delay.
79
+ const rawPollMin = process.env.CELLO_MANIFEST_POLL_MIN_MS;
80
+ const rawPollMax = process.env.CELLO_MANIFEST_POLL_MAX_MS;
81
+ let pollOpts;
82
+ if (rawPollMin !== undefined || rawPollMax !== undefined) {
83
+ const minMs = Number.parseInt(rawPollMin ?? "", 10);
84
+ const maxMs = Number.parseInt(rawPollMax ?? "", 10);
85
+ if (Number.isNaN(minMs) || Number.isNaN(maxMs) || minMs <= 0 || maxMs < minMs) {
86
+ throw new Error("CELLO_MANIFEST_POLL_MIN_MS / _MAX_MS must BOTH be set to positive integers with min <= max");
87
+ }
88
+ pollOpts = { minMs, maxMs };
89
+ }
90
+ const manifestPollScheduler = new RandomizedPollScheduler(pollOpts);
91
+ logger.info("daemon.manifest.configured", {
92
+ manifestPath,
93
+ rootKeyCount: manifestRootKeys.length,
94
+ threshold: manifestThreshold,
95
+ pollMinMs: pollOpts?.minMs ?? null,
96
+ pollMaxMs: pollOpts?.maxMs ?? null,
97
+ });
98
+ return { manifestProvider, manifestRootKeys, manifestThreshold, challengeVerifier, manifestVersionStore, manifestPollScheduler };
99
+ }
100
+ async function main() {
101
+ // M7 Keystone (Part 1): give the daemon its door to the directory. The resolver
102
+ // discovers the directory endpoint via GET ${CELLO_DIRECTORY_URL}/bootstrap (the
103
+ // proven M6 path); startDaemon builds the real signalingConnect from it + the
104
+ // primary agent identity.
105
+ const directoryEndpointResolver = createDirectoryEndpointResolver({ logger });
106
+ // M7 J-AUTH (DOD-AUTH-1/2): the consortium-manifest hardening is OPT-IN. When the
107
+ // operator (or the live harness) configures a manifest, the daemon loads + verifies
108
+ // it (threshold officer signatures + validity window) and verifies the directory's
109
+ // step-6 identity proof against the node pubkeys in that manifest. When unset, the
110
+ // daemon runs the M6 backward-compat path: no challengeVerifier → step-6 skipped.
111
+ // Both dialers (keystone + per-agent) receive the verifier via startDaemon.
112
+ const manifest = buildManifestDeps(logger);
113
+ const handle = await startDaemon({
114
+ celloDir,
115
+ socketPath,
116
+ lockFilePath,
117
+ maxConnections: MAX_CONNECTIONS,
118
+ version,
119
+ logger,
120
+ directoryEndpointResolver,
121
+ ...manifest,
122
+ });
123
+ const shutdown = async (signal) => {
124
+ await handle.stop(signal);
125
+ process.exit(0);
126
+ };
127
+ process.on("SIGTERM", () => {
128
+ try {
129
+ shutdown("SIGTERM").catch((err) => {
130
+ logger.error("daemon.shutdown.failed", {
131
+ signal: "SIGTERM",
132
+ error: err instanceof Error ? err.message : String(err),
133
+ });
134
+ process.exit(1);
135
+ });
136
+ }
137
+ catch (err) {
138
+ logger.error("daemon.shutdown.failed", {
139
+ signal: "SIGTERM",
140
+ error: err instanceof Error ? err.message : String(err),
141
+ });
142
+ process.exit(1);
143
+ }
144
+ });
145
+ process.on("SIGINT", () => {
146
+ try {
147
+ shutdown("SIGINT").catch((err) => {
148
+ logger.error("daemon.shutdown.failed", {
149
+ signal: "SIGINT",
150
+ error: err instanceof Error ? err.message : String(err),
151
+ });
152
+ process.exit(1);
153
+ });
154
+ }
155
+ catch (err) {
156
+ logger.error("daemon.shutdown.failed", {
157
+ signal: "SIGINT",
158
+ error: err instanceof Error ? err.message : String(err),
159
+ });
160
+ process.exit(1);
161
+ }
162
+ });
163
+ }
164
+ main().catch((err) => {
165
+ logger.error("daemon.startup.failed", {
166
+ error: err instanceof Error ? err.message : String(err),
167
+ });
168
+ process.exit(1);
169
+ });
170
+ //# sourceMappingURL=cello-daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cello-daemon.js","sourceRoot":"","sources":["../../src/bin/cello-daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * CELLO Daemon — cello-node-transport-dialer.ts (CELLO-M7-TRANSPORT-001)
3
+ *
4
+ * CelloNodeTransportDialer — the REAL TransportDialer (CELLO_ENV dev/staging/
5
+ * production). It performs the low-level dial operations the TransportSelector
6
+ * composes, over a live session CelloNode plus the daemon's relay registry.
7
+ *
8
+ * Review finding C1(b): no real TransportDialer existed — only the
9
+ * LocalTransportSelectorStub returned canned results. This is the production
10
+ * implementation the composition root wires when CELLO_ENV is a production
11
+ * variant.
12
+ *
13
+ * ─── Operations ──────────────────────────────────────────────────────────────
14
+ * dialDirect(peerId, addrs, timeoutMs) — dial the counterparty's direct
15
+ * multiaddrs (TCP/QUIC). Each addr is raced against timeoutMs. Throws if every
16
+ * address fails (the selector catches and falls back to relay — AC-006).
17
+ * dialRelay(relayCircuitAddr, peerId) — dial via the relay circuit address.
18
+ * relayCircuitAddr(assignment) — build the circuit address from the
19
+ * daemon's relay registry (NOT the assignment's address fields — AC-006), so
20
+ * the fallback works regardless of transport_mode.
21
+ * attemptDcutr(peerId) — observe whether libp2p's automatic
22
+ * dcutr hole-punch upgraded the relayed connection to direct within a window.
23
+ * Throws if no direct connection appears (non-fatal — selector logs DEBUG).
24
+ *
25
+ * SI-001: this dialer NEVER inspects transport_mode or infers strategy from
26
+ * address format — it only performs the operation the selector asks for. The
27
+ * authoritative transport_mode decision lives entirely in the TransportSelector.
28
+ */
29
+ import type { SessionAssignment } from "@cello-protocol/protocol-types";
30
+ import type { CelloNode } from "@cello-protocol/transport";
31
+ import type { TransportDialer } from "./transport-selector.js";
32
+ /** A relay endpoint from the daemon's relay registry (populated at directory connect). */
33
+ export interface RelayEndpoint {
34
+ peerId: string;
35
+ multiaddrs: string[];
36
+ }
37
+ export interface CelloNodeTransportDialerOptions {
38
+ /** The session node used to dial the counterparty. */
39
+ node: CelloNode;
40
+ /**
41
+ * The daemon's relay registry — returns the known relay endpoint, or null when
42
+ * no relay is registered. AC-006: the relay address is sourced here, never
43
+ * parsed from the assignment's address fields.
44
+ */
45
+ relayRegistry: () => RelayEndpoint | null;
46
+ /** How long to wait for the dcutr upgrade to surface a direct connection (ms). */
47
+ dcutrObserveTimeoutMs?: number;
48
+ /** Poll interval while observing for the dcutr upgrade (ms). */
49
+ dcutrPollIntervalMs?: number;
50
+ }
51
+ export declare class CelloNodeTransportDialer implements TransportDialer {
52
+ #private;
53
+ constructor(opts: CelloNodeTransportDialerOptions);
54
+ dialDirect(peerId: string | undefined, addrs: string[], timeoutMs: number): Promise<void>;
55
+ dialRelay(relayCircuitAddr: string, _peerId: string | undefined): Promise<void>;
56
+ relayCircuitAddr(assignment: SessionAssignment): string;
57
+ attemptDcutr(peerId: string | undefined): Promise<void>;
58
+ }
59
+ //# sourceMappingURL=cello-node-transport-dialer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cello-node-transport-dialer.d.ts","sourceRoot":"","sources":["../src/cello-node-transport-dialer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,0FAA0F;AAC1F,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,+BAA+B;IAC9C,sDAAsD;IACtD,IAAI,EAAE,SAAS,CAAC;IAChB;;;;OAIG;IACH,aAAa,EAAE,MAAM,aAAa,GAAG,IAAI,CAAC;IAC1C,kFAAkF;IAClF,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gEAAgE;IAChE,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAKD,qBAAa,wBAAyB,YAAW,eAAe;;gBAMlD,IAAI,EAAE,+BAA+B;IAO3C,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBzF,SAAS,CAAC,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrF,gBAAgB,CAAC,UAAU,EAAE,iBAAiB,GAAG,MAAM;IAajD,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CA8B9D"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * CELLO Daemon — cello-node-transport-dialer.ts (CELLO-M7-TRANSPORT-001)
3
+ *
4
+ * CelloNodeTransportDialer — the REAL TransportDialer (CELLO_ENV dev/staging/
5
+ * production). It performs the low-level dial operations the TransportSelector
6
+ * composes, over a live session CelloNode plus the daemon's relay registry.
7
+ *
8
+ * Review finding C1(b): no real TransportDialer existed — only the
9
+ * LocalTransportSelectorStub returned canned results. This is the production
10
+ * implementation the composition root wires when CELLO_ENV is a production
11
+ * variant.
12
+ *
13
+ * ─── Operations ──────────────────────────────────────────────────────────────
14
+ * dialDirect(peerId, addrs, timeoutMs) — dial the counterparty's direct
15
+ * multiaddrs (TCP/QUIC). Each addr is raced against timeoutMs. Throws if every
16
+ * address fails (the selector catches and falls back to relay — AC-006).
17
+ * dialRelay(relayCircuitAddr, peerId) — dial via the relay circuit address.
18
+ * relayCircuitAddr(assignment) — build the circuit address from the
19
+ * daemon's relay registry (NOT the assignment's address fields — AC-006), so
20
+ * the fallback works regardless of transport_mode.
21
+ * attemptDcutr(peerId) — observe whether libp2p's automatic
22
+ * dcutr hole-punch upgraded the relayed connection to direct within a window.
23
+ * Throws if no direct connection appears (non-fatal — selector logs DEBUG).
24
+ *
25
+ * SI-001: this dialer NEVER inspects transport_mode or infers strategy from
26
+ * address format — it only performs the operation the selector asks for. The
27
+ * authoritative transport_mode decision lives entirely in the TransportSelector.
28
+ */
29
+ const DEFAULT_DCUTR_OBSERVE_TIMEOUT_MS = 10_000;
30
+ const DEFAULT_DCUTR_POLL_INTERVAL_MS = 250;
31
+ export class CelloNodeTransportDialer {
32
+ #node;
33
+ #relayRegistry;
34
+ #dcutrTimeoutMs;
35
+ #dcutrPollMs;
36
+ constructor(opts) {
37
+ this.#node = opts.node;
38
+ this.#relayRegistry = opts.relayRegistry;
39
+ this.#dcutrTimeoutMs = opts.dcutrObserveTimeoutMs ?? DEFAULT_DCUTR_OBSERVE_TIMEOUT_MS;
40
+ this.#dcutrPollMs = opts.dcutrPollIntervalMs ?? DEFAULT_DCUTR_POLL_INTERVAL_MS;
41
+ }
42
+ async dialDirect(peerId, addrs, timeoutMs) {
43
+ if (addrs.length === 0) {
44
+ throw new Error("no direct addresses to dial");
45
+ }
46
+ let lastError;
47
+ for (const addr of addrs) {
48
+ try {
49
+ await this.#withTimeout(this.#node.dial(addr), timeoutMs, `direct dial to ${addr}`);
50
+ return; // first successful dial wins
51
+ }
52
+ catch (err) {
53
+ lastError = err;
54
+ }
55
+ }
56
+ throw lastError instanceof Error
57
+ ? lastError
58
+ : new Error(`direct dial failed for all ${addrs.length} address(es): ${String(lastError)}`);
59
+ }
60
+ async dialRelay(relayCircuitAddr, _peerId) {
61
+ await this.#node.dial(relayCircuitAddr);
62
+ }
63
+ relayCircuitAddr(assignment) {
64
+ const relay = this.#relayRegistry();
65
+ if (!relay || relay.multiaddrs.length === 0) {
66
+ throw new Error("no relay endpoint registered — cannot build relay circuit address");
67
+ }
68
+ const counterparty = assignment.counterparty_session_peer_id;
69
+ if (!counterparty) {
70
+ throw new Error("assignment has no counterparty_session_peer_id for the relay circuit");
71
+ }
72
+ // /<relay-multiaddr>/p2p/<relay-peer-id>/p2p-circuit/p2p/<counterparty-session-peer-id>
73
+ return `${relay.multiaddrs[0]}/p2p/${relay.peerId}/p2p-circuit/p2p/${counterparty}`;
74
+ }
75
+ async attemptDcutr(peerId) {
76
+ if (!peerId) {
77
+ throw new Error("no counterparty peer id to observe dcutr upgrade for");
78
+ }
79
+ // libp2p's dcutr service runs automatically once a relayed connection exists.
80
+ // We observe whether it produced a direct (non-circuit) connection within the
81
+ // window. This is best-effort — failure is non-fatal (SI-003).
82
+ const deadline = Date.now() + this.#dcutrTimeoutMs;
83
+ for (;;) {
84
+ if (this.#node.hasDirectConnectionTo(peerId))
85
+ return;
86
+ if (Date.now() >= deadline) {
87
+ throw new Error(`dcutr did not establish a direct connection to ${peerId} within ${this.#dcutrTimeoutMs}ms`);
88
+ }
89
+ await new Promise((r) => setTimeout(r, this.#dcutrPollMs));
90
+ }
91
+ }
92
+ async #withTimeout(p, timeoutMs, label) {
93
+ let timer;
94
+ try {
95
+ return await Promise.race([
96
+ p,
97
+ new Promise((_resolve, reject) => {
98
+ timer = setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs);
99
+ }),
100
+ ]);
101
+ }
102
+ finally {
103
+ if (timer)
104
+ clearTimeout(timer);
105
+ }
106
+ }
107
+ }
108
+ //# sourceMappingURL=cello-node-transport-dialer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cello-node-transport-dialer.js","sourceRoot":"","sources":["../src/cello-node-transport-dialer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * M7-MANIFEST-002 — Challenge verifier re-exports.
3
+ *
4
+ * The production verifier (ManifestDirectoryChallengeVerifier) and test stub
5
+ * (TestDirectoryChallengeVerifier) are both defined in @cello-protocol/transport
6
+ * to avoid creating a circular dependency between daemon and transport.
7
+ *
8
+ * This module re-exports them for consumers that import from the daemon package.
9
+ */
10
+ export { ManifestDirectoryChallengeVerifier, TestDirectoryChallengeVerifier, } from "@cello-protocol/transport";
11
+ export type { IDirectoryChallengeVerifier } from "@cello-protocol/transport";
12
+ //# sourceMappingURL=challenge-verifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"challenge-verifier.d.ts","sourceRoot":"","sources":["../src/challenge-verifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,kCAAkC,EAClC,8BAA8B,GAC/B,MAAM,2BAA2B,CAAC;AACnC,YAAY,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * M7-MANIFEST-002 — Challenge verifier re-exports.
3
+ *
4
+ * The production verifier (ManifestDirectoryChallengeVerifier) and test stub
5
+ * (TestDirectoryChallengeVerifier) are both defined in @cello-protocol/transport
6
+ * to avoid creating a circular dependency between daemon and transport.
7
+ *
8
+ * This module re-exports them for consumers that import from the daemon package.
9
+ */
10
+ export { ManifestDirectoryChallengeVerifier, TestDirectoryChallengeVerifier, } from "@cello-protocol/transport";
11
+ //# sourceMappingURL=challenge-verifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"challenge-verifier.js","sourceRoot":"","sources":["../src/challenge-verifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,kCAAkC,EAClC,8BAA8B,GAC/B,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Connect-or-start logic for the CELLO daemon.
3
+ *
4
+ * Pseudocode:
5
+ * 1. connectOrStart(celloDir, logger):
6
+ * a. Read lock file at ~/.cello/daemon.lock
7
+ * b. If lock exists:
8
+ * - Check if PID is alive (signal 0)
9
+ * - If alive, attempt to connect to socket
10
+ * - If connection succeeds, return existing daemon client
11
+ * - If connection fails (socket gone), remove stale lock and start fresh
12
+ * c. If lock doesn't exist or is stale:
13
+ * - Remove stale lock (log daemon_lock_stale_removed)
14
+ * - Spawn daemon as detached child process
15
+ * - Wait for daemon.started event (read from child stdout)
16
+ * - Return client connected to new daemon
17
+ */
18
+ import { type IpcClient } from "./ipc-client.js";
19
+ import type { Logger } from "./types.js";
20
+ export interface ConnectResult {
21
+ client: IpcClient;
22
+ alreadyRunning: boolean;
23
+ }
24
+ export declare function connectOrStart(celloDir: string, logger: Logger, daemonBin: string): Promise<ConnectResult>;
25
+ //# sourceMappingURL=connect-or-start.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect-or-start.d.ts","sourceRoot":"","sources":["../src/connect-or-start.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC,CAiCxB"}