@0xmaxma/claude-gateway 1.7.10 → 1.8.0

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 (173) hide show
  1. package/README.md +167 -41
  2. package/dist/agent/builtin-commands.js +2 -2
  3. package/dist/agent/builtin-commands.js.map +1 -1
  4. package/dist/agent/knowledge/config.d.ts.map +1 -1
  5. package/dist/agent/knowledge/config.js +2 -9
  6. package/dist/agent/knowledge/config.js.map +1 -1
  7. package/dist/agent/model-catalog.d.ts +62 -0
  8. package/dist/agent/model-catalog.d.ts.map +1 -0
  9. package/dist/agent/model-catalog.js +264 -0
  10. package/dist/agent/model-catalog.js.map +1 -0
  11. package/dist/agent/runner.d.ts +76 -0
  12. package/dist/agent/runner.d.ts.map +1 -1
  13. package/dist/agent/runner.js +189 -36
  14. package/dist/agent/runner.js.map +1 -1
  15. package/dist/api/cron-router.d.ts +12 -8
  16. package/dist/api/cron-router.d.ts.map +1 -1
  17. package/dist/api/cron-router.js +82 -16
  18. package/dist/api/cron-router.js.map +1 -1
  19. package/dist/api/gateway-router.d.ts +4 -0
  20. package/dist/api/gateway-router.d.ts.map +1 -1
  21. package/dist/api/gateway-router.js +14 -0
  22. package/dist/api/gateway-router.js.map +1 -1
  23. package/dist/api/manifest-sources.d.ts +15 -0
  24. package/dist/api/manifest-sources.d.ts.map +1 -0
  25. package/dist/api/manifest-sources.js +27 -0
  26. package/dist/api/manifest-sources.js.map +1 -0
  27. package/dist/api/meta-router.d.ts +13 -0
  28. package/dist/api/meta-router.d.ts.map +1 -0
  29. package/dist/api/meta-router.js +26 -0
  30. package/dist/api/meta-router.js.map +1 -0
  31. package/dist/api/packages.d.ts.map +1 -1
  32. package/dist/api/packages.js +18 -198
  33. package/dist/api/packages.js.map +1 -1
  34. package/dist/api/route-registry.d.ts +81 -0
  35. package/dist/api/route-registry.d.ts.map +1 -0
  36. package/dist/api/route-registry.js +44 -0
  37. package/dist/api/route-registry.js.map +1 -0
  38. package/dist/api/router.d.ts.map +1 -1
  39. package/dist/api/router.js +21 -3
  40. package/dist/api/router.js.map +1 -1
  41. package/dist/apps/installer.d.ts +73 -5
  42. package/dist/apps/installer.d.ts.map +1 -1
  43. package/dist/apps/installer.js +237 -22
  44. package/dist/apps/installer.js.map +1 -1
  45. package/dist/cli/args.d.ts +25 -0
  46. package/dist/cli/args.d.ts.map +1 -0
  47. package/dist/cli/args.js +55 -0
  48. package/dist/cli/args.js.map +1 -0
  49. package/dist/cli/colors.d.ts +37 -0
  50. package/dist/cli/colors.d.ts.map +1 -0
  51. package/dist/cli/colors.js +63 -0
  52. package/dist/cli/colors.js.map +1 -0
  53. package/dist/cli/command-names.d.ts +98 -0
  54. package/dist/cli/command-names.d.ts.map +1 -0
  55. package/dist/cli/command-names.js +109 -0
  56. package/dist/cli/command-names.js.map +1 -0
  57. package/dist/cli/commands/agents.d.ts +28 -0
  58. package/dist/cli/commands/agents.d.ts.map +1 -0
  59. package/dist/cli/commands/agents.js +321 -0
  60. package/dist/cli/commands/agents.js.map +1 -0
  61. package/dist/cli/commands/channels.d.ts +17 -0
  62. package/dist/cli/commands/channels.d.ts.map +1 -0
  63. package/dist/cli/commands/channels.js +101 -0
  64. package/dist/cli/commands/channels.js.map +1 -0
  65. package/dist/cli/commands/debug-bundle.d.ts +6 -0
  66. package/dist/cli/commands/debug-bundle.d.ts.map +1 -0
  67. package/dist/cli/commands/debug-bundle.js +220 -0
  68. package/dist/cli/commands/debug-bundle.js.map +1 -0
  69. package/dist/cli/commands/doctor.d.ts +3 -0
  70. package/dist/cli/commands/doctor.d.ts.map +1 -0
  71. package/dist/cli/commands/doctor.js +104 -0
  72. package/dist/cli/commands/doctor.js.map +1 -0
  73. package/dist/cli/commands/gateway.d.ts +12 -0
  74. package/dist/cli/commands/gateway.d.ts.map +1 -0
  75. package/dist/cli/commands/gateway.js +145 -0
  76. package/dist/cli/commands/gateway.js.map +1 -0
  77. package/dist/cli/commands/service.d.ts +33 -0
  78. package/dist/cli/commands/service.d.ts.map +1 -0
  79. package/dist/cli/commands/service.js +513 -0
  80. package/dist/cli/commands/service.js.map +1 -0
  81. package/dist/cli/commands/update.d.ts +16 -0
  82. package/dist/cli/commands/update.d.ts.map +1 -0
  83. package/dist/cli/commands/update.js +149 -0
  84. package/dist/cli/commands/update.js.map +1 -0
  85. package/dist/cli/commands.generated.d.ts +4 -0
  86. package/dist/cli/commands.generated.d.ts.map +1 -0
  87. package/dist/cli/commands.generated.js +220 -0
  88. package/dist/cli/commands.generated.js.map +1 -0
  89. package/dist/cli/health.d.ts +31 -0
  90. package/dist/cli/health.d.ts.map +1 -0
  91. package/dist/cli/health.js +49 -0
  92. package/dist/cli/health.js.map +1 -0
  93. package/dist/cli/http-client.d.ts +140 -0
  94. package/dist/cli/http-client.d.ts.map +1 -0
  95. package/dist/cli/http-client.js +332 -0
  96. package/dist/cli/http-client.js.map +1 -0
  97. package/dist/cli/index.d.ts +27 -0
  98. package/dist/cli/index.d.ts.map +1 -0
  99. package/dist/cli/index.js +372 -0
  100. package/dist/cli/index.js.map +1 -0
  101. package/dist/cli/manager.d.ts +64 -0
  102. package/dist/cli/manager.d.ts.map +1 -0
  103. package/dist/cli/manager.js +181 -0
  104. package/dist/cli/manager.js.map +1 -0
  105. package/dist/cli/output.d.ts +49 -0
  106. package/dist/cli/output.d.ts.map +1 -0
  107. package/dist/cli/output.js +101 -0
  108. package/dist/cli/output.js.map +1 -0
  109. package/dist/cli/prompt.d.ts +29 -0
  110. package/dist/cli/prompt.d.ts.map +1 -0
  111. package/dist/cli/prompt.js +154 -0
  112. package/dist/cli/prompt.js.map +1 -0
  113. package/dist/cli/redact.d.ts +3 -0
  114. package/dist/cli/redact.d.ts.map +1 -0
  115. package/dist/cli/redact.js +33 -0
  116. package/dist/cli/redact.js.map +1 -0
  117. package/dist/cli/types.d.ts +22 -0
  118. package/dist/cli/types.d.ts.map +1 -0
  119. package/dist/cli/types.js +4 -0
  120. package/dist/cli/types.js.map +1 -0
  121. package/dist/config/bootstrap.d.ts +33 -0
  122. package/dist/config/bootstrap.d.ts.map +1 -0
  123. package/dist/config/bootstrap.js +117 -0
  124. package/dist/config/bootstrap.js.map +1 -0
  125. package/dist/config/loader.d.ts.map +1 -1
  126. package/dist/config/loader.js +5 -1
  127. package/dist/config/loader.js.map +1 -1
  128. package/dist/config/watcher.d.ts.map +1 -1
  129. package/dist/config/watcher.js +2 -35
  130. package/dist/config/watcher.js.map +1 -1
  131. package/dist/discord/receiver.d.ts +6 -1
  132. package/dist/discord/receiver.d.ts.map +1 -1
  133. package/dist/discord/receiver.js +9 -1
  134. package/dist/discord/receiver.js.map +1 -1
  135. package/dist/entry.d.ts +3 -0
  136. package/dist/entry.d.ts.map +1 -0
  137. package/dist/entry.js +83 -0
  138. package/dist/entry.js.map +1 -0
  139. package/dist/index.js +160 -46
  140. package/dist/index.js.map +1 -1
  141. package/dist/load-dotenv.d.ts +2 -0
  142. package/dist/load-dotenv.d.ts.map +1 -0
  143. package/dist/load-dotenv.js +68 -0
  144. package/dist/load-dotenv.js.map +1 -0
  145. package/dist/packages/registry.d.ts +50 -0
  146. package/dist/packages/registry.d.ts.map +1 -0
  147. package/dist/packages/registry.js +209 -0
  148. package/dist/packages/registry.js.map +1 -0
  149. package/dist/shutdown-signals.d.ts +47 -0
  150. package/dist/shutdown-signals.d.ts.map +1 -0
  151. package/dist/shutdown-signals.js +62 -0
  152. package/dist/shutdown-signals.js.map +1 -0
  153. package/dist/telegram/receiver.d.ts +6 -1
  154. package/dist/telegram/receiver.d.ts.map +1 -1
  155. package/dist/telegram/receiver.js +9 -1
  156. package/dist/telegram/receiver.js.map +1 -1
  157. package/dist/utils/orphan-receivers.d.ts +90 -0
  158. package/dist/utils/orphan-receivers.d.ts.map +1 -0
  159. package/dist/utils/orphan-receivers.js +218 -0
  160. package/dist/utils/orphan-receivers.js.map +1 -0
  161. package/dist/utils/paths.d.ts +16 -0
  162. package/dist/utils/paths.d.ts.map +1 -0
  163. package/dist/utils/paths.js +60 -0
  164. package/dist/utils/paths.js.map +1 -0
  165. package/dist/utils/stop-child.d.ts +21 -0
  166. package/dist/utils/stop-child.d.ts.map +1 -0
  167. package/dist/utils/stop-child.js +64 -0
  168. package/dist/utils/stop-child.js.map +1 -0
  169. package/lib/pairing.ts +1 -1
  170. package/mcp/tools/agent/handlers.ts +1 -1
  171. package/mcp/tools/discord/receiver-server.ts +4 -0
  172. package/mcp/tools/telegram/receiver-server.ts +6 -4
  173. package/package.json +6 -4
@@ -177,6 +177,27 @@ type AsyncSpawnFn = (cmd: string, args: string[], opts?: {
177
177
  stderr: string;
178
178
  status: number | null;
179
179
  }>;
180
+ /**
181
+ * A filesystem call that failed because the gateway user lacks the rights, not
182
+ * because the path is wrong. Containers running as their image's uid leave
183
+ * files and directories the gateway user can neither delete nor rename, and
184
+ * those are the two codes the kernel reports for it.
185
+ */
186
+ export declare function isPermissionError(err: unknown): boolean;
187
+ /**
188
+ * Deepest directory containing both paths, never either path itself.
189
+ *
190
+ * Used to pick a **single** mount for the root helper that moves a bind path:
191
+ * mounting each parent separately would put the two paths on different mount
192
+ * points, where `rename(2)` fails EXDEV and `mv` degrades to a copy.
193
+ *
194
+ * The comparison stops one component short of the shorter path, so the result
195
+ * is always a strict ancestor of both — for siblings (what the callers here
196
+ * pass) that is exact. When one path contains the other it is the *shallower*
197
+ * path's parent, i.e. broader than strictly needed; never narrower, which would
198
+ * put a path outside the mount.
199
+ */
200
+ export declare function commonAncestorDir(a: string, b: string): string;
180
201
  export declare class AppInstaller {
181
202
  private readonly registry;
182
203
  private readonly registryClient;
@@ -311,13 +332,16 @@ export declare class AppInstaller {
311
332
  * are disabled. The timer is `unref`'d so it never holds the event loop open.
312
333
  */
313
334
  /**
314
- * Reclaim update scratch directories a crashed or killed update left behind:
315
- * the `.cg-update-*` staging checkout and the `-old-`/`-failed-` release
316
- * snapshots. Staging moved next to the install path so the swap is a
335
+ * Reclaim the `.cg-update-*` staging checkout a crashed or killed update left
336
+ * behind. Staging moved next to the install path so the swap is a
317
337
  * same-filesystem rename, which also means `/tmp` cleanup no longer collects
318
338
  * it — without this sweep a mid-update crash leaks a full app checkout
319
339
  * forever.
320
340
  *
341
+ * Release snapshots (`-old-`/`-failed-`) are **reported, never removed**: see
342
+ * {@link RELEASE_SNAPSHOT_DIR_RE}. They can hold the only copy of a live bind
343
+ * mount, and this sweep deletes with root.
344
+ *
321
345
  * **Boot only.** An update in flight owns directories matching these names,
322
346
  * so this must run before any update can start. Never throws; a directory it
323
347
  * cannot remove is reported and skipped.
@@ -475,10 +499,54 @@ export declare class AppInstaller {
475
499
  private moveBindMounts;
476
500
  private moveBindEntry;
477
501
  /**
478
- * Undo {@link moveBindMounts} after a failed update: replay the recorded
479
- * renames backwards so the restored previous app dir gets its state back.
502
+ * Name what a preserve-the-live-copy collision is about to delete.
503
+ *
504
+ * The discarded side is the release's own checkout, so it is always readable
505
+ * by the gateway user even when the live side is not. Without this the log
506
+ * says only that "the release's copy was discarded" — which reads as a
507
+ * `.gitkeep` and hides the case that matters: a release shipping a new
508
+ * `init.sql` or entrypoint script *inside* a path whose live copy wins, where
509
+ * the file is dropped on every update and nothing ever says which.
510
+ */
511
+ private describeDiscarded;
512
+ /**
513
+ * Undo {@link moveBindMounts}. Returns the paths it could **not** move back:
514
+ * those still live only under `fromDir`, so the caller must keep that
515
+ * directory rather than clean it up.
516
+ *
517
+ * A path that is missing at the source, or already present at the
518
+ * destination, is not a failure — it needs no move and is not reported.
480
519
  */
481
520
  private restoreBindMounts;
521
+ /**
522
+ * Move one bind-mount path between app directories.
523
+ *
524
+ * `rename(2)` that moves a **directory** to a different parent needs write
525
+ * permission on the directory itself — the kernel has to update its `..`
526
+ * entry — not just on the two parents, which is exactly what the swap does. A bind mount the app's own container created is owned by that
527
+ * image's uid (postgres initdb leaves its data directory mode 0700), so the
528
+ * gateway user cannot rename it and the update swap failed with EACCES on
529
+ * every attempt.
530
+ *
531
+ * The same host-vs-container ownership split is already handled elsewhere in
532
+ * this file — backups tar through a root helper container, {@link rmrf} falls
533
+ * back to `sudo rm -rf`. The swap was the one path still doing it bare, so
534
+ * the fallback here matches the backup mechanism: a throwaway root container.
535
+ *
536
+ * The helper mounts the **nearest common ancestor** of the two paths rather
537
+ * than each parent separately, and that is load-bearing: two bind mounts are
538
+ * two mount points, so `rename(2)` across them fails EXDEV and busybox `mv`
539
+ * silently degrades to copy-then-unlink. Measured by inode, two mounts give a
540
+ * new inode (a copy) where one mount preserves it (a real rename). For a
541
+ * database directory the degraded path means duplicating it on disk and
542
+ * losing the atomicity the swap depends on.
543
+ *
544
+ * The single mount is therefore the apps root, not one app's directory — the
545
+ * two app directories are siblings under it. The helper is a throwaway
546
+ * container whose only command is the `mv`, and it is reached only after the
547
+ * gateway user's own rename was refused.
548
+ */
549
+ private moveBindPath;
482
550
  /**
483
551
  * Stop conflicting containers then run `docker compose up -d --wait`.
484
552
  * Captures container logs into the job on failure before rethrowing.
@@ -1 +1 @@
1
- {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../src/apps/installer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../src/apps/installer.ts"],"names":[],"mappings":"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"}
@@ -34,6 +34,8 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.AppInstaller = void 0;
37
+ exports.isPermissionError = isPermissionError;
38
+ exports.commonAncestorDir = commonAncestorDir;
37
39
  exports.parseComposePs = parseComposePs;
38
40
  exports.mapContainerStatesToAppStatus = mapContainerStatesToAppStatus;
39
41
  const fs = __importStar(require("node:fs"));
@@ -65,6 +67,10 @@ const VOLUME_TAR_TIMEOUT_MS = 300000;
65
67
  // OCI image used for the throwaway tar helper. Small, ubiquitous, already a
66
68
  // transitive dependency of most stacks, so it is almost always cache-warm.
67
69
  const BACKUP_HELPER_IMAGE = 'alpine';
70
+ // Ceiling for the root helper that moves a bind path the gateway user cannot
71
+ // rename itself. The move is a single rename(2) — instant — so this only bounds
72
+ // a stuck container (or a cold pull of the helper image) during an update swap.
73
+ const BIND_MOVE_TIMEOUT_MS = 120000;
68
74
  // Per-app ceiling for the boot-time `compose up --wait` during restore. Runs in
69
75
  // the background (non-blocking), so this only bounds how long a hung container
70
76
  // keeps its child process alive — not the gateway's responsiveness. Shorter than
@@ -98,12 +104,60 @@ const GITHUB_URL_RE = /^https:\/\/github\.com\/(?!.*\.\.)[A-Za-z0-9][A-Za-z0-9_.
98
104
  // ─── Installer ────────────────────────────────────────────────────────────────
99
105
  const UUID_RE_SRC = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}';
100
106
  /**
101
- * Scratch directories `runUpdate` creates beside an app's install path:
102
- * `.cg-update-<app>-<uuid>` (staging checkout) and `<appDir>-old-<uuid>` /
103
- * `<appDir>-failed-<uuid>` (release snapshots). All three carry a v4 UUID, so
104
- * the pattern cannot match an app directory named by a user.
107
+ * The staging checkout `runUpdate` creates beside an app's install path,
108
+ * `.cg-update-<app>-<uuid>`. It holds a fresh clone and nothing else, so a
109
+ * crash leftover is pure garbage and safe to reclaim. The UUID means the
110
+ * pattern cannot match an app directory named by a user.
105
111
  */
106
- const STALE_UPDATE_DIR_RE = new RegExp(`^\\.cg-update-.+-${UUID_RE_SRC}$|-(?:old|failed)-${UUID_RE_SRC}$`);
112
+ const STALE_UPDATE_DIR_RE = new RegExp(`^\\.cg-update-.+-${UUID_RE_SRC}$`);
113
+ /**
114
+ * The release snapshots `runUpdate` renames an install path to during the swap:
115
+ * `<appDir>-old-<uuid>` (previous release) and `<appDir>-failed-<uuid>` (the
116
+ * release that failed). Deliberately **not** swept.
117
+ *
118
+ * Both can hold the only copy of live bind-mount data. `-failed-` is kept on
119
+ * purpose when a rollback cannot move a bind path back, and a crash between the
120
+ * swap's two renames leaves an `-old-` still holding the paths that had not
121
+ * moved yet. Sweeping them would delete a database — through {@link rmrf}'s
122
+ * `sudo rm -rf` fallback, which exists precisely to defeat the container
123
+ * ownership that made the data unmovable in the first place. They are reported
124
+ * at boot instead, so a leak is visible rather than silent.
125
+ */
126
+ const RELEASE_SNAPSHOT_DIR_RE = new RegExp(`-(?:old|failed)-${UUID_RE_SRC}$`);
127
+ /**
128
+ * A filesystem call that failed because the gateway user lacks the rights, not
129
+ * because the path is wrong. Containers running as their image's uid leave
130
+ * files and directories the gateway user can neither delete nor rename, and
131
+ * those are the two codes the kernel reports for it.
132
+ */
133
+ function isPermissionError(err) {
134
+ const code = err?.code;
135
+ return code === 'EACCES' || code === 'EPERM';
136
+ }
137
+ /**
138
+ * Deepest directory containing both paths, never either path itself.
139
+ *
140
+ * Used to pick a **single** mount for the root helper that moves a bind path:
141
+ * mounting each parent separately would put the two paths on different mount
142
+ * points, where `rename(2)` fails EXDEV and `mv` degrades to a copy.
143
+ *
144
+ * The comparison stops one component short of the shorter path, so the result
145
+ * is always a strict ancestor of both — for siblings (what the callers here
146
+ * pass) that is exact. When one path contains the other it is the *shallower*
147
+ * path's parent, i.e. broader than strictly needed; never narrower, which would
148
+ * put a path outside the mount.
149
+ */
150
+ function commonAncestorDir(a, b) {
151
+ const left = path.resolve(a).split(path.sep);
152
+ const right = path.resolve(b).split(path.sep);
153
+ const shared = [];
154
+ for (let i = 0; i < Math.min(left.length, right.length) - 1; i++) {
155
+ if (left[i] !== right[i])
156
+ break;
157
+ shared.push(left[i]);
158
+ }
159
+ return shared.join(path.sep) || path.sep;
160
+ }
107
161
  /**
108
162
  * Rows from a `docker compose … --format json` call. Compose has emitted both a
109
163
  * single JSON array and one object per line across its 2.x line, so accept
@@ -876,13 +930,16 @@ class AppInstaller {
876
930
  * are disabled. The timer is `unref`'d so it never holds the event loop open.
877
931
  */
878
932
  /**
879
- * Reclaim update scratch directories a crashed or killed update left behind:
880
- * the `.cg-update-*` staging checkout and the `-old-`/`-failed-` release
881
- * snapshots. Staging moved next to the install path so the swap is a
933
+ * Reclaim the `.cg-update-*` staging checkout a crashed or killed update left
934
+ * behind. Staging moved next to the install path so the swap is a
882
935
  * same-filesystem rename, which also means `/tmp` cleanup no longer collects
883
936
  * it — without this sweep a mid-update crash leaks a full app checkout
884
937
  * forever.
885
938
  *
939
+ * Release snapshots (`-old-`/`-failed-`) are **reported, never removed**: see
940
+ * {@link RELEASE_SNAPSHOT_DIR_RE}. They can hold the only copy of a live bind
941
+ * mount, and this sweep deletes with root.
942
+ *
886
943
  * **Boot only.** An update in flight owns directories matching these names,
887
944
  * so this must run before any update can start. Never throws; a directory it
888
945
  * cannot remove is reported and skipped.
@@ -906,11 +963,16 @@ class AppInstaller {
906
963
  return swept; // registry unreadable — do not guess
907
964
  }
908
965
  for (const name of names) {
909
- if (!STALE_UPDATE_DIR_RE.test(name))
910
- continue;
911
966
  const full = path.join(this.appsDir, name);
912
967
  if (live.has(full))
913
968
  continue;
969
+ if (RELEASE_SNAPSHOT_DIR_RE.test(name)) {
970
+ console.warn(`[installer] keeping app release snapshot "${full}" — it may hold the only copy of a `
971
+ + 'bind mount an update could not move back. Recover or delete it by hand.');
972
+ continue;
973
+ }
974
+ if (!STALE_UPDATE_DIR_RE.test(name))
975
+ continue;
914
976
  try {
915
977
  this.rmrf(full);
916
978
  swept.push(full);
@@ -1542,6 +1604,7 @@ class AppInstaller {
1542
1604
  const finalDir = entry.installPath;
1543
1605
  const oldBackupDir = `${finalDir}-old-${crypto.randomUUID()}`;
1544
1606
  let swapped = false;
1607
+ let reachedContainerStart = false;
1545
1608
  let failedDir = null;
1546
1609
  const movedBindMounts = [];
1547
1610
  try {
@@ -1562,17 +1625,25 @@ class AppInstaller {
1562
1625
  }
1563
1626
  // ── Start new containers ────────────────────────────────────────────
1564
1627
  this.log(job, 'Starting new containers');
1628
+ reachedContainerStart = true;
1565
1629
  this.composeUp(appName, finalDir, job);
1566
1630
  }
1567
1631
  catch (upErr) {
1568
- this.log(job, 'New containers failed — rolling back to previous version');
1632
+ // The catch spans the whole swap, so a failure here is not necessarily a
1633
+ // container failure — saying it is sends diagnosis to the wrong
1634
+ // subsystem (a bind-mount move that threw looked exactly like a crashed
1635
+ // container in the job log).
1636
+ this.log(job, reachedContainerStart
1637
+ ? 'New containers failed — rolling back to previous version'
1638
+ : `Update failed during the directory swap — rolling back to previous version: ${upErr.message}`);
1569
1639
  let rollbackFailed = false;
1570
1640
  failedDir = `${finalDir}-failed-${crypto.randomUUID()}`;
1571
1641
  try {
1642
+ let unrestored = [];
1572
1643
  if (swapped) {
1573
1644
  fs.renameSync(finalDir, failedDir);
1574
1645
  fs.renameSync(oldBackupDir, finalDir);
1575
- this.restoreBindMounts(failedDir, finalDir, movedBindMounts, job);
1646
+ unrestored = this.restoreBindMounts(failedDir, finalDir, movedBindMounts, job);
1576
1647
  }
1577
1648
  else if (fs.existsSync(oldBackupDir)) {
1578
1649
  fs.renameSync(oldBackupDir, finalDir);
@@ -1581,7 +1652,25 @@ class AppInstaller {
1581
1652
  // before this update built over it, so the restored source and the
1582
1653
  // restored image are the same release. Falls back to rebuilding from
1583
1654
  // the restored source when an old image is no longer on disk.
1655
+ //
1656
+ // This has to happen even when the bind restore below refuses to
1657
+ // start the app: the source is already back on the previous release,
1658
+ // so `<app>-<service>:latest` must name that release's build before
1659
+ // *anything* starts it. Leaving it on the failed release's build
1660
+ // means an operator who finishes the recovery by hand brings old
1661
+ // source up on a new image — the crash-loop `preserveRunningImages`
1662
+ // exists to prevent.
1584
1663
  const rebuild = !this.restorePreservedImages(preservedImages, finalDir, job);
1664
+ if (unrestored.length > 0) {
1665
+ // Throwing here is deliberate: it skips both the container start
1666
+ // below and the safeRmrf of failedDir, so the only copy of that
1667
+ // data stays on disk. Starting the app on a half-restored
1668
+ // directory is worse than not starting it — postgres on an empty
1669
+ // pgdata initialises a fresh cluster and then looks healthy.
1670
+ throw new Error(`live bind-mount data for ${unrestored.map((r) => `"${r}"`).join(', ')} could not be moved back `
1671
+ + `— it is still in "${failedDir}", which has been kept. Move those paths back into `
1672
+ + `"${finalDir}" before starting the app.`);
1673
+ }
1585
1674
  const upArgs = ['docker', 'compose', '-p', appName, 'up', '-d'];
1586
1675
  if (rebuild)
1587
1676
  upArgs.push('--build');
@@ -1603,6 +1692,20 @@ class AppInstaller {
1603
1692
  this.log(job, `ROLLBACK FAILED — app "${appName}" may be in a broken state: ${rollbackErr.message}`);
1604
1693
  }
1605
1694
  if (rollbackFailed) {
1695
+ // Keep the private `cg-rollback-*` tags: they are the only remaining
1696
+ // reference to the pre-update build, and the outer catch drops them.
1697
+ // Clearing the list here is what stops that — an incomplete rollback
1698
+ // is finished by hand, and that recovery needs the old image to still
1699
+ // exist. Untagged, containerd is free to reclaim it.
1700
+ if (preservedImages.some((i) => i.backupRef)) {
1701
+ this.log(job, `Pre-update images kept for manual recovery: ${preservedImages.filter((i) => i.backupRef).map((i) => `"${i.backupRef}" (for "${i.ref}")`).join(', ')}`);
1702
+ }
1703
+ preservedImages = [];
1704
+ // The success path above sets 'running'. Leaving the registry on the
1705
+ // pre-update 'running' here would report a healthy app that is not
1706
+ // running at all — and this branch now includes the case where the
1707
+ // app was deliberately not restarted.
1708
+ await this.registry.updateStatus(appName, 'error').catch(() => { });
1606
1709
  throw new Error(`Update failed and rollback also failed — app "${appName}" may be in a broken state. Check job logs for details.`);
1607
1710
  }
1608
1711
  throw upErr;
@@ -2111,7 +2214,7 @@ class AppInstaller {
2111
2214
  const code = err.code;
2112
2215
  // Containers running as root leave root-owned files (e.g. postgres pgdata)
2113
2216
  // that the gateway user cannot delete — fs.rmSync surfaces EACCES or EPERM.
2114
- if (code === 'EACCES' || code === 'EPERM') {
2217
+ if (isPermissionError(err)) {
2115
2218
  console.warn(`[installer] ${code} removing "${dirPath}" — falling back to sudo rm -rf`);
2116
2219
  this.run(['sudo', 'rm', '-rf', dirPath]);
2117
2220
  }
@@ -2218,7 +2321,7 @@ class AppInstaller {
2218
2321
  destStat = null;
2219
2322
  }
2220
2323
  if (destStat === null) {
2221
- fs.renameSync(source, destination);
2324
+ this.moveBindPath(source, destination, rel, job);
2222
2325
  moved?.push(rel);
2223
2326
  return;
2224
2327
  }
@@ -2232,24 +2335,78 @@ class AppInstaller {
2232
2335
  // into the restored previous app dir. Exact rollback beats the renames,
2233
2336
  // which are same-filesystem and only walk a directory the release also
2234
2337
  // ships content in.
2235
- for (const name of fs.readdirSync(source)) {
2236
- this.moveBindEntry(fromDir, toDir, `${rel}/${name}`, job, moved);
2338
+ let entries;
2339
+ try {
2340
+ entries = fs.readdirSync(source);
2341
+ }
2342
+ catch (err) {
2343
+ // The live directory is one the app's container owns and the gateway
2344
+ // user cannot even list (postgres leaves pgdata 0700). There is no way
2345
+ // to merge into it entry by entry, so preserve it wholesale — the same
2346
+ // trade the non-directory collision below already makes.
2347
+ if (!isPermissionError(err))
2348
+ throw err;
2349
+ entries = null;
2350
+ }
2351
+ if (entries !== null) {
2352
+ // Each child that needs the root helper costs its own container. That
2353
+ // is bounded by the live directory's own entry count, and only reached
2354
+ // when a release ships tracked content *inside* a path whose children
2355
+ // the gateway user cannot rename — rare enough not to trade the exact
2356
+ // rollback above for one wholesale move.
2357
+ for (const name of entries) {
2358
+ this.moveBindEntry(fromDir, toDir, `${rel}/${name}`, job, moved);
2359
+ }
2360
+ return;
2237
2361
  }
2238
- return;
2239
2362
  }
2240
- this.log(job, `Warning: preserved existing bind-mount data at "${rel}" — the updated release's copy of that path was discarded`);
2363
+ this.log(job, `Warning: preserved existing bind-mount data at "${rel}" — the updated release's copy of that path `
2364
+ + `was discarded (${this.describeDiscarded(destination)})`);
2241
2365
  fs.rmSync(destination, { recursive: true, force: true });
2242
- fs.renameSync(source, destination);
2366
+ this.moveBindPath(source, destination, rel, job);
2243
2367
  moved?.push(rel);
2244
2368
  }
2245
2369
  /**
2246
- * Undo {@link moveBindMounts} after a failed update: replay the recorded
2247
- * renames backwards so the restored previous app dir gets its state back.
2370
+ * Name what a preserve-the-live-copy collision is about to delete.
2371
+ *
2372
+ * The discarded side is the release's own checkout, so it is always readable
2373
+ * by the gateway user even when the live side is not. Without this the log
2374
+ * says only that "the release's copy was discarded" — which reads as a
2375
+ * `.gitkeep` and hides the case that matters: a release shipping a new
2376
+ * `init.sql` or entrypoint script *inside* a path whose live copy wins, where
2377
+ * the file is dropped on every update and nothing ever says which.
2378
+ */
2379
+ describeDiscarded(destination, limit = 10) {
2380
+ let names;
2381
+ try {
2382
+ names = fs.statSync(destination).isDirectory()
2383
+ ? fs.readdirSync(destination).sort()
2384
+ : [path.basename(destination)];
2385
+ }
2386
+ catch {
2387
+ return 'contents unreadable';
2388
+ }
2389
+ if (names.length === 0)
2390
+ return 'it was empty';
2391
+ const shown = names.slice(0, limit).map((n) => `"${n}"`).join(', ');
2392
+ return names.length > limit
2393
+ ? `${shown} and ${names.length - limit} more`
2394
+ : shown;
2395
+ }
2396
+ /**
2397
+ * Undo {@link moveBindMounts}. Returns the paths it could **not** move back:
2398
+ * those still live only under `fromDir`, so the caller must keep that
2399
+ * directory rather than clean it up.
2400
+ *
2401
+ * A path that is missing at the source, or already present at the
2402
+ * destination, is not a failure — it needs no move and is not reported.
2248
2403
  */
2249
2404
  restoreBindMounts(fromDir, toDir, moved, job) {
2405
+ const unrestored = [];
2250
2406
  for (const rel of [...moved].reverse()) {
2251
2407
  if (!this.isSafeBindRel(fromDir, toDir, rel)) {
2252
2408
  this.log(job, `Warning: skipping bind-mount path outside the app directory: "${rel}"`);
2409
+ unrestored.push(rel);
2253
2410
  continue;
2254
2411
  }
2255
2412
  const source = path.resolve(fromDir, rel);
@@ -2258,12 +2415,70 @@ class AppInstaller {
2258
2415
  continue;
2259
2416
  try {
2260
2417
  this.ensureDirWithinNoSymlink(toDir, path.dirname(destination));
2261
- fs.renameSync(source, destination);
2418
+ this.moveBindPath(source, destination, rel, job);
2262
2419
  }
2263
2420
  catch (err) {
2264
2421
  this.log(job, `Warning: could not restore bind-mount path "${rel}": ${err.message}`);
2422
+ unrestored.push(rel);
2265
2423
  }
2266
2424
  }
2425
+ return unrestored;
2426
+ }
2427
+ /**
2428
+ * Move one bind-mount path between app directories.
2429
+ *
2430
+ * `rename(2)` that moves a **directory** to a different parent needs write
2431
+ * permission on the directory itself — the kernel has to update its `..`
2432
+ * entry — not just on the two parents, which is exactly what the swap does. A bind mount the app's own container created is owned by that
2433
+ * image's uid (postgres initdb leaves its data directory mode 0700), so the
2434
+ * gateway user cannot rename it and the update swap failed with EACCES on
2435
+ * every attempt.
2436
+ *
2437
+ * The same host-vs-container ownership split is already handled elsewhere in
2438
+ * this file — backups tar through a root helper container, {@link rmrf} falls
2439
+ * back to `sudo rm -rf`. The swap was the one path still doing it bare, so
2440
+ * the fallback here matches the backup mechanism: a throwaway root container.
2441
+ *
2442
+ * The helper mounts the **nearest common ancestor** of the two paths rather
2443
+ * than each parent separately, and that is load-bearing: two bind mounts are
2444
+ * two mount points, so `rename(2)` across them fails EXDEV and busybox `mv`
2445
+ * silently degrades to copy-then-unlink. Measured by inode, two mounts give a
2446
+ * new inode (a copy) where one mount preserves it (a real rename). For a
2447
+ * database directory the degraded path means duplicating it on disk and
2448
+ * losing the atomicity the swap depends on.
2449
+ *
2450
+ * The single mount is therefore the apps root, not one app's directory — the
2451
+ * two app directories are siblings under it. The helper is a throwaway
2452
+ * container whose only command is the `mv`, and it is reached only after the
2453
+ * gateway user's own rename was refused.
2454
+ */
2455
+ moveBindPath(source, destination, rel, job) {
2456
+ try {
2457
+ fs.renameSync(source, destination);
2458
+ return;
2459
+ }
2460
+ catch (err) {
2461
+ if (!isPermissionError(err))
2462
+ throw err;
2463
+ }
2464
+ const base = commonAncestorDir(source, destination);
2465
+ const from = path.relative(base, source);
2466
+ const to = path.relative(base, destination);
2467
+ if (base === path.parse(base).root || base.includes(':') || from === '' || to === ''
2468
+ || from.split(path.sep).includes('..') || to.split(path.sep).includes('..')) {
2469
+ // A colon would be read as the mount separator by `docker -v`, silently
2470
+ // mounting something other than what was asked for.
2471
+ throw new Error(`Cannot move bind-mount path "${rel}" as root: unsafe mount base "${base}"`);
2472
+ }
2473
+ this.log(job, `Bind-mount path "${rel}" is not writable by the gateway user (created by the app's container) `
2474
+ + '— moving it with a root helper container');
2475
+ this.run([
2476
+ 'docker', 'run', '--rm',
2477
+ '-v', `${base}:/mnt`,
2478
+ BACKUP_HELPER_IMAGE,
2479
+ 'mv', path.posix.join('/mnt', from.split(path.sep).join('/')),
2480
+ path.posix.join('/mnt', to.split(path.sep).join('/')),
2481
+ ], undefined, BIND_MOVE_TIMEOUT_MS);
2267
2482
  }
2268
2483
  /**
2269
2484
  * Stop conflicting containers then run `docker compose up -d --wait`.