dodo-mcp 1.2.1 → 1.3.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 (142) hide show
  1. package/README.md +29 -6
  2. package/dist/cli/dataRecovery.js +38 -0
  3. package/dist/cli/dataRecovery.js.map +1 -0
  4. package/dist/cli/deployment.js +61 -0
  5. package/dist/cli/deployment.js.map +1 -0
  6. package/dist/cli/main.js +35 -1
  7. package/dist/cli/main.js.map +1 -1
  8. package/dist/config/globalConfig.js +3 -5
  9. package/dist/config/globalConfig.js.map +1 -1
  10. package/dist/errors.js +1 -1
  11. package/dist/errors.js.map +1 -1
  12. package/dist/ipc/server.js +2 -0
  13. package/dist/ipc/server.js.map +1 -1
  14. package/dist/ipc/timeouts.js +5 -0
  15. package/dist/ipc/timeouts.js.map +1 -0
  16. package/dist/platform/privateFs.js +32 -3
  17. package/dist/platform/privateFs.js.map +1 -1
  18. package/dist/platform/windowsPrivateAcl.js +90 -0
  19. package/dist/platform/windowsPrivateAcl.js.map +1 -0
  20. package/dist/security/outboundAddress.js +20 -0
  21. package/dist/security/outboundAddress.js.map +1 -0
  22. package/dist/server/aiAdmin.js +5 -4
  23. package/dist/server/aiAdmin.js.map +1 -1
  24. package/dist/server/appServer.js +2 -5
  25. package/dist/server/appServer.js.map +1 -1
  26. package/dist/server/bootstrap.js +14 -0
  27. package/dist/server/bootstrap.js.map +1 -1
  28. package/dist/server/configUi/index.html +3 -0
  29. package/dist/server/configUi/ui/dataRecovery.js +90 -0
  30. package/dist/server/configUi/ui/deployment.js +111 -0
  31. package/dist/server/configUi/ui/recovery.js +167 -0
  32. package/dist/server/configUi/workbench.js +23 -7
  33. package/dist/server/installationRuntime.js +3 -1
  34. package/dist/server/installationRuntime.js.map +1 -1
  35. package/dist/server/instructions.js.map +1 -1
  36. package/dist/server/ipcDispatch.js +219 -0
  37. package/dist/server/ipcDispatch.js.map +1 -1
  38. package/dist/server/localConfig.js +1 -1
  39. package/dist/server/localConfig.js.map +1 -1
  40. package/dist/server/stdioServer.js +2 -5
  41. package/dist/server/stdioServer.js.map +1 -1
  42. package/dist/services/ai/network.js +1 -18
  43. package/dist/services/ai/network.js.map +1 -1
  44. package/dist/services/ai/subagents.js +5 -2
  45. package/dist/services/ai/subagents.js.map +1 -1
  46. package/dist/services/assistance/verification.js +43 -10
  47. package/dist/services/assistance/verification.js.map +1 -1
  48. package/dist/services/changes/applier.js +472 -297
  49. package/dist/services/changes/applier.js.map +1 -1
  50. package/dist/services/git/gitService.js +150 -34
  51. package/dist/services/git/gitService.js.map +1 -1
  52. package/dist/services/jobs/jobManager.js +59 -2
  53. package/dist/services/jobs/jobManager.js.map +1 -1
  54. package/dist/services/jobs/spool.js +9 -3
  55. package/dist/services/jobs/spool.js.map +1 -1
  56. package/dist/services/multimodal/mediaService.js +6 -6
  57. package/dist/services/multimodal/mediaService.js.map +1 -1
  58. package/dist/services/recovery/configKeys.js +115 -0
  59. package/dist/services/recovery/configKeys.js.map +1 -0
  60. package/dist/services/recovery/configVault.js +361 -0
  61. package/dist/services/recovery/configVault.js.map +1 -0
  62. package/dist/services/recovery/contracts.js +24 -0
  63. package/dist/services/recovery/contracts.js.map +1 -0
  64. package/dist/services/recovery/databaseAwareness.js +179 -0
  65. package/dist/services/recovery/databaseAwareness.js.map +1 -0
  66. package/dist/services/recovery/deploymentArchive.js +129 -0
  67. package/dist/services/recovery/deploymentArchive.js.map +1 -0
  68. package/dist/services/recovery/deploymentContracts.js +70 -0
  69. package/dist/services/recovery/deploymentContracts.js.map +1 -0
  70. package/dist/services/recovery/deploymentHealth.js +143 -0
  71. package/dist/services/recovery/deploymentHealth.js.map +1 -0
  72. package/dist/services/recovery/deploymentMaintenance.js +197 -0
  73. package/dist/services/recovery/deploymentMaintenance.js.map +1 -0
  74. package/dist/services/recovery/deploymentService.js +429 -0
  75. package/dist/services/recovery/deploymentService.js.map +1 -0
  76. package/dist/services/recovery/deploymentSource.js +36 -0
  77. package/dist/services/recovery/deploymentSource.js.map +1 -0
  78. package/dist/services/recovery/dockerDeployment.js +172 -0
  79. package/dist/services/recovery/dockerDeployment.js.map +1 -0
  80. package/dist/services/recovery/drift.js +178 -0
  81. package/dist/services/recovery/drift.js.map +1 -0
  82. package/dist/services/recovery/evidence.js +127 -0
  83. package/dist/services/recovery/evidence.js.map +1 -0
  84. package/dist/services/recovery/gitCopies.js +219 -0
  85. package/dist/services/recovery/gitCopies.js.map +1 -0
  86. package/dist/services/recovery/history.js +423 -0
  87. package/dist/services/recovery/history.js.map +1 -0
  88. package/dist/services/recovery/recoveryService.js +521 -0
  89. package/dist/services/recovery/recoveryService.js.map +1 -0
  90. package/dist/services/recovery/storage.js +344 -0
  91. package/dist/services/recovery/storage.js.map +1 -0
  92. package/dist/services/schedules/scheduleService.js +9 -10
  93. package/dist/services/schedules/scheduleService.js.map +1 -1
  94. package/dist/store/db.js +71 -0
  95. package/dist/store/db.js.map +1 -1
  96. package/dist/tools/catalog.js +4 -0
  97. package/dist/tools/catalog.js.map +1 -1
  98. package/dist/tools/changeTools.js +1 -1
  99. package/dist/tools/changeTools.js.map +1 -1
  100. package/dist/tools/context.js +23 -3
  101. package/dist/tools/context.js.map +1 -1
  102. package/dist/tools/deploymentTools.js +58 -0
  103. package/dist/tools/deploymentTools.js.map +1 -0
  104. package/dist/tools/directTools.js +9 -5
  105. package/dist/tools/directTools.js.map +1 -1
  106. package/dist/tools/gitTools.js +7 -2
  107. package/dist/tools/gitTools.js.map +1 -1
  108. package/dist/tools/jobTools.js +4 -4
  109. package/dist/tools/jobTools.js.map +1 -1
  110. package/dist/tools/projectTools.js +1 -0
  111. package/dist/tools/projectTools.js.map +1 -1
  112. package/dist/tools/recoveryTools.js +79 -0
  113. package/dist/tools/recoveryTools.js.map +1 -0
  114. package/dist/tools/runtimeTools.js +1 -1
  115. package/dist/tools/runtimeTools.js.map +1 -1
  116. package/dist/tools/surface.js +6 -1
  117. package/dist/tools/surface.js.map +1 -1
  118. package/dist/tunnel/credentials.js +1 -1
  119. package/dist/tunnel/credentials.js.map +1 -1
  120. package/docs/ARCHITECTURE.md +82 -2
  121. package/docs/CI.md +118 -0
  122. package/docs/COMPATIBILITY.md +7 -6
  123. package/docs/EVALUATION.md +17 -12
  124. package/docs/MANUAL_ACCEPTANCE.md +59 -2
  125. package/docs/RECOVERY.md +448 -0
  126. package/docs/RELEASE_1.3.0.md +64 -0
  127. package/docs/RELEASE_NOTES.md +23 -0
  128. package/docs/SECURITY.md +112 -0
  129. package/docs/TEST_REPORT.md +300 -5
  130. package/docs/adr/051-source-recovery-foundation.md +45 -0
  131. package/docs/adr/052-reviewed-source-restore.md +50 -0
  132. package/docs/adr/053-content-drift-and-private-git-recovery.md +29 -0
  133. package/docs/adr/054-verified-source-checkpoints.md +58 -0
  134. package/docs/adr/055-reviewed-docker-deployments.md +42 -0
  135. package/docs/adr/056-owner-data-recovery.md +47 -0
  136. package/docs/adr/README.md +7 -0
  137. package/native/private-state-windows.cs +105 -0
  138. package/package.json +6 -1
  139. package/schemas/global-config.schema.json +34 -0
  140. package/schemas/tools.compact.json +175 -15
  141. package/schemas/tools.hybrid.json +350 -16
  142. package/schemas/tools.json +4883 -816
@@ -0,0 +1 @@
1
+ {"version":3,"file":"databaseAwareness.js","sourceRoot":"","sources":["../../../src/services/recovery/databaseAwareness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ import { pack, extract } from 'tar-stream';
2
+ import { DodoError } from '../../errors.js';
3
+ import { sha256Bytes } from '../../util/hash.js';
4
+ export const DEPLOYMENT_ARCHIVE_BYTES = 32 * 1024 * 1024;
5
+ const safeName = (name) => name.length <= 1024 && !/[\\:\x00-\x1f]/.test(name)
6
+ && name.split('/').every(p => p !== '' && p !== '.' && p !== '..');
7
+ /** Deterministic, bounded bytes from the verified CAS, not a walk of the live workspace. */
8
+ export async function packDeploymentSource(entries, read, assertPath) {
9
+ if (entries.length > 100000)
10
+ throw new DodoError('RESOURCE_LIMIT', 'build context entry budget exceeded');
11
+ if (entries.reduce((sum, e) => sum + Math.ceil(e.bytes / 512) * 512 + 4096, 1024) > DEPLOYMENT_ARCHIVE_BYTES)
12
+ throw new DodoError('RESOURCE_LIMIT', 'build context archive reservation exceeds 32 MiB');
13
+ const stream = pack(), chunks = [];
14
+ let total = 0;
15
+ const completed = new Promise((resolve, reject) => {
16
+ stream.on('data', (raw) => {
17
+ const chunk = Buffer.from(raw);
18
+ total += chunk.length;
19
+ if (total > DEPLOYMENT_ARCHIVE_BYTES) {
20
+ reject(new DodoError('RESOURCE_LIMIT', 'build context archive exceeds 32 MiB'));
21
+ stream.destroy();
22
+ }
23
+ else
24
+ chunks.push(chunk);
25
+ });
26
+ stream.on('error', () => reject(new DodoError('RECOVERY_REQUIRED', 'build context archive failed')));
27
+ stream.on('end', () => resolve(Buffer.concat(chunks)));
28
+ });
29
+ void completed.catch(() => { });
30
+ try {
31
+ const names = new Set();
32
+ for (const e of [...entries].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0)) {
33
+ assertPath(e.path);
34
+ if (!safeName(e.path) || names.has(e.path.toLowerCase()) || e.kind === 'absent')
35
+ throw new DodoError('PATH_DENIED', 'build context contains an ambiguous path');
36
+ names.add(e.path.toLowerCase());
37
+ if (!Number.isSafeInteger(e.bytes) || e.bytes < 0 || total + e.bytes > DEPLOYMENT_ARCHIVE_BYTES)
38
+ throw new DodoError('RESOURCE_LIMIT', 'build context archive exceeds 32 MiB');
39
+ const bytes = e.kind === 'file' ? await read(e) : Buffer.alloc(0);
40
+ if (e.kind === 'file' && (bytes.length !== e.bytes || sha256Bytes(bytes).slice(7) !== e.hash))
41
+ throw new DodoError('RECOVERY_REQUIRED', 'build context source hash differs');
42
+ await new Promise((resolve, reject) => stream.entry({ name: e.path, type: e.kind === 'file' ? 'file' : 'directory', size: bytes.length,
43
+ mode: (e.mode ?? (e.kind === 'file' ? 0o644 : 0o755)) & 0o777, uid: 0, gid: 0, mtime: new Date(0), uname: '', gname: '' }, bytes, error => error ? reject(new DodoError('RECOVERY_REQUIRED', 'build context entry failed')) : resolve()));
44
+ }
45
+ stream.finalize();
46
+ return await completed;
47
+ }
48
+ catch (error) {
49
+ stream.destroy();
50
+ throw error;
51
+ }
52
+ }
53
+ /** No filesystem extraction: untrusted container tar is checked before any source bytes can be used. */
54
+ export async function verifySourceArchive(archive, expected, assertPath, prefix = '') {
55
+ if (archive.length > DEPLOYMENT_ARCHIVE_BYTES)
56
+ throw new DodoError('RESOURCE_LIMIT', 'container source archive exceeds 32 MiB');
57
+ if (prefix && !safeName(prefix))
58
+ throw new DodoError('INVALID_INPUT', 'invalid archive prefix');
59
+ const wanted = new Map(expected.filter(e => e.kind !== 'absent').map(e => [e.path, e]));
60
+ if (!expected.some(e => e.kind === 'file'))
61
+ throw new DodoError('NOT_SUPPORTED', 'no declared source manifest is available');
62
+ const seen = new Set(), seenFolded = new Set(), files = new Map();
63
+ const parser = extract();
64
+ let count = 0, extracted = 0;
65
+ const checked = new Promise((resolve, reject) => {
66
+ const fail = (error) => { reject(error instanceof DodoError ? error : new DodoError('RECOVERY_REQUIRED', 'invalid container source archive')); parser.destroy(); };
67
+ parser.on('error', fail);
68
+ parser.on('finish', () => resolve());
69
+ parser.on('entry', (header, stream, next) => {
70
+ stream.on('error', fail);
71
+ void (async () => {
72
+ if (++count > 100000 || !['file', 'directory'].includes(header.type) || header.linkname)
73
+ throw new DodoError('PATH_DENIED', 'container source links, devices and unsupported entries are refused');
74
+ if (header.name.startsWith('/'))
75
+ throw new DodoError('PATH_DENIED', 'absolute archive path refused');
76
+ if (header.pax && Object.keys(header.pax).some(key => !['path', 'size', 'mtime', 'atime', 'ctime', 'uid', 'gid', 'uname', 'gname'].includes(key)))
77
+ throw new DodoError('PATH_DENIED', 'unsupported source archive metadata');
78
+ let name = header.name.replace(/^\.\//, '').replace(/\/$/, '');
79
+ if ((name === '.' || name === '' || prefix && name === prefix) && header.type === 'directory') {
80
+ if (header.size !== 0)
81
+ throw new DodoError('PATH_DENIED', 'invalid archive root');
82
+ stream.resume();
83
+ stream.on('end', () => next());
84
+ return;
85
+ }
86
+ if (prefix) {
87
+ if (!name.startsWith(prefix + '/'))
88
+ throw new DodoError('PATH_DENIED', 'entry is outside the declared container source mapping');
89
+ name = name.slice(prefix.length + 1);
90
+ }
91
+ if (!safeName(name) || seenFolded.has(name.toLowerCase()))
92
+ throw new DodoError('PATH_DENIED', 'archive traversal, duplicate or ambiguous path refused');
93
+ assertPath(name);
94
+ seenFolded.add(name.toLowerCase());
95
+ seen.add(name);
96
+ const expectedEntry = wanted.get(name);
97
+ if (!expectedEntry || header.type !== (expectedEntry.kind === 'directory' ? 'directory' : 'file'))
98
+ throw new DodoError('RECOVERY_REQUIRED', 'image source coverage differs from its recorded manifest');
99
+ if (!Number.isSafeInteger(header.size) || header.size < 0 || header.size !== expectedEntry.bytes
100
+ || (header.mode & 0o7777) !== expectedEntry.mode)
101
+ throw new DodoError('RECOVERY_REQUIRED', 'image source size or mode differs from its recorded manifest');
102
+ extracted += header.size;
103
+ if (extracted > DEPLOYMENT_ARCHIVE_BYTES)
104
+ throw new DodoError('RESOURCE_LIMIT', 'container source budget exceeded');
105
+ const chunks = [];
106
+ let bytes = 0;
107
+ for await (const raw of stream) {
108
+ const chunk = Buffer.from(raw);
109
+ bytes += chunk.length;
110
+ if (bytes > expectedEntry.bytes)
111
+ throw new DodoError('RESOURCE_LIMIT', 'archive entry exceeds declared bytes');
112
+ chunks.push(chunk);
113
+ }
114
+ const data = Buffer.concat(chunks);
115
+ if (bytes !== expectedEntry.bytes || header.type === 'file' && sha256Bytes(data).slice(7) !== expectedEntry.hash)
116
+ throw new DodoError('RECOVERY_REQUIRED', 'image source hash differs; image labels are not proof');
117
+ if (header.type === 'file')
118
+ files.set(name, data);
119
+ next();
120
+ })().catch(fail);
121
+ });
122
+ });
123
+ parser.end(archive);
124
+ await checked;
125
+ if ([...wanted.keys()].some(name => !seen.has(name)))
126
+ throw new DodoError('NOT_SUPPORTED', 'image source is missing or partial; exact recovery is unavailable');
127
+ return { files, fileCount: files.size, bytes: extracted, complete: true };
128
+ }
129
+ //# sourceMappingURL=deploymentArchive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploymentArchive.js","sourceRoot":"","sources":["../../../src/services/recovery/deploymentArchive.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import { z } from 'zod';
2
+ const name = z.string().min(1).max(64).regex(/^[a-z0-9][a-z0-9_-]*$/);
3
+ export const DeploymentHash = z.string().regex(/^sha256:[a-f0-9]{64}$/);
4
+ // Additional filesystem/secret checks use the project's shared path policy.
5
+ const relative = z.string().min(1).max(1024).refine(p => p === '.' || (!p.startsWith('/') && !/[\\:\x00-\x1f]/.test(p)
6
+ && p.split('/').every(part => part !== '' && part !== '.' && part !== '..')), 'use a canonical relative source path');
7
+ const operation = z.object({
8
+ path: z.string().min(1).max(1024).startsWith('/'),
9
+ method: z.enum(['get', 'post', 'put', 'patch', 'delete', 'head', 'options']),
10
+ responses: z.array(z.string().regex(/^(?:[1-5][0-9]{2}|default)$/)).min(1).max(20),
11
+ // Exact hashes are deliberately conservative: uncertainty never proves compatibility.
12
+ contractHash: DeploymentHash.optional(),
13
+ }).strict();
14
+ export const DeploymentHealthCheck = z.object({
15
+ id: name,
16
+ kind: z.enum(['http', 'openapi']),
17
+ url: z.string().max(2048).url(),
18
+ allowPrivateNetwork: z.boolean().default(false),
19
+ expectedStatus: z.number().int().min(100).max(599).default(200),
20
+ requiredOperations: z.array(operation).max(100).default([]),
21
+ }).strict().refine(c => c.kind === 'openapi' ? c.requiredOperations.length > 0 : c.requiredOperations.length === 0, 'OpenAPI checks require explicit operations; HTTP checks do not accept a contract');
22
+ /** Owner-only input. No credential values, arbitrary argv, privileged flags or repo Compose hooks. */
23
+ export const DeploymentTargetSchema = z.object({
24
+ name,
25
+ adapter: z.literal('docker-compose'),
26
+ dockerContext: name.default('default'),
27
+ composeProject: name,
28
+ service: name,
29
+ contextRoot: relative.default('.'),
30
+ dockerfile: relative.refine(p => p !== '.').default('Dockerfile'),
31
+ buildNetwork: z.enum(['none', 'default']).default('none'),
32
+ sourceMapping: z.object({
33
+ workspaceRoot: relative.default('.'),
34
+ containerRoot: z.string().min(2).max(1024).refine(p => /^\//.test(p) && !/[\\:\x00-\x1f]/.test(p)
35
+ && p.slice(1).split('/').every(v => v !== '' && v !== '.' && v !== '..')
36
+ && !/^\/(?:proc|sys|dev|run|var\/run)(?:\/|$)/.test(p), 'use a dedicated absolute container source directory'),
37
+ }).strict().optional(),
38
+ ports: z.array(z.object({
39
+ host: z.enum(['127.0.0.1', '0.0.0.0', '::1']).default('127.0.0.1'),
40
+ published: z.number().int().min(1024).max(65535),
41
+ target: z.number().int().min(1).max(65535),
42
+ }).strict()).max(20).default([]),
43
+ // Only existing, explicitly named volumes; never bind mounts or automatic data deletion.
44
+ volumes: z.array(z.object({ name, target: z.string().regex(/^\/[a-zA-Z0-9_/-]+$/).max(256) }).strict()).max(10).default([]),
45
+ requiredChecks: z.array(z.object({ taskId: z.string().min(1).max(128), recipeDigest: DeploymentHash }).strict()).min(1).max(20),
46
+ health: z.array(DeploymentHealthCheck).min(1).max(20),
47
+ stabilizationMs: z.number().int().min(1000).max(300000).default(10000),
48
+ intervalMs: z.number().int().min(250).max(10000).default(2000),
49
+ commandTimeoutMs: z.number().int().min(1000).max(1800000).default(300000),
50
+ retainedImages: z.number().int().min(2).max(100).default(5),
51
+ }).strict().superRefine((target, ctx) => {
52
+ for (const [field, values] of [
53
+ ['requiredChecks', target.requiredChecks.map(c => c.taskId)], ['health', target.health.map(c => c.id)],
54
+ ['ports', target.ports.map(p => `${p.host}:${p.published}`)], ['volumes', target.volumes.map(v => v.target)],
55
+ ])
56
+ if (new Set(values).size !== values.length)
57
+ ctx.addIssue({ code: 'custom', path: [field], message: 'duplicate target entry' });
58
+ if (target.intervalMs > target.stabilizationMs)
59
+ ctx.addIssue({ code: 'custom', path: ['intervalMs'], message: 'interval must fit the stabilization window' });
60
+ for (const v of target.volumes) {
61
+ if (v.target.includes('//') || /^\/(?:proc|sys|dev|run)(?:\/|$)/.test(v.target))
62
+ ctx.addIssue({ code: 'custom', path: ['volumes'], message: 'invalid volume destination' });
63
+ if (target.volumes.some(other => other !== v && (other.target.startsWith(v.target + '/') || v.target.startsWith(other.target + '/'))))
64
+ ctx.addIssue({ code: 'custom', path: ['volumes'], message: 'nested volume destinations are not supported' });
65
+ const source = target.sourceMapping?.containerRoot;
66
+ if (source && (source === v.target || source.startsWith(v.target + '/') || v.target.startsWith(source + '/')))
67
+ ctx.addIssue({ code: 'custom', path: ['volumes'], message: 'source recovery mapping must not overlap volume data' });
68
+ }
69
+ });
70
+ //# sourceMappingURL=deploymentContracts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploymentContracts.js","sourceRoot":"","sources":["../../../src/services/recovery/deploymentContracts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;AACtE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;AACxE,4EAA4E;AAC5E,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CACtD,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;OACxD,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,EAC9E,sCAAsC,CAAC,CAAC;AAC1C,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IACzB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;IACjD,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAC5E,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IAClF,sFAAsF;IACtF,YAAY,EAAE,cAAc,CAAC,QAAQ,EAAE;CACxC,CAAC,CAAC,MAAM,EAAE,CAAC;AACZ,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,EAAE,EAAE,IAAI;IACR,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACjC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE;IAC/B,mBAAmB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAC/C,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;IAC/D,kBAAkB,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAC5D,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,MAAM,KAAK,CAAC,EAChH,kFAAkF,CAAC,CAAC;AAEtF,sGAAsG;AACtG,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,IAAI;IACJ,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC;IACpC,aAAa,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;IACtC,cAAc,EAAE,IAAI;IACpB,OAAO,EAAE,IAAI;IACb,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC;IAClC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC;IACjE,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;IACzD,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC;QACtB,aAAa,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC;QACpC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;eAC5F,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC;eACrE,CAAC,0CAA0C,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,qDAAqD,CAAC;KACjH,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACtB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QACtB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;QAClE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;QAChD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;KAC3C,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAChC,yFAAyF;IACzF,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAC3H,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IAC/H,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IACrD,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IACtE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;IAC9D,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;IACzE,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;CAC5D,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;IACtC,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI;QAC5B,CAAC,gBAAgB,EAAE,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACtG,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;KACpG;QAAE,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,MAAM;YAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,wBAAwB,EAAE,CAAC,CAAC;IAC3I,IAAI,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,eAAe;QAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,4CAA4C,EAAE,CAAC,CAAC;IAC9J,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QAC/B,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,iCAAiC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;YAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,4BAA4B,EAAE,CAAC,CAAC;QAC5K,IAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAA,EAAE,CAAA,KAAK,KAAG,CAAC,IAAE,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,GAAC,GAAG,CAAC,IAAE,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,GAAC,GAAG,CAAC,CAAC,CAAC;YACtH,GAAG,CAAC,QAAQ,CAAC,EAAC,IAAI,EAAC,QAAQ,EAAC,IAAI,EAAC,CAAC,SAAS,CAAC,EAAC,OAAO,EAAC,8CAA8C,EAAC,CAAC,CAAC;QACxG,MAAM,MAAM,GAAG,MAAM,CAAC,aAAa,EAAE,aAAa,CAAC;QACnD,IAAI,MAAM,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;YAC3G,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,sDAAsD,EAAE,CAAC,CAAC;IACzH,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,143 @@
1
+ import http from 'node:http';
2
+ import https from 'node:https';
3
+ import dns from 'node:dns/promises';
4
+ import net from 'node:net';
5
+ import { DodoError } from '../../errors.js';
6
+ import { addressClass } from '../../security/outboundAddress.js';
7
+ import { digestOf } from '../../util/hash.js';
8
+ export function validateHealthUrl(check, config) {
9
+ let u;
10
+ try {
11
+ u = new URL(check.url);
12
+ }
13
+ catch {
14
+ throw new DodoError('INVALID_INPUT', 'invalid deployment health URL');
15
+ }
16
+ if (!['http:', 'https:'].includes(u.protocol) || u.username || u.password || u.search || u.hash)
17
+ throw new DodoError('INVALID_INPUT', 'health URL must be HTTP(S), without credentials, query or fragment');
18
+ const port = Number(u.port || (u.protocol === 'https:' ? 443 : 80));
19
+ if ([21730, 21731, 21732, config.port, config.configPort, ...(config.controlPorts ?? [])].includes(port)
20
+ || (config.publicUrl && u.origin === new URL(config.publicUrl).origin))
21
+ throw new DodoError('FORBIDDEN', 'DODO endpoints cannot be deployment health targets');
22
+ const host = u.hostname.replace(/^\[|\]$/g, '');
23
+ if (host === 'metadata.google.internal' || host.endsWith('.metadata.google.internal'))
24
+ throw new DodoError('FORBIDDEN', 'metadata endpoints cannot be health targets');
25
+ if (u.protocol === 'http:' && !check.allowPrivateNetwork)
26
+ throw new DodoError('FORBIDDEN', 'HTTP health requires explicit owner permission for this private target');
27
+ if (net.isIP(host))
28
+ validateHealthAddress(host, check, u);
29
+ return u;
30
+ }
31
+ export function validateHealthAddress(ip, check, url) {
32
+ const type = addressClass(ip);
33
+ if (!net.isIP(ip) || type === 'denied' || (type === 'private' && !check.allowPrivateNetwork) || (type === 'public' && url.protocol !== 'https:'))
34
+ throw new DodoError('FORBIDDEN', 'deployment health address is not permitted');
35
+ }
36
+ /** A bounded GET with a pinned, pre-validated DNS result. No auth, cookies, redirect or retries. */
37
+ export async function readHealth(check, config, revalidate, signal) {
38
+ const url = validateHealthUrl(check, config), host = url.hostname.replace(/^\[|\]$/g, '');
39
+ const deadline = AbortSignal.timeout(10000), combined = signal ? AbortSignal.any([signal, deadline]) : deadline;
40
+ combined.throwIfAborted();
41
+ revalidate();
42
+ // dns.lookup has no AbortSignal: cap waiting and never send a late result after expiry.
43
+ let abort;
44
+ const addresses = await Promise.race([
45
+ net.isIP(host) ? Promise.resolve([{ address: host, family: net.isIP(host) }]) : dns.lookup(host, { all: true }),
46
+ new Promise((_resolve, reject) => { abort = () => reject(new DodoError('TIMEOUT', 'deployment health DNS deadline expired')); combined.addEventListener('abort', abort, { once: true }); }),
47
+ ]).catch(() => { throw new DodoError('CONFLICT', 'deployment health DNS unavailable; no request sent'); })
48
+ .finally(() => { if (abort)
49
+ combined.removeEventListener('abort', abort); });
50
+ if (!addresses.length)
51
+ throw new DodoError('NOT_SUPPORTED', 'deployment health hostname has no address');
52
+ for (const entry of addresses)
53
+ validateHealthAddress(entry.address, check, url);
54
+ const chosen = addresses.find(a => a.family === 4) ?? addresses[0];
55
+ combined.throwIfAborted();
56
+ revalidate();
57
+ return new Promise((resolve, reject) => {
58
+ const req = (url.protocol === 'https:' ? https : http).request(url, {
59
+ method: 'GET', headers: { accept: 'application/json', 'accept-encoding': 'identity' }, agent: false, signal: combined,
60
+ lookup: (_host, options, callback) => {
61
+ if (typeof options === 'object' && options.all)
62
+ callback(null, [chosen]);
63
+ else
64
+ callback(null, chosen.address, chosen.family);
65
+ },
66
+ }, response => {
67
+ const status = response.statusCode ?? 0;
68
+ if (status >= 300 && status < 400 || response.headers['content-encoding'] && response.headers['content-encoding'] !== 'identity') {
69
+ reject(new DodoError('FORBIDDEN', 'health redirects and compressed responses are refused'));
70
+ response.destroy();
71
+ return;
72
+ }
73
+ const chunks = [];
74
+ let size = 0;
75
+ response.on('data', (chunk) => {
76
+ size += chunk.length;
77
+ if (size > 2 * 1024 * 1024) {
78
+ reject(new DodoError('RESOURCE_LIMIT', 'deployment health response exceeds its limit'));
79
+ response.destroy();
80
+ }
81
+ else
82
+ chunks.push(chunk);
83
+ });
84
+ response.on('aborted', () => reject(new DodoError('CONFLICT', 'deployment health response was interrupted')));
85
+ response.on('error', () => reject(new DodoError('CONFLICT', 'deployment health response failed')));
86
+ response.on('end', () => { try {
87
+ revalidate();
88
+ resolve({ status, body: Buffer.concat(chunks) });
89
+ }
90
+ catch (error) {
91
+ reject(error);
92
+ } });
93
+ });
94
+ req.on('error', () => reject(new DodoError('CONFLICT', 'deployment health request failed; not retried')));
95
+ req.end();
96
+ });
97
+ }
98
+ function object(value) {
99
+ return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
100
+ }
101
+ /** Only owner-declared operation presence/statuses or exact contracts are proven, never all semantic compatibility. */
102
+ export function evaluateHealth(check, response) {
103
+ let passed = response.status === check.expectedStatus, reason = passed ? 'expected_status' : 'unexpected_status';
104
+ let contractDigest = null;
105
+ if (passed && check.kind === 'openapi') {
106
+ let spec;
107
+ try {
108
+ spec = object(JSON.parse(response.body.toString('utf8')));
109
+ }
110
+ catch { /* sanitized below */ }
111
+ const paths = object(spec?.['paths']);
112
+ passed = Boolean(paths && typeof spec?.['openapi'] === 'string' && /^3\.[01]\./.test(spec['openapi']));
113
+ const selected = [];
114
+ for (const expected of check.requiredOperations) {
115
+ const operation = object(object(paths?.[expected.path])?.[expected.method]), responses = object(operation?.['responses']);
116
+ if (!operation || operation['$ref'] || !responses || expected.responses.some(code => !Object.hasOwn(responses, code)))
117
+ passed = false;
118
+ else {
119
+ selected.push(operation);
120
+ if (expected.contractHash) {
121
+ // The entire document participates so a changed referenced component cannot pass unnoticed.
122
+ try {
123
+ if (digestOf({ operation, components: spec?.['components'] ?? {} }) !== expected.contractHash)
124
+ passed = false;
125
+ }
126
+ catch {
127
+ passed = false;
128
+ }
129
+ }
130
+ }
131
+ }
132
+ try {
133
+ contractDigest = digestOf(selected);
134
+ }
135
+ catch {
136
+ passed = false;
137
+ }
138
+ reason = passed ? 'declared_contract_matched' : 'contract_missing_incompatible_or_unknown';
139
+ }
140
+ return { checkId: check.id, kind: check.kind, passed, reason, status: response.status, contractDigest,
141
+ checkedAt: Date.now(), coverage: check.kind === 'openapi' ? 'owner_declared_operations_only' : 'expected_http_status_only' };
142
+ }
143
+ //# sourceMappingURL=deploymentHealth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploymentHealth.js","sourceRoot":"","sources":["../../../src/services/recovery/deploymentHealth.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,GAAG,MAAM,mBAAmB,CAAC;AACpC,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAI9C,MAAM,UAAU,iBAAiB,CAAC,KAAkB,EAAE,MAAqB;IACzE,IAAI,CAAM,CAAC;IACX,IAAI,CAAC;QAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,MAAM,IAAI,SAAS,CAAC,eAAe,EAAE,+BAA+B,CAAC,CAAC;IAAC,CAAC;IAChH,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI;QAC7F,MAAM,IAAI,SAAS,CAAC,eAAe,EAAE,oEAAoE,CAAC,CAAC;IAC7G,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACpE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;WACnG,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC;QACtE,MAAM,IAAI,SAAS,CAAC,WAAW,EAAE,oDAAoD,CAAC,CAAC;IACzF,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAChD,IAAI,IAAI,KAAK,0BAA0B,IAAI,IAAI,CAAC,QAAQ,CAAC,2BAA2B,CAAC;QACnF,MAAM,IAAI,SAAS,CAAC,WAAW,EAAE,6CAA6C,CAAC,CAAC;IAClF,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,mBAAmB;QAAE,MAAM,IAAI,SAAS,CAAC,WAAW,EAAE,wEAAwE,CAAC,CAAC;IACrK,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,qBAAqB,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAC1D,OAAO,CAAC,CAAC;AACX,CAAC;AACD,MAAM,UAAU,qBAAqB,CAAC,EAAU,EAAE,KAAkB,EAAE,GAAQ;IAC5E,MAAM,IAAI,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC;QAC9I,MAAM,IAAI,SAAS,CAAC,WAAW,EAAE,4CAA4C,CAAC,CAAC;AACnF,CAAC;AAED,oGAAoG;AACpG,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAkB,EAAE,MAAqB,EAAE,UAAsB,EAAE,MAAoB;IACtH,MAAM,GAAG,GAAG,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC1F,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAChH,QAAQ,CAAC,cAAc,EAAE,CAAC;IAAC,UAAU,EAAE,CAAC;IACxC,wFAAwF;IACxF,IAAI,KAA+B,CAAC;IACpC,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;QACnC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;QAC/G,IAAI,OAAO,CAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,GAAG,KAAK,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,SAAS,EAAE,wCAAwC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;KACnM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,MAAM,IAAI,SAAS,CAAC,UAAU,EAAE,oDAAoD,CAAC,CAAC,CAAC,CAAC,CAAC;SACvG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,KAAK;QAAE,QAAQ,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/E,IAAI,CAAC,SAAS,CAAC,MAAM;QAAE,MAAM,IAAI,SAAS,CAAC,eAAe,EAAE,2CAA2C,CAAC,CAAC;IACzG,KAAK,MAAM,KAAK,IAAI,SAAS;QAAE,qBAAqB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IAChF,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAE,CAAC;IACpE,QAAQ,CAAC,cAAc,EAAE,CAAC;IAAC,UAAU,EAAE,CAAC;IACxC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE;YAClE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ;YACrH,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;gBACnC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,GAAG;oBAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;;oBAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YAC/H,CAAC;SACF,EAAE,QAAQ,CAAC,EAAE;YACZ,MAAM,MAAM,GAAG,QAAQ,CAAC,UAAU,IAAI,CAAC,CAAC;YACxC,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,OAAO,CAAC,kBAAkB,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,kBAAkB,CAAC,KAAK,UAAU,EAAE,CAAC;gBACjI,MAAM,CAAC,IAAI,SAAS,CAAC,WAAW,EAAE,uDAAuD,CAAC,CAAC,CAAC;gBAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;gBAAC,OAAO;YAC1H,CAAC;YACD,MAAM,MAAM,GAAa,EAAE,CAAC;YAAC,IAAI,IAAI,GAAG,CAAC,CAAC;YAC1C,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gBACpC,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC;gBACrB,IAAI,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;oBAAC,MAAM,CAAC,IAAI,SAAS,CAAC,gBAAgB,EAAE,8CAA8C,CAAC,CAAC,CAAC;oBAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;gBAAC,CAAC;;oBACvI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,CAAC,CAAC,CAAC;YACH,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,UAAU,EAAE,4CAA4C,CAAC,CAAC,CAAC,CAAC;YAC9G,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,UAAU,EAAE,mCAAmC,CAAC,CAAC,CAAC,CAAC;YACnG,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;gBAAC,UAAU,EAAE,CAAC;gBAAC,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAAC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACzI,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,UAAU,EAAE,+CAA+C,CAAC,CAAC,CAAC,CAAC;QAC1G,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,MAAM,CAAC,KAAc;IAC5B,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAgC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7H,CAAC;AACD,uHAAuH;AACvH,MAAM,UAAU,cAAc,CAAC,KAAkB,EAAE,QAA0C;IAC3F,IAAI,MAAM,GAAG,QAAQ,CAAC,MAAM,KAAK,KAAK,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,mBAAmB,CAAC;IACjH,IAAI,cAAc,GAAkB,IAAI,CAAC;IACzC,IAAI,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACvC,IAAI,IAAyC,CAAC;QAC9C,IAAI,CAAC;YAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,CAAC;QAClG,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QACtC,MAAM,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,IAAI,EAAE,CAAC,SAAS,CAAC,KAAK,QAAQ,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QACvG,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,kBAAkB,EAAE,CAAC;YAChD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;YAC1H,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;gBAAE,MAAM,GAAG,KAAK,CAAC;iBACjI,CAAC;gBACJ,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACzB,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;oBAC1B,4FAA4F;oBAC5F,IAAI,CAAC;wBAAC,IAAI,QAAQ,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,QAAQ,CAAC,YAAY;4BAAE,MAAM,GAAG,KAAK,CAAC;oBAAC,CAAC;oBACtH,MAAM,CAAC;wBAAC,MAAM,GAAG,KAAK,CAAC;oBAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;QACH,CAAC;QACD,IAAI,CAAC;YAAC,cAAc,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,MAAM,GAAG,KAAK,CAAC;QAAC,CAAC;QACtE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,0CAA0C,CAAC;IAC7F,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,cAAc;QACnG,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,gCAAgC,CAAC,CAAC,CAAC,2BAA2B,EAAE,CAAC;AACjI,CAAC"}
@@ -0,0 +1,197 @@
1
+ import { z } from 'zod';
2
+ import { DodoError } from '../../errors.js';
3
+ import { digestOf, newId } from '../../util/hash.js';
4
+ const id = z.string().min(1).max(128);
5
+ export const DeploymentMaintenanceInput = z.discriminatedUnion('action', [
6
+ z.object({ action: z.literal('pin'), deploymentId: id, pinned: z.boolean(), expectedPinned: z.boolean() }).strict(),
7
+ z.object({ action: z.literal('resolve_preview'), deploymentId: id }).strict(),
8
+ z.object({ action: z.literal('image_preview'), deploymentId: id }).strict(),
9
+ z.object({ action: z.literal('probe_preview'), deploymentId: id }).strict(),
10
+ z.object({ action: z.literal('cleanup_preview'), targetId: id }).strict(),
11
+ z.object({ action: z.literal('apply'), reviewId: id, reviewHash: z.string().regex(/^sha256:[a-f0-9]{64}$/) }).strict(),
12
+ ]);
13
+ /** Private owner administration only. Preview is durable; apply reobserves and
14
+ * binds the exact preview. Interrupted effects remain UNKNOWN, never replayed. */
15
+ export class DeploymentMaintenance {
16
+ s;
17
+ revalidate;
18
+ inspect;
19
+ target;
20
+ adapter;
21
+ constructor(s, revalidate, inspect, target, adapter) {
22
+ this.s = s;
23
+ this.revalidate = revalidate;
24
+ this.inspect = inspect;
25
+ this.target = target;
26
+ this.adapter = adapter;
27
+ }
28
+ get db() { return this.s.store.db; }
29
+ meta(id) {
30
+ return this.db.prepare('SELECT * FROM recovery_deployment_maintenance WHERE deployment_id=?').get(id)
31
+ ?? { pinned: 0, retired: 0, probe_json: null, resolution_json: null, cleanup_json: null };
32
+ }
33
+ assertIdle(id) {
34
+ if (this.db.prepare("SELECT 1 FROM recovery_deployment_jobs d JOIN jobs j ON j.id=d.job_id WHERE d.deployment_id=? AND j.status='running' LIMIT 1").get(id))
35
+ throw new DodoError('CONFLICT', 'deployment jobs are still running; wait or explicitly cancel them before maintenance');
36
+ }
37
+ async observation(kind, reference) {
38
+ this.revalidate();
39
+ if (kind === 'cleanup')
40
+ return this.cleanup(reference);
41
+ const record = this.inspect(reference), target = this.target(record.plan.targetId);
42
+ this.assertIdle(reference);
43
+ if (target.hash !== record.plan.targetHash)
44
+ throw new DodoError('CONFLICT', 'registered target changed; do not inspect an unrelated Docker endpoint');
45
+ const adapter = this.adapter(record, target), meta = this.meta(reference);
46
+ if (kind === 'image') {
47
+ if (!record.imageDigest || !meta.cleanup_json)
48
+ throw new DodoError('NOT_FOUND', 'no image cleanup receipt to inspect');
49
+ return { targetHash: target.hash, image: record.imageDigest, actual: await adapter.imageInventory(record.imageDigest) };
50
+ }
51
+ if (kind === 'probe') {
52
+ if (!meta.probe_json)
53
+ throw new DodoError('NOT_FOUND', 'no recorded source probe');
54
+ const probe = JSON.parse(meta.probe_json);
55
+ return { targetHash: target.hash, probe, actual: await adapter.probeObservation(probe) };
56
+ }
57
+ if (record.state !== 'UNKNOWN' || meta.resolution_json)
58
+ throw new DodoError('CONFLICT', 'this deployment has no unresolved uncertain outcome');
59
+ return { targetHash: target.hash, planHash: record.planHash, containers: await adapter.containers(),
60
+ image: record.imageDigest ? await adapter.imageInventory(record.imageDigest) : null,
61
+ probe: meta.probe_json ? await adapter.probeObservation(JSON.parse(meta.probe_json)) : null,
62
+ action: 'acknowledge_uncertainty_without_retry_or_health_claim', previousKnownGoodUnchanged: true };
63
+ }
64
+ async cleanup(targetId) {
65
+ const target = this.target(targetId);
66
+ const rows = this.db.prepare('SELECT id FROM recovery_deployments WHERE target_id=? ORDER BY created_at DESC,id DESC LIMIT 501').all(targetId);
67
+ if (rows.length > 500)
68
+ throw new DodoError('RESOURCE_LIMIT', 'deployment history exceeds the bounded maintenance limit');
69
+ const records = rows.map(row => this.inspect(row.id));
70
+ if (records.some(r => r.imageDigest && !this.meta(r.plan.deploymentId).retired && r.plan.targetHash !== target.hash))
71
+ throw new DodoError('CONFLICT', 'image history binds an earlier target definition; restore its reviewed definition before cleanup');
72
+ const pointer = this.db.prepare('SELECT deployment_id,previous_id,revision FROM recovery_production_pointers WHERE target_id=?').get(targetId);
73
+ const groups = new Map();
74
+ for (const r of records)
75
+ if (r.imageDigest && !this.meta(r.plan.deploymentId).retired)
76
+ groups.set(r.imageDigest, [...(groups.get(r.imageDigest) ?? []), r]);
77
+ const keep = new Set([...groups.keys()].slice(0, target.definition.retainedImages));
78
+ const items = [];
79
+ for (const [image, refs] of groups) {
80
+ const adapter = this.adapter(refs[0], target), inventory = await adapter.imageInventory(image), reasons = new Set();
81
+ if (keep.has(image))
82
+ reasons.add('retention_budget');
83
+ if (inventory.used)
84
+ reasons.add('container_reference');
85
+ const managedTags = refs.filter(r => !r.plan.rollback).map(r => `dodo-recovery:${r.plan.deploymentId}`);
86
+ if (inventory.tags.some(tag => !managedTags.includes(tag)))
87
+ reasons.add('unowned_tag');
88
+ for (const r of refs) {
89
+ const deploymentId = r.plan.deploymentId, meta = this.meta(deploymentId);
90
+ this.assertIdle(deploymentId);
91
+ if (meta.pinned)
92
+ reasons.add('owner_pin');
93
+ if (meta.cleanup_json)
94
+ reasons.add('previous_cleanup_outcome_requires_inspection');
95
+ if (pointer?.deployment_id === deploymentId || pointer?.previous_id === deploymentId)
96
+ reasons.add('known_good_pointer');
97
+ if (['BUILDING', 'DEPLOYING', 'HEALTH_CHECKING'].includes(r.state) || (r.state === 'UNKNOWN' && !meta.resolution_json))
98
+ reasons.add('unfinished_outcome');
99
+ if (r.state === 'BUILT' && r.plan.expiresAt > Date.now())
100
+ reasons.add('live_deployment_plan');
101
+ }
102
+ // Another project/target may use the same image ID. No cross-target purge.
103
+ if (this.db.prepare('SELECT 1 FROM recovery_deployments WHERE image_digest=? AND target_id<>? LIMIT 1').get(image, targetId))
104
+ reasons.add('other_target_reference');
105
+ items.push({ image, deployments: refs.map(r => r.plan.deploymentId), ...inventory, eligible: inventory.present && !reasons.size, reasons: [...reasons].sort() });
106
+ }
107
+ return { targetId, targetHash: target.hash, pointerRevision: pointer?.revision ?? 0, retainedImages: target.definition.retainedImages,
108
+ items, externalImagesIncluded: false, deletesVolumes: false };
109
+ }
110
+ async execute(raw) {
111
+ const input = DeploymentMaintenanceInput.parse(raw);
112
+ this.revalidate();
113
+ if (input.action === 'pin') {
114
+ this.inspect(input.deploymentId);
115
+ const before = this.meta(input.deploymentId);
116
+ if (Boolean(before.pinned) !== input.expectedPinned || before.retired)
117
+ throw new DodoError('CONFLICT', 'image pin changed or image was retired');
118
+ this.revalidate();
119
+ this.db.prepare('INSERT INTO recovery_deployment_maintenance(deployment_id,pinned) VALUES (?,?) ON CONFLICT(deployment_id) DO UPDATE SET pinned=excluded.pinned')
120
+ .run(input.deploymentId, Number(input.pinned));
121
+ this.audit(input.deploymentId, 'pin', digestOf(input));
122
+ return { deploymentId: input.deploymentId, pinned: input.pinned };
123
+ }
124
+ if (input.action !== 'apply') {
125
+ const kind = input.action === 'cleanup_preview' ? 'cleanup' : input.action === 'probe_preview' ? 'probe' : input.action === 'image_preview' ? 'image' : 'resolve';
126
+ const reference = 'targetId' in input ? input.targetId : input.deploymentId;
127
+ const review = { kind, reference, epoch: this.s.epoch, observation: await this.observation(kind, reference) };
128
+ this.revalidate();
129
+ // Expired completed previews contain no source/credentials; bound owner review storage.
130
+ this.db.prepare('DELETE FROM recovery_deployment_reviews WHERE workspace_id=? AND expires_at<? AND result_json IS NULL').run(this.s.workspaceId, Date.now());
131
+ if (this.db.prepare('SELECT COUNT(*) AS n FROM recovery_deployment_reviews WHERE workspace_id=?').get(this.s.workspaceId).n >= 2000)
132
+ throw new DodoError('RESOURCE_LIMIT', 'deployment review limit reached');
133
+ const reviewId = newId('deployreview'), reviewHash = digestOf(review), expiresAt = Date.now() + 10 * 60000;
134
+ this.db.prepare('INSERT INTO recovery_deployment_reviews VALUES (?,?,?,?,?,?,NULL)').run(reviewId, this.s.workspaceId, kind, JSON.stringify(review), reviewHash, expiresAt);
135
+ return { reviewId, reviewHash, expiresAt, ...review };
136
+ }
137
+ const row = this.db.prepare('SELECT * FROM recovery_deployment_reviews WHERE id=? AND workspace_id=?').get(input.reviewId, this.s.workspaceId);
138
+ if (!row)
139
+ throw new DodoError('NOT_FOUND', 'owner review unavailable');
140
+ const review = JSON.parse(row.payload);
141
+ if (digestOf(review) !== row.digest || input.reviewHash !== row.digest)
142
+ throw new DodoError('PLAN_HASH_MISMATCH', 'review the exact maintenance plan');
143
+ if (row.result_json)
144
+ return { ...JSON.parse(row.result_json), replayed: true };
145
+ if (review.epoch !== this.s.epoch || row.expires_at < Date.now())
146
+ throw new DodoError('PLAN_EXPIRED', 'maintenance review expired; inspect again');
147
+ const actual = await this.observation(review.kind, review.reference);
148
+ if (digestOf(actual) !== digestOf(review.observation))
149
+ throw new DodoError('CONFLICT', 'live deployment state changed since maintenance preview');
150
+ this.revalidate();
151
+ const finish = (value) => {
152
+ this.db.prepare('UPDATE recovery_deployment_reviews SET result_json=? WHERE id=?').run(JSON.stringify(value), input.reviewId);
153
+ return value;
154
+ };
155
+ // Commit uncertain receipt BEFORE any daemon command. A retry only returns it.
156
+ finish({ state: 'UNKNOWN', retryAllowed: false, reviewId: input.reviewId });
157
+ try {
158
+ if (review.kind === 'resolve') {
159
+ this.db.prepare('INSERT INTO recovery_deployment_maintenance(deployment_id,resolution_json) VALUES (?,?) ON CONFLICT(deployment_id) DO UPDATE SET resolution_json=excluded.resolution_json')
160
+ .run(review.reference, JSON.stringify({ reviewId: input.reviewId, acknowledgedAt: Date.now(), historicalOutcome: 'UNKNOWN', noRetry: true }));
161
+ }
162
+ else if (review.kind === 'image') {
163
+ const observed = actual;
164
+ this.db.prepare('UPDATE recovery_deployment_maintenance SET retired=?,cleanup_json=? WHERE deployment_id=?').run(Number(!observed.actual.present), observed.actual.present ? null : JSON.stringify({ state: 'REMOVAL_OBSERVED', reviewId: input.reviewId }), review.reference);
165
+ }
166
+ else if (review.kind === 'probe') {
167
+ const r = this.inspect(review.reference);
168
+ await this.adapter(r, this.target(r.plan.targetId)).removeProbe(actual.probe);
169
+ }
170
+ else {
171
+ const data = actual;
172
+ for (const item of data.items.filter(i => i.eligible)) {
173
+ this.revalidate();
174
+ const record = this.inspect(item.deployments[0]);
175
+ for (const id of item.deployments)
176
+ this.db.prepare('INSERT INTO recovery_deployment_maintenance(deployment_id,cleanup_json) VALUES (?,?) ON CONFLICT(deployment_id) DO UPDATE SET cleanup_json=excluded.cleanup_json')
177
+ .run(id, JSON.stringify({ reviewId: input.reviewId, state: 'UNKNOWN', retryAllowed: false }));
178
+ await this.adapter(record, this.target(data.targetId)).removeImage(item.image, item.tags);
179
+ this.revalidate();
180
+ for (const id of item.deployments)
181
+ this.db.prepare('UPDATE recovery_deployment_maintenance SET retired=1,cleanup_json=? WHERE deployment_id=?')
182
+ .run(JSON.stringify({ reviewId: input.reviewId, state: 'REMOVED' }), id);
183
+ }
184
+ }
185
+ this.revalidate();
186
+ this.audit(input.reviewId, review.kind, row.digest);
187
+ return finish({ state: 'COMPLETED', kind: review.kind, productionHealthClaim: false, reviewId: input.reviewId, replayed: false });
188
+ }
189
+ catch (e) {
190
+ return finish({ state: 'UNKNOWN', reviewId: input.reviewId, errorCode: e instanceof DodoError ? e.code : 'INTERNAL_ERROR', retryAllowed: false });
191
+ }
192
+ }
193
+ audit(id, action, digest) {
194
+ this.s.store.audit({ principal: 'local-config-owner', workspaceId: this.s.workspaceId, tool: `deployment.maintenance.${action}`, refId: id, inputDigest: digest, result: 'owner_reviewed' });
195
+ }
196
+ }
197
+ //# sourceMappingURL=deploymentMaintenance.js.map