@cello-protocol/daemon 0.0.49 → 0.0.51

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 (41) hide show
  1. package/dist/bin/cello-daemon.js +13 -0
  2. package/dist/bin/cello-daemon.js.map +1 -1
  3. package/dist/connect-or-start.d.ts +11 -11
  4. package/dist/connect-or-start.d.ts.map +1 -1
  5. package/dist/connect-or-start.js +97 -39
  6. package/dist/connect-or-start.js.map +1 -1
  7. package/dist/daemon.d.ts +11 -0
  8. package/dist/daemon.d.ts.map +1 -1
  9. package/dist/daemon.js +127 -26
  10. package/dist/daemon.js.map +1 -1
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +2 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/ipc-server.d.ts.map +1 -1
  16. package/dist/ipc-server.js +79 -13
  17. package/dist/ipc-server.js.map +1 -1
  18. package/dist/lock-file.d.ts +12 -0
  19. package/dist/lock-file.d.ts.map +1 -1
  20. package/dist/lock-file.js +36 -1
  21. package/dist/lock-file.js.map +1 -1
  22. package/dist/park-envelope.d.ts +93 -0
  23. package/dist/park-envelope.d.ts.map +1 -0
  24. package/dist/park-envelope.js +172 -0
  25. package/dist/park-envelope.js.map +1 -0
  26. package/dist/retry-queue.d.ts.map +1 -1
  27. package/dist/retry-queue.js +13 -1
  28. package/dist/retry-queue.js.map +1 -1
  29. package/dist/session-node-manager.d.ts +31 -13
  30. package/dist/session-node-manager.d.ts.map +1 -1
  31. package/dist/session-node-manager.js +104 -26
  32. package/dist/session-node-manager.js.map +1 -1
  33. package/dist/singleton-lock.d.ts +85 -0
  34. package/dist/singleton-lock.d.ts.map +1 -0
  35. package/dist/singleton-lock.js +208 -0
  36. package/dist/singleton-lock.js.map +1 -0
  37. package/dist/sqlcipher-db.d.ts +28 -0
  38. package/dist/sqlcipher-db.d.ts.map +1 -1
  39. package/dist/sqlcipher-db.js +6 -1
  40. package/dist/sqlcipher-db.js.map +1 -1
  41. package/package.json +4 -4
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export type { Logger, LockFileContent, IpcRequest, IpcResponse, IpcResponseOk, IpcResponseError, IpcNotification, IpcFrame, AgentState, AgentInfo, ConnectionStatus, ConnectionInfo, DirectorySignalingState, DaemonStatusResponse, DaemonConfig, IManifestProvider, IManifestVersionStore, IManifestPollScheduler, IDirectoryChallengeVerifier, } from "./types.js";
2
2
  export { ErrorCodes } from "./types.js";
3
3
  export { startDaemon, type DaemonHandle } from "./daemon.js";
4
- export { readLock, acquireLock, removeLock, isProcessAlive } from "./lock-file.js";
4
+ export { readLock, acquireLock, removeLock, removeLockIfOwned, isProcessAlive } from "./lock-file.js";
5
+ export { acquireSingletonLock, probeSingletonLock, DaemonAlreadyRunningError, SINGLETON_LOCK_FILENAME, type SingletonLock, } from "./singleton-lock.js";
5
6
  export { loadAgents, type LoadedAgent, type FailedAgent, type AgentLoadResult } from "./agent-loader.js";
6
7
  export { createIpcServer, type IpcServer, type IpcHandler, type IpcServerConfig, type IpcDisconnectHandler } from "./ipc-server.js";
7
8
  export { connectToDaemon, type IpcClient, IpcError } from "./ipc-client.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,MAAM,EACN,eAAe,EACf,UAAU,EACV,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,QAAQ,EACR,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,cAAc,EACd,uBAAuB,EACvB,oBAAoB,EACpB,YAAY,EACZ,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,2BAA2B,GAC5B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzG,OAAO,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACpI,OAAO,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACvH,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGpE,OAAO,EACL,iBAAiB,EACjB,0BAA0B,EAC1B,eAAe,EACf,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,kCAAkC,EAAE,8BAA8B,EAAE,MAAM,yBAAyB,CAAC;AAK7G,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,aAAa,GACnB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,MAAM,EACN,eAAe,EACf,UAAU,EACV,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,QAAQ,EACR,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,cAAc,EACd,uBAAuB,EACvB,oBAAoB,EACpB,YAAY,EACZ,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,2BAA2B,GAC5B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACtG,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,yBAAyB,EACzB,uBAAuB,EACvB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzG,OAAO,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACpI,OAAO,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACvH,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGpE,OAAO,EACL,iBAAiB,EACjB,0BAA0B,EAC1B,eAAe,EACf,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,kCAAkC,EAAE,8BAA8B,EAAE,MAAM,yBAAyB,CAAC;AAK7G,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,aAAa,GACnB,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export { ErrorCodes } from "./types.js";
2
2
  export { startDaemon } from "./daemon.js";
3
- export { readLock, acquireLock, removeLock, isProcessAlive } from "./lock-file.js";
3
+ export { readLock, acquireLock, removeLock, removeLockIfOwned, isProcessAlive } from "./lock-file.js";
4
+ export { acquireSingletonLock, probeSingletonLock, DaemonAlreadyRunningError, SINGLETON_LOCK_FILENAME, } from "./singleton-lock.js";
4
5
  export { loadAgents } from "./agent-loader.js";
5
6
  export { createIpcServer } from "./ipc-server.js";
6
7
  export { connectToDaemon, IpcError } from "./ipc-client.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,WAAW,EAAqB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACnF,OAAO,EAAE,UAAU,EAA4D,MAAM,mBAAmB,CAAC;AACzG,OAAO,EAAE,eAAe,EAAoF,MAAM,iBAAiB,CAAC;AACpI,OAAO,EAAE,eAAe,EAAkB,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAsB,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,UAAU,EAA0D,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACvH,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEpE,qFAAqF;AACrF,OAAO,EACL,iBAAiB,EACjB,0BAA0B,EAC1B,eAAe,EACf,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AASjC,+DAA+D;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,kCAAkC,EAAE,8BAA8B,EAAE,MAAM,yBAAyB,CAAC;AAE7G,6FAA6F;AAC7F,+FAA+F;AAC/F,4EAA4E;AAC5E,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,gBAAgB,GAGjB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,WAAW,EAAqB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACtG,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,yBAAyB,EACzB,uBAAuB,GAExB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,UAAU,EAA4D,MAAM,mBAAmB,CAAC;AACzG,OAAO,EAAE,eAAe,EAAoF,MAAM,iBAAiB,CAAC;AACpI,OAAO,EAAE,eAAe,EAAkB,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAsB,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,UAAU,EAA0D,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACvH,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEpE,qFAAqF;AACrF,OAAO,EACL,iBAAiB,EACjB,0BAA0B,EAC1B,eAAe,EACf,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AASjC,+DAA+D;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,kCAAkC,EAAE,8BAA8B,EAAE,MAAM,yBAAyB,CAAC;AAE7G,6FAA6F;AAC7F,+FAA+F;AAC/F,4EAA4E;AAC5E,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,gBAAgB,GAGjB,MAAM,iBAAiB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ipc-server.d.ts","sourceRoot":"","sources":["../src/ipc-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAKH,OAAO,KAAK,EAAE,MAAM,EAA2B,eAAe,EAAE,MAAM,YAAY,CAAC;AAEnF,MAAM,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAAE,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEjH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;AAElE,MAAM,WAAW,SAAS;IACxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,kBAAkB,IAAI,MAAM,CAAC;IAC7B,YAAY,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAClD,qFAAqF;IACrF,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,GAAG,OAAO,CAAC;IAC/E,wCAAwC;IACxC,gBAAgB,IAAI,MAAM,EAAE,CAAC;CAC9B;AASD,wBAAgB,eAAe,CAC7B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GAChC,SAAS,CAyPX"}
1
+ {"version":3,"file":"ipc-server.d.ts","sourceRoot":"","sources":["../src/ipc-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAKH,OAAO,KAAK,EAAE,MAAM,EAA2B,eAAe,EAAE,MAAM,YAAY,CAAC;AAEnF,MAAM,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAAE,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEjH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;AAElE,MAAM,WAAW,SAAS;IACxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,kBAAkB,IAAI,MAAM,CAAC;IAC7B,YAAY,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAClD,qFAAqF;IACrF,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,GAAG,OAAO,CAAC;IAC/E,wCAAwC;IACxC,gBAAgB,IAAI,MAAM,EAAE,CAAC;CAC9B;AASD,wBAAgB,eAAe,CAC7B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GAChC,SAAS,CA6TX"}
@@ -28,13 +28,42 @@
28
28
  * e. Unlink socket file
29
29
  */
30
30
  import { createServer } from "node:net";
31
- import { chmod, unlink } from "node:fs/promises";
31
+ import { chmod, stat, unlink } from "node:fs/promises";
32
32
  import { randomUUID } from "node:crypto";
33
33
  export function createIpcServer(config, handlers) {
34
34
  const { socketPath, maxConnections, logger } = config;
35
35
  let server = null;
36
36
  const connections = new Map();
37
37
  let stopping = false;
38
+ // DOD-DAEMON-CLEANUP-1 (AC2): the identity — not the path — of the socket THIS server created.
39
+ // dev+ino is the only thing that survives another daemon rebinding the same path.
40
+ let createdSocket = null;
41
+ /**
42
+ * What is sitting at socketPath right now?
43
+ * "ours" — the socket this server created; we may remove it.
44
+ * "gone" — nothing there; nothing to do.
45
+ * "foreign" — a DIFFERENT socket. Another daemon rebound the path. Hands off.
46
+ *
47
+ * The distinction is by inode, not by path. A path tells you a file exists; only the inode tells
48
+ * you it is the same file you made.
49
+ */
50
+ async function socketOwnership() {
51
+ let current;
52
+ try {
53
+ current = await stat(socketPath);
54
+ }
55
+ catch {
56
+ // Nothing at the path (ENOENT is the only realistic case) — there is nothing to own.
57
+ return "gone";
58
+ }
59
+ if (createdSocket === null) {
60
+ // Unreachable: stop() returns early unless the server is listening, and a listening server has
61
+ // been through start(), which sets this or throws. Say so rather than inventing an answer —
62
+ // guessing here is what the whole unit is about.
63
+ throw new Error("ipc server: socket identity unknown — stop() ran without a completed start()");
64
+ }
65
+ return current.dev === createdSocket.dev && current.ino === createdSocket.ino ? "ours" : "foreign";
66
+ }
38
67
  let disconnectHandler = null;
39
68
  function handleConnection(socket) {
40
69
  if (stopping) {
@@ -186,6 +215,18 @@ export function createIpcServer(config, handlers) {
186
215
  });
187
216
  // SI-001: Set socket permissions to owner-only
188
217
  await chmod(socketPath, 0o600);
218
+ // DOD-DAEMON-CLEANUP-1 (AC2): remember WHICH socket we created, by identity — not merely that
219
+ // a file exists at this path. If another daemon later rebinds the path, its listen() replaces
220
+ // the file with a different inode, and stop() must be able to tell the two apart so it does not
221
+ // unlink a socket it does not own.
222
+ //
223
+ // No fallback here, deliberately. Swallowing this stat and leaving the identity unknown would
224
+ // make stop() treat our OWN socket as foreign: it would then never close the server, so the
225
+ // daemon would go on accepting IPC connections into a torn-down handler stack after stop()
226
+ // returned, and would leave its socket behind. A daemon that cannot identify the socket it just
227
+ // bound has no business coming up — and startup failure is already handled cleanly.
228
+ const created = await stat(socketPath);
229
+ createdSocket = { dev: created.dev, ino: created.ino };
189
230
  },
190
231
  async stop() {
191
232
  stopping = true;
@@ -221,19 +262,44 @@ export function createIpcServer(config, handlers) {
221
262
  // Connection may already be closed
222
263
  }
223
264
  }
224
- // Close server and remove socket
225
- await new Promise((resolve) => {
226
- server.close(() => resolve());
227
- });
228
- try {
229
- await unlink(socketPath);
265
+ // DOD-DAEMON-CLEANUP-1 (AC2): decide ownership BEFORE closing, because close() is itself a
266
+ // blind unlink.
267
+ //
268
+ // Node's server.close() removes the unix socket path it bound — by PATH, with no check on what
269
+ // is actually there now. If another daemon has since rebound this path, closing our server
270
+ // deletes THEIR socket, and they are left alive, serving, and unreachable. Guarding our own
271
+ // explicit unlink is not enough; the close would already have done the damage.
272
+ const ownership = await socketOwnership();
273
+ if (ownership === "foreign") {
274
+ // We must not close: that would unlink the healthy daemon's socket out from under it. Drop
275
+ // the handle from the event loop instead and let process exit reclaim the fd. The listening
276
+ // socket is already inert — its path was taken from it, so nothing can reach it. Leaking an
277
+ // unreachable fd for the last moments of a dying process is a trade we make happily against
278
+ // disarming a live peer.
279
+ logger.info("daemon.ipc.socket.not_ours", { socketPath, ourIno: createdSocket?.ino ?? null });
280
+ server.unref();
230
281
  }
231
- catch (err) {
232
- if (err.code !== "ENOENT") {
233
- logger.warn("daemon.ipc.socket.unlink.failed", {
234
- socketPath,
235
- error: err instanceof Error ? err.message : String(err),
236
- });
282
+ else {
283
+ await new Promise((resolve) => {
284
+ server.close(() => resolve());
285
+ });
286
+ // close() normally unlinks the path for us. Re-check rather than assume: only remove the file
287
+ // if it is STILL the socket we created. Between the close and here, the singleton lock is
288
+ // still held (the daemon releases it after this returns), so no successor can have bound the
289
+ // path — but the cost of re-checking is one stat, and the cost of being wrong is the bug this
290
+ // whole unit exists to kill.
291
+ if (await socketOwnership() === "ours") {
292
+ try {
293
+ await unlink(socketPath);
294
+ }
295
+ catch (err) {
296
+ if (err.code !== "ENOENT") {
297
+ logger.warn("daemon.ipc.socket.unlink.failed", {
298
+ socketPath,
299
+ error: err instanceof Error ? err.message : String(err),
300
+ });
301
+ }
302
+ }
237
303
  }
238
304
  }
239
305
  server = null;
@@ -1 +1 @@
1
- {"version":3,"file":"ipc-server.js","sourceRoot":"","sources":["../src/ipc-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,YAAY,EAA4B,MAAM,UAAU,CAAC;AAClE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA+BzC,MAAM,UAAU,eAAe,CAC7B,MAAuB,EACvB,QAAiC;IAEjC,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IACtD,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,MAAM,WAAW,GAAG,IAAI,GAAG,EAA4B,CAAC;IACxD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,iBAAiB,GAAgC,IAAI,CAAC;IAE1D,SAAS,gBAAgB,CAAC,MAAc;QACtC,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QAED,IAAI,WAAW,CAAC,IAAI,IAAI,cAAc,EAAE,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,qCAAqC,EAAE;gBACjD,YAAY,EAAE,WAAW,CAAC,IAAI;gBAC9B,QAAQ,EAAE,cAAc;aACzB,CAAC,CAAC;YACH,wEAAwE;YACxE,yEAAyE;YACzE,+CAA+C;YAC/C,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QAED,MAAM,YAAY,GAAG,UAAU,EAAE,CAAC;QAClC,MAAM,IAAI,GAAqB,EAAE,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;QACpG,WAAW,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QAEpC,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAErD,+EAA+E;QAC/E,2EAA2E;QAC3E,iFAAiF;QACjF,2EAA2E;QAC3E,kFAAkF;QAClF,+EAA+E;QAC/E,mFAAmF;QACnF,kFAAkF;QAClF,gFAAgF;QAChF,iFAAiF;QACjF,oDAAoD;QACpD,MAAM,eAAe,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,oDAAoD;QAC7F,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAClC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAClC,IAAI,MAAM,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;gBACpC,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;gBACvF,MAAM,GAAG,EAAE,CAAC;gBACZ,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;gBAClC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACjB,OAAO;YACT,CAAC;YACD,IAAI,UAAkB,CAAC;YACvB,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;gBAClD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;gBACzC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;gBACtC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;oBAAE,SAAS;gBACvC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,WAAW,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YACjC,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,IAAI,eAAe,CAAC;YACtD,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC;YACjE,IAAI,iBAAiB;gBAAE,iBAAiB,CAAC,YAAY,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;QAEH,wFAAwF;QACxF,uFAAuF;QACvF,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;YAChC,IAAI,CAAC,cAAc,GAAG,GAAG,CAAC,OAAO,CAAC;QACpC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,SAAS,aAAa,CAAC,IAAsB,EAAE,IAAY;QACzD,IAAI,OAAmB,CAAC;QACxB,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,wDAAwD;YACxD,mEAAmE;YACnE,oEAAoE;YACpE,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YACjE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACnC,gEAAgE;YAChE,iEAAiE;YACjE,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;YACvH,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,SAAS,GAAgB;gBAC7B,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,KAAK,EAAE;oBACL,IAAI,EAAE,kBAAkB;oBACxB,OAAO,EAAE,mBAAmB,OAAO,CAAC,MAAM,EAAE;oBAC5C,QAAQ,EAAE,uBAAuB,OAAO,CAAC,MAAM,kHAAkH;iBAClK;aACF,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QAED,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;aAC9C,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;YACf,IAAI,CAAC;gBACH,MAAM,IAAI,GAAgB,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC;gBACrD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;YACjD,CAAC;YAAC,MAAM,CAAC;gBACP,iDAAiD;YACnD,CAAC;QACH,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;YACtB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAgB;oBACxB,EAAE,EAAE,OAAO,CAAC,EAAE;oBACd,KAAK,EAAE;wBACL,IAAI,EAAE,gBAAgB;wBACtB,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;wBACzD,QAAQ,EAAE,8DAA8D;qBACzE;iBACF,CAAC;gBACF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;YACjD,CAAC;YAAC,MAAM,CAAC;gBACP,8CAA8C;YAChD,CAAC;QACH,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE;YACZ,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,CAAC,CAAC,CAAC;IACP,CAAC;IAED,OAAO;QACL,KAAK,CAAC,KAAK;YACT,wCAAwC;YACxC,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;YAC3B,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACrD,MAAM,GAAG,CAAC;gBACZ,CAAC;YACH,CAAC;YAED,MAAM,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAC;YAExC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC1C,MAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;oBACjC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACd,CAAC,CAAC,CAAC;gBACH,MAAO,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,EAAE;oBAC9B,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,+CAA+C;YAC/C,MAAM,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACjC,CAAC;QAED,KAAK,CAAC,IAAI;YACR,QAAQ,GAAG,IAAI,CAAC;YAChB,IAAI,CAAC,MAAM;gBAAE,OAAO;YAEpB,0DAA0D;YAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;YACnC,IAAI,aAAa,GAAG,CAAC,CAAC;YACtB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;gBAC7B,aAAa,GAAG,CAAC,CAAC;gBAClB,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;oBACxC,aAAa,IAAI,IAAI,CAAC,aAAa,CAAC;gBACtC,CAAC;gBACD,IAAI,aAAa,KAAK,CAAC;oBAAE,MAAM;gBAC/B,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC9C,CAAC;YAED,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,CAAC,IAAI,CAAC,6BAA6B,EAAE,EAAE,iBAAiB,EAAE,aAAa,EAAE,CAAC,CAAC;YACnF,CAAC;YAED,uDAAuD;YACvD,oEAAoE;YACpE,qDAAqD;YACrD,MAAM,oBAAoB,GAAoB,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;YAC3E,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAAC;YAClE,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;gBACxC,IAAI,CAAC;oBACH,IAAI,CAAC,cAAc,GAAG,iBAAiB,CAAC;oBACxC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;oBACjC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;gBACpB,CAAC;gBAAC,MAAM,CAAC;oBACP,mCAAmC;gBACrC,CAAC;YACH,CAAC;YAED,iCAAiC;YACjC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBAClC,MAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YACjC,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;YAC3B,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACrD,MAAM,CAAC,IAAI,CAAC,iCAAiC,EAAE;wBAC7C,UAAU;wBACV,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;qBACxD,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;QAED,kBAAkB;YAChB,OAAO,WAAW,CAAC,IAAI,CAAC;QAC1B,CAAC;QAED,YAAY,CAAC,OAA6B;YACxC,iBAAiB,GAAG,OAAO,CAAC;QAC9B,CAAC;QAED,gBAAgB,CAAC,YAAoB,EAAE,YAA6B;YAClE,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAC3C,IAAI,CAAC,IAAI;gBAAE,OAAO,KAAK,CAAC;YACxB,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC;gBAClD,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAClC,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,MAAM,CAAC,KAAK,CAAC,sCAAsC,EAAE;oBACnD,YAAY;oBACZ,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;iBACxD,CAAC,CAAC;gBACH,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAED,gBAAgB;YACd,OAAO,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;QACxC,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"ipc-server.js","sourceRoot":"","sources":["../src/ipc-server.ts"],"names":[],"mappings":"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"}
@@ -23,5 +23,17 @@ import type { LockFileContent, Logger } from "./types.js";
23
23
  export declare function readLock(lockPath: string): Promise<LockFileContent | null>;
24
24
  export declare function acquireLock(lockPath: string, content: LockFileContent): Promise<void>;
25
25
  export declare function removeLock(lockPath: string, logger: Logger): Promise<void>;
26
+ /**
27
+ * DOD-DAEMON-CLEANUP-1 (AC1) — remove the lock ONLY if it is still ours.
28
+ *
29
+ * An exiting daemon must not disarm a daemon it does not own. By the time we shut down, a DIFFERENT
30
+ * daemon may have written this lock; deleting it would leave that healthy daemon unreachable
31
+ * (`cello logout` reports "No daemon running") and the next `cello login` would spawn a third beside
32
+ * it. That is the cascade — the obvious recovery action, killing the orphan, propagates the bug.
33
+ *
34
+ * `removeLock` (unconditional) is still correct for the CLI's stale-lock cleanup, where the lock
35
+ * belongs to a process that is provably gone. It is NOT correct on the way out of a live daemon.
36
+ */
37
+ export declare function removeLockIfOwned(lockPath: string, ownPid: number, logger: Logger): Promise<void>;
26
38
  export declare function isProcessAlive(pid: number): boolean;
27
39
  //# sourceMappingURL=lock-file.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lock-file.d.ts","sourceRoot":"","sources":["../src/lock-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAE1D,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAwBhF;AAED,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAU3F;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhF;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOnD"}
1
+ {"version":3,"file":"lock-file.d.ts","sourceRoot":"","sources":["../src/lock-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAE1D,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAwBhF;AAED,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAU3F;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhF;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOnD"}
package/dist/lock-file.js CHANGED
@@ -19,7 +19,7 @@
19
19
  * 4. removeLock(path):
20
20
  * - Unlink the file, ignoring ENOENT
21
21
  */
22
- import { readFile, writeFile, rename, unlink } from "node:fs/promises";
22
+ import { readFile, writeFile, rename, stat, unlink } from "node:fs/promises";
23
23
  export async function readLock(lockPath) {
24
24
  let raw;
25
25
  try {
@@ -72,6 +72,41 @@ export async function removeLock(lockPath, logger) {
72
72
  }
73
73
  }
74
74
  }
75
+ /**
76
+ * DOD-DAEMON-CLEANUP-1 (AC1) — remove the lock ONLY if it is still ours.
77
+ *
78
+ * An exiting daemon must not disarm a daemon it does not own. By the time we shut down, a DIFFERENT
79
+ * daemon may have written this lock; deleting it would leave that healthy daemon unreachable
80
+ * (`cello logout` reports "No daemon running") and the next `cello login` would spawn a third beside
81
+ * it. That is the cascade — the obvious recovery action, killing the orphan, propagates the bug.
82
+ *
83
+ * `removeLock` (unconditional) is still correct for the CLI's stale-lock cleanup, where the lock
84
+ * belongs to a process that is provably gone. It is NOT correct on the way out of a live daemon.
85
+ */
86
+ export async function removeLockIfOwned(lockPath, ownPid, logger) {
87
+ const lock = await readLock(lockPath);
88
+ if (lock === null) {
89
+ // readLock returns null for BOTH "absent" and "unparseable" — so this is the one place that
90
+ // cannot tell "nothing to do" from "something is wrong". Distinguish them: a lock file that
91
+ // exists but cannot be read (truncated by a full disk, a partial write) is left alone, because we
92
+ // cannot prove it is ours — but it is said out loud rather than passed over in silence.
93
+ try {
94
+ await stat(lockPath);
95
+ logger.warn("daemon.lock.unreadable", { ownPid, lockPath });
96
+ }
97
+ catch {
98
+ // Genuinely absent. Nothing to remove, nothing to report.
99
+ }
100
+ return;
101
+ }
102
+ if (lock.pid !== ownPid) {
103
+ // SI: a daemon that declines to remove a lock says so. Silence here is what made the live
104
+ // incident so hard to see.
105
+ logger.info("daemon.lock.not_ours", { ownPid, lockPid: lock.pid, lockPath });
106
+ return;
107
+ }
108
+ await removeLock(lockPath, logger);
109
+ }
75
110
  export function isProcessAlive(pid) {
76
111
  try {
77
112
  process.kill(pid, 0);
@@ -1 +1 @@
1
- {"version":3,"file":"lock-file.js","sourceRoot":"","sources":["../src/lock-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAGvE,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,QAAgB;IAC7C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QAC1D,IACE,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ;YAC9B,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ;YACrC,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,EAClC,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;IACrF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,QAAgB,EAAE,OAAwB;IAC1E,MAAM,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACrD,MAAM,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAI,CAAC;YAAC,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,yBAAyB,CAAC,CAAC;QAClE,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB,EAAE,MAAc;IAC/D,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrD,MAAM,CAAC,IAAI,CAAC,2BAA2B,EAAE;gBACvC,QAAQ;gBACR,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aACxD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"lock-file.js","sourceRoot":"","sources":["../src/lock-file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ import { type KeyProvider } from "@cello-protocol/crypto";
2
+ /**
3
+ * Compare an identity key (raw bytes) against a stored hex pubkey — on BYTES, never on the hex
4
+ * strings.
5
+ *
6
+ * Review M1: `sessions.counterparty_pubkey` is persisted VERBATIM from the IPC `target_pubkey`
7
+ * string with no normalization, so an uppercase or mixed-case pubkey is a perfectly functional
8
+ * session (Buffer.from(hex) is case-insensitive, so dialing, sealing and the direct content path all
9
+ * work) — but a `toString("hex") !== stored` comparison would then refuse EVERY parked recovery for
10
+ * that session as `signer_not_counterparty`, i.e. permanent store-and-forward mail loss reported as
11
+ * an attack. Comparing bytes makes the check independent of how the hex was cased on the way in.
12
+ *
13
+ * Length-guarded because timingSafeEqual throws on a length mismatch, and the input is
14
+ * attacker-controlled (a forged envelope can carry a short/long pubkey).
15
+ */
16
+ export declare function pubkeyMatchesHex(pubkey: Uint8Array, storedHex: string | undefined): boolean;
17
+ /** Current envelope version. v1 (unsigned) is decodable but NEVER acceptable — see authenticate(). */
18
+ export declare const PARK_ENVELOPE_VERSION = 2;
19
+ export interface ParkEnvelope {
20
+ /** 1 = legacy/unsigned (bare content or the pre-SEC-1 shape). 2 = signed. */
21
+ version: number;
22
+ content: Uint8Array;
23
+ /** DOD-MSG-4 ordering record — optional; absent on the crash-backstop path. */
24
+ structure1Cbor?: Uint8Array;
25
+ structure2Cbor?: Uint8Array;
26
+ /** SEC-1 — the sender's identity key. Present only on v2. */
27
+ senderPubkey?: Uint8Array;
28
+ /** SEC-1 — Ed25519 over buildParkContentTbs(...). Present only on v2. */
29
+ parkSig?: Uint8Array;
30
+ }
31
+ export type ParkAuthFailure = "unsigned_envelope" | "bad_signature" | "signer_not_counterparty" | "counterparty_unknown";
32
+ export type ParkAuthVerdict = {
33
+ ok: true;
34
+ } | {
35
+ ok: false;
36
+ reason: ParkAuthFailure;
37
+ };
38
+ /**
39
+ * SEC-1: encode a SIGNED park envelope. `senderPubkey` and `parkSig` are REQUIRED — the type system
40
+ * is the enforcement point, so no call site can construct an unsigned envelope by omission (the
41
+ * exact drift that made this a downgrade attack: verification existed, but was opt-in).
42
+ */
43
+ export declare function encodeParkEnvelope(args: {
44
+ content: Uint8Array;
45
+ senderPubkey: Uint8Array;
46
+ parkSig: Uint8Array;
47
+ structure1Cbor?: Uint8Array;
48
+ structure2Cbor?: Uint8Array;
49
+ }): Uint8Array;
50
+ /**
51
+ * SEC-1 — THE ONLY PRODUCER. Sign the entry as the sending agent, wrap it in a v2 envelope, and seal
52
+ * it to the recipient. Both park sites (the live hook and the crash backstop) call THIS, so the
53
+ * to-be-signed arguments are constructed in exactly one place.
54
+ *
55
+ * Review (hollow-test finding): while the two call sites each built their own signature, a producer
56
+ * that signed the WRONG TBS arguments — e.g. its own pubkey where the recipient's belongs — would
57
+ * emit envelopes no recipient could ever accept, and every consumer-side test would still pass. The
58
+ * bug would only surface as mail that silently never arrives. One producer + a round-trip test
59
+ * against the real consumer removes that whole class: if this function signs the wrong statement,
60
+ * `authenticateParkedEntry` rejects its output and the test goes red.
61
+ */
62
+ export declare function sealParkEnvelope(args: {
63
+ signer: KeyProvider;
64
+ sessionIdHex: string;
65
+ recipientPubkey: Uint8Array;
66
+ contentHash: Uint8Array;
67
+ content: Uint8Array;
68
+ structure1Cbor?: Uint8Array;
69
+ structure2Cbor?: Uint8Array;
70
+ }): Promise<Uint8Array>;
71
+ /**
72
+ * Decode a park envelope. Legacy shapes (the pre-SEC-1 4-element v1 array, and raw non-CBOR bare
73
+ * content) still DECODE — deliberately, so `authenticate` can reject them with a precise reason
74
+ * rather than the caller seeing an opaque parse failure. Decoding is not accepting.
75
+ */
76
+ export declare function decodeParkEnvelope(plaintext: Uint8Array): ParkEnvelope;
77
+ /**
78
+ * SEC-1: the authentication decision for a recovered parked entry. FAIL CLOSED — every path that is
79
+ * not a proven-good signature from THIS session's counterparty returns a failure.
80
+ *
81
+ * `counterpartyPubkeyHex` is the session's counterparty (the only principal allowed to have parked
82
+ * content into this session). Pass undefined when the session/counterparty is unknown: that is
83
+ * `counterparty_unknown` and is a REFUSAL, never a bypass — we cannot prove the signer, so we do not
84
+ * trust the content. (Same fail-closed stance the ordering-record check already takes.)
85
+ */
86
+ export declare function authenticateParkedEntry(args: {
87
+ env: ParkEnvelope;
88
+ sessionIdHex: string;
89
+ recipientPubkey: Uint8Array;
90
+ contentHash: Uint8Array;
91
+ counterpartyPubkeyHex: string | undefined;
92
+ }): ParkAuthVerdict;
93
+ //# sourceMappingURL=park-envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"park-envelope.d.ts","sourceRoot":"","sources":["../src/park-envelope.ts"],"names":[],"mappings":"AA6BA,OAAO,EAA2B,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAGnF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAW3F;AAED,sGAAsG;AACtG,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,CAAC;IACpB,+EAA+E;IAC/E,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,6DAA6D;IAC7D,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B,yEAAyE;IACzE,OAAO,CAAC,EAAE,UAAU,CAAC;CACtB;AAED,MAAM,MAAM,eAAe,GACvB,mBAAmB,GACnB,eAAe,GACf,yBAAyB,GACzB,sBAAsB,CAAC;AAE3B,MAAM,MAAM,eAAe,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,CAAC;AAEpF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,OAAO,EAAE,UAAU,CAAC;IACpB,YAAY,EAAE,UAAU,CAAC;IACzB,OAAO,EAAE,UAAU,CAAC;IACpB,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,cAAc,CAAC,EAAE,UAAU,CAAC;CAC7B,GAAG,UAAU,CASb;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,MAAM,EAAE,WAAW,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,UAAU,CAAC;IAC5B,WAAW,EAAE,UAAU,CAAC;IACxB,OAAO,EAAE,UAAU,CAAC;IACpB,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,cAAc,CAAC,EAAE,UAAU,CAAC;CAC7B,GAAG,OAAO,CAAC,UAAU,CAAC,CAetB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,UAAU,GAAG,YAAY,CA0BtE;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,YAAY,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,UAAU,CAAC;IAC5B,WAAW,EAAE,UAAU,CAAC;IACxB,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C,GAAG,eAAe,CA8BlB"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * SEC-1 — the relay-park content envelope, and its authentication.
3
+ *
4
+ * WHY THIS EXISTS. Relay deposit is unauthenticated by design (the blob is E2E-encrypted, so an open
5
+ * deposit cannot LEAK anything) and `sealToRecipient` is an ANONYMOUS public-key seal. Those two
6
+ * facts together mean anyone holding the recipient's PUBLIC key can mint a well-formed sealed entry
7
+ * for their mailbox. Before SEC-1 the recovery path took the sender identity from the SESSION ROW,
8
+ * never from the envelope — so injected content was attributed to the honest counterparty, appended
9
+ * as a Merkle leaf, and then NOTARIZED by the bilateral seal. The party best placed to exploit that
10
+ * is the RELAY ITSELF: the protocol hands it the session_id in plaintext on every deposit and the
11
+ * recipient pubkey is its mailbox key.
12
+ *
13
+ * Confidentiality was mistaken for the whole job. A relay that cannot READ your messages could still
14
+ * WRITE them for you.
15
+ *
16
+ * THE FIX. Every envelope carries a per-message SENDER SIGNATURE over a domain-separated statement
17
+ * bound to (session_id, recipient_pubkey, content_hash) — see `buildParkContentTbs`. It rides INSIDE
18
+ * the seal, so the relay can neither read, strip, nor forge it. Recovery FAILS CLOSED.
19
+ *
20
+ * Note the signature is bound to the sender's own K_local and NOT to the relay's ordering record
21
+ * (Structure1/2). That is deliberate: the ordering record embeds a RELAY-ASSIGNED sequence, so
22
+ * making authenticity depend on it would make the adversary a precondition for trusting content.
23
+ * It also keeps the CELLO-M7-MSG-001 crash backstop working — that path legitimately parks content
24
+ * with no ordering record (the durable awaiting queue does not persist one), and it can still sign.
25
+ *
26
+ * Crypto: Ed25519 (RFC 8032), SHA-256 (FIPS 180-4).
27
+ */
28
+ import { timingSafeEqual } from "node:crypto";
29
+ import { encode as cborEncode, decode as cborDecode } from "cbor-x";
30
+ import { verify, sealToRecipient } from "@cello-protocol/crypto";
31
+ import { buildParkContentTbs } from "@cello-protocol/protocol-types";
32
+ /**
33
+ * Compare an identity key (raw bytes) against a stored hex pubkey — on BYTES, never on the hex
34
+ * strings.
35
+ *
36
+ * Review M1: `sessions.counterparty_pubkey` is persisted VERBATIM from the IPC `target_pubkey`
37
+ * string with no normalization, so an uppercase or mixed-case pubkey is a perfectly functional
38
+ * session (Buffer.from(hex) is case-insensitive, so dialing, sealing and the direct content path all
39
+ * work) — but a `toString("hex") !== stored` comparison would then refuse EVERY parked recovery for
40
+ * that session as `signer_not_counterparty`, i.e. permanent store-and-forward mail loss reported as
41
+ * an attack. Comparing bytes makes the check independent of how the hex was cased on the way in.
42
+ *
43
+ * Length-guarded because timingSafeEqual throws on a length mismatch, and the input is
44
+ * attacker-controlled (a forged envelope can carry a short/long pubkey).
45
+ */
46
+ export function pubkeyMatchesHex(pubkey, storedHex) {
47
+ if (!storedHex)
48
+ return false;
49
+ let stored;
50
+ try {
51
+ stored = Buffer.from(storedHex, "hex");
52
+ }
53
+ catch {
54
+ return false;
55
+ }
56
+ // Buffer.from ignores trailing garbage rather than throwing, so a length check is the real guard.
57
+ if (stored.length === 0 || stored.length !== pubkey.length)
58
+ return false;
59
+ return timingSafeEqual(Buffer.from(pubkey), stored);
60
+ }
61
+ /** Current envelope version. v1 (unsigned) is decodable but NEVER acceptable — see authenticate(). */
62
+ export const PARK_ENVELOPE_VERSION = 2;
63
+ /**
64
+ * SEC-1: encode a SIGNED park envelope. `senderPubkey` and `parkSig` are REQUIRED — the type system
65
+ * is the enforcement point, so no call site can construct an unsigned envelope by omission (the
66
+ * exact drift that made this a downgrade attack: verification existed, but was opt-in).
67
+ */
68
+ export function encodeParkEnvelope(args) {
69
+ return cborEncode([
70
+ PARK_ENVELOPE_VERSION,
71
+ args.content,
72
+ args.structure1Cbor ?? null,
73
+ args.structure2Cbor ?? null,
74
+ args.senderPubkey,
75
+ args.parkSig,
76
+ ]);
77
+ }
78
+ /**
79
+ * SEC-1 — THE ONLY PRODUCER. Sign the entry as the sending agent, wrap it in a v2 envelope, and seal
80
+ * it to the recipient. Both park sites (the live hook and the crash backstop) call THIS, so the
81
+ * to-be-signed arguments are constructed in exactly one place.
82
+ *
83
+ * Review (hollow-test finding): while the two call sites each built their own signature, a producer
84
+ * that signed the WRONG TBS arguments — e.g. its own pubkey where the recipient's belongs — would
85
+ * emit envelopes no recipient could ever accept, and every consumer-side test would still pass. The
86
+ * bug would only surface as mail that silently never arrives. One producer + a round-trip test
87
+ * against the real consumer removes that whole class: if this function signs the wrong statement,
88
+ * `authenticateParkedEntry` rejects its output and the test goes red.
89
+ */
90
+ export async function sealParkEnvelope(args) {
91
+ const senderPubkey = await args.signer.getPublicKey();
92
+ const parkSig = await args.signer.sign(buildParkContentTbs(args.sessionIdHex, args.recipientPubkey, args.contentHash));
93
+ const envelope = encodeParkEnvelope({
94
+ content: args.content,
95
+ structure1Cbor: args.structure1Cbor,
96
+ structure2Cbor: args.structure2Cbor,
97
+ senderPubkey,
98
+ parkSig,
99
+ });
100
+ // The signature rides INSIDE the seal — the relay (the adversary) never sees it, so it can neither
101
+ // strip nor forge it. INV-3 is untouched: the relay still holds only ciphertext.
102
+ return sealToRecipient(args.recipientPubkey, envelope);
103
+ }
104
+ /**
105
+ * Decode a park envelope. Legacy shapes (the pre-SEC-1 4-element v1 array, and raw non-CBOR bare
106
+ * content) still DECODE — deliberately, so `authenticate` can reject them with a precise reason
107
+ * rather than the caller seeing an opaque parse failure. Decoding is not accepting.
108
+ */
109
+ export function decodeParkEnvelope(plaintext) {
110
+ try {
111
+ const arr = cborDecode(plaintext);
112
+ if (Array.isArray(arr) && arr.length === 6 && arr[0] === PARK_ENVELOPE_VERSION && arr[1] instanceof Uint8Array) {
113
+ return {
114
+ version: PARK_ENVELOPE_VERSION,
115
+ content: arr[1],
116
+ structure1Cbor: arr[2] instanceof Uint8Array ? arr[2] : undefined,
117
+ structure2Cbor: arr[3] instanceof Uint8Array ? arr[3] : undefined,
118
+ senderPubkey: arr[4] instanceof Uint8Array ? arr[4] : undefined,
119
+ parkSig: arr[5] instanceof Uint8Array ? arr[5] : undefined,
120
+ };
121
+ }
122
+ // Pre-SEC-1 v1 envelope: [1, content, s1|null, s2|null]. Decoded so it can be REFUSED by name.
123
+ if (Array.isArray(arr) && arr.length === 4 && arr[0] === 1 && arr[1] instanceof Uint8Array) {
124
+ return {
125
+ version: 1,
126
+ content: arr[1],
127
+ structure1Cbor: arr[2] instanceof Uint8Array ? arr[2] : undefined,
128
+ structure2Cbor: arr[3] instanceof Uint8Array ? arr[3] : undefined,
129
+ };
130
+ }
131
+ }
132
+ catch {
133
+ /* not an envelope — fall through to raw bare content */
134
+ }
135
+ return { version: 1, content: plaintext };
136
+ }
137
+ /**
138
+ * SEC-1: the authentication decision for a recovered parked entry. FAIL CLOSED — every path that is
139
+ * not a proven-good signature from THIS session's counterparty returns a failure.
140
+ *
141
+ * `counterpartyPubkeyHex` is the session's counterparty (the only principal allowed to have parked
142
+ * content into this session). Pass undefined when the session/counterparty is unknown: that is
143
+ * `counterparty_unknown` and is a REFUSAL, never a bypass — we cannot prove the signer, so we do not
144
+ * trust the content. (Same fail-closed stance the ordering-record check already takes.)
145
+ */
146
+ export function authenticateParkedEntry(args) {
147
+ const { env, sessionIdHex, recipientPubkey, contentHash, counterpartyPubkeyHex } = args;
148
+ // 1. Unsigned (v1 envelope, or raw bare content) — the shape an attacker sends, and the shape the
149
+ // pre-SEC-1 daemon ingested without a single check. Refused outright.
150
+ if (env.version !== PARK_ENVELOPE_VERSION || !env.senderPubkey || !env.parkSig) {
151
+ return { ok: false, reason: "unsigned_envelope" };
152
+ }
153
+ // 2. Fail closed when there is no counterparty to bind the signer to.
154
+ if (!counterpartyPubkeyHex) {
155
+ return { ok: false, reason: "counterparty_unknown" };
156
+ }
157
+ // 3. The signature must cover THESE bytes, for THIS session, into THIS mailbox. Verified BEFORE
158
+ // the signer check so a forged signature is never mistaken for a wrong-principal one.
159
+ const tbs = buildParkContentTbs(sessionIdHex, recipientPubkey, contentHash);
160
+ if (!verify(env.senderPubkey, tbs, env.parkSig)) {
161
+ return { ok: false, reason: "bad_signature" };
162
+ }
163
+ // 4. The signer MUST be this session's counterparty. A cryptographically valid signature by any
164
+ // other key is still a forgery of THIS conversation. Compared on BYTES (review M1) — the stored
165
+ // hex is un-normalized, so a string compare would turn a mixed-case pubkey into permanent
166
+ // mail loss that looks exactly like an attack in the log.
167
+ if (!pubkeyMatchesHex(env.senderPubkey, counterpartyPubkeyHex)) {
168
+ return { ok: false, reason: "signer_not_counterparty" };
169
+ }
170
+ return { ok: true };
171
+ }
172
+ //# sourceMappingURL=park-envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"park-envelope.js","sourceRoot":"","sources":["../src/park-envelope.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"retry-queue.d.ts","sourceRoot":"","sources":["../src/retry-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,wCAAwC;AACxC,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,UAAU,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,+CAA+C;AAC/C,MAAM,MAAM,YAAY,GAAG;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAErF,MAAM,MAAM,QAAQ,GAAG,CAAC,WAAW,EAAE,UAAU,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAE1E;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;sGAIkG;IAClG,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,UAAU,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,8DAA8D;AAC9D,MAAM,MAAM,UAAU,GAAG;IAAE,MAAM,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAC7E,MAAM,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,oBAAoB,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAE1E,qBAAa,UAAU;;gBAUT,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM;IAiD9C;;;OAGG;IACH,UAAU,IAAI,IAAI;IA6FlB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,GAAG,IAAI;IA2E5E;;;;OAIG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAmExE,mDAAmD;IACnD,aAAa,IAAI,MAAM;IAQvB,gDAAgD;IAChD,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAI1C,+FAA+F;IAC/F,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,EAAE;IAOvD;;;;;OAKG;IACH,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,GAAG,IAAI;IA0ClH,qEAAqE;IACrE,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,GAAG,IAAI;IAoBnF,iEAAiE;IACjE,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAI5D,mGAAmG;IACnG,mBAAmB,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAQpE;;;;;OAKG;IACG,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CA6B/F"}
1
+ {"version":3,"file":"retry-queue.d.ts","sourceRoot":"","sources":["../src/retry-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,wCAAwC;AACxC,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,UAAU,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,+CAA+C;AAC/C,MAAM,MAAM,YAAY,GAAG;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAErF,MAAM,MAAM,QAAQ,GAAG,CAAC,WAAW,EAAE,UAAU,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAE1E;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;sGAIkG;IAClG,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,UAAU,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,8DAA8D;AAC9D,MAAM,MAAM,UAAU,GAAG;IAAE,MAAM,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAC7E,MAAM,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,oBAAoB,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAE1E,qBAAa,UAAU;;gBAUT,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM;IAiD9C;;;OAGG;IACH,UAAU,IAAI,IAAI;IA6FlB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,GAAG,IAAI;IA2E5E;;;;OAIG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAmExE,mDAAmD;IACnD,aAAa,IAAI,MAAM;IAQvB,gDAAgD;IAChD,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAI1C,+FAA+F;IAC/F,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,EAAE;IAOvD;;;;;OAKG;IACH,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,GAAG,IAAI;IA0ClH,qEAAqE;IACrE,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,GAAG,IAAI;IAoBnF,iEAAiE;IACjE,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAI5D,mGAAmG;IACnG,mBAAmB,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAQpE;;;;;OAKG;IACG,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CA0C/F"}
@@ -444,8 +444,20 @@ export class RetryQueue {
444
444
  catch (err) {
445
445
  result = { parked: false, error: err instanceof Error ? err.message : String(err) };
446
446
  }
447
- if (!result.parked)
447
+ if (!result.parked) {
448
+ // Review M3: `result.error` used to be DISCARDED here, so every failure branch of the park
449
+ // target (owning_agent_not_found, no_persisted_relay_endpoint, no_counterparty,
450
+ // standing_receiver_unavailable, and SEC-1's new signing_key_unavailable) failed in total
451
+ // silence — the only trace was `parkedCount: 0`, a number with no reason. The entry is still
452
+ // KEPT for the next flush (AC-019); it just no longer fails quietly.
453
+ this.#logger.warn("content.park.failed", {
454
+ agentId,
455
+ sessionId,
456
+ contentHash: entry.contentHashHex,
457
+ error: result.error,
458
+ });
448
459
  continue; // keep for next reconnect / startup flush (AC-019)
460
+ }
449
461
  const idx = q.indexOf(entry);
450
462
  if (idx !== -1)
451
463
  q.splice(idx, 1);