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,361 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createCipheriv, createDecipheriv, createHmac, randomBytes } from 'node:crypto';
4
+ import { z } from 'zod';
5
+ import { DodoError } from '../../errors.js';
6
+ import { digestOf, newId } from '../../util/hash.js';
7
+ import { assertPrivatePath } from '../../platform/privateFs.js';
8
+ import { rootIdentity } from './storage.js';
9
+ import { OSRecoveryKeys } from './configKeys.js';
10
+ const MAX_BYTES = 1024 * 1024;
11
+ const Definition = z.object({ name: z.string().min(1).max(80), path: z.string().min(1).max(1024),
12
+ retention: z.number().int().min(2).max(100).default(10) }).strict();
13
+ const fileIdentity = (s) => `${s.dev}:${s.ino}:${s.birthtimeMs}`;
14
+ /** Explicit owner-only encrypted private-file backups. No source-tool/resource
15
+ * export, plaintext state, session-key fallback, or automatic config reload. */
16
+ export class RecoveryConfigVault {
17
+ s;
18
+ r;
19
+ keys;
20
+ constructor(s, r, keys) {
21
+ this.s = s;
22
+ this.r = r;
23
+ this.keys = keys ?? new OSRecoveryKeys(s.wfs.root);
24
+ }
25
+ get db() { return this.s.store.db; }
26
+ target(id) {
27
+ this.r.assertProject();
28
+ const row = this.db.prepare('SELECT * FROM recovery_config_targets WHERE id=? AND workspace_id=?').get(id, this.s.workspaceId);
29
+ if (!row)
30
+ throw new DodoError('NOT_FOUND', 'private config target unavailable');
31
+ return row;
32
+ }
33
+ definition(target) { return Definition.parse(JSON.parse(target.payload)); }
34
+ resolve(target) {
35
+ this.r.assertProject();
36
+ const rel = this.definition(target).path;
37
+ if (this.s.wfs.normalizeRel(rel) !== rel || !this.s.wfs.ignores.isSecret(rel) || this.s.wfs.ignores.isProtected(rel))
38
+ throw new DodoError('PATH_DENIED', 'config backup accepts only explicit project paths denied to source tools');
39
+ let absolute = this.s.wfs.root;
40
+ for (const [i, name] of rel.split('/').entries()) {
41
+ absolute = path.join(absolute, name);
42
+ const st = fs.lstatSync(absolute), real = fs.realpathSync.native(absolute);
43
+ if (st.isSymbolicLink() || (process.platform === 'win32' ? real.toLowerCase() !== absolute.toLowerCase() : real !== absolute)
44
+ || (i < rel.split('/').length - 1 && !st.isDirectory()))
45
+ throw new DodoError('PATH_DENIED', 'private config path contains a link or alias');
46
+ }
47
+ // Owner must keep the original secret file private too. Never chmod the
48
+ // workspace or silently grant another user access during restore.
49
+ const stat = assertPrivatePath(absolute);
50
+ if (stat.size > MAX_BYTES)
51
+ throw new DodoError('RESOURCE_LIMIT', 'private config exceeds 1 MiB');
52
+ return { absolute, stat };
53
+ }
54
+ read(target) {
55
+ const file = this.resolve(target), fd = fs.openSync(file.absolute, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
56
+ let bytes, returned = false;
57
+ try {
58
+ const before = fs.fstatSync(fd);
59
+ if (fileIdentity(before) !== fileIdentity(file.stat) || before.nlink !== 1 || before.size > MAX_BYTES)
60
+ throw new DodoError('PATH_DENIED', 'private config identity changed');
61
+ bytes = Buffer.alloc(before.size);
62
+ let n = 0;
63
+ while (n < bytes.length) {
64
+ const count = fs.readSync(fd, bytes, n, bytes.length - n, n);
65
+ if (!count)
66
+ break;
67
+ n += count;
68
+ }
69
+ const after = fs.fstatSync(fd), live = this.resolve(target).stat;
70
+ if (n !== bytes.length || before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs || fileIdentity(live) !== fileIdentity(before)) {
71
+ bytes.fill(0);
72
+ throw new DodoError('FILE_CHANGED', 'private config changed while reading');
73
+ }
74
+ returned = true;
75
+ return { bytes, stat: after };
76
+ }
77
+ finally {
78
+ if (!returned)
79
+ bytes?.fill(0);
80
+ fs.closeSync(fd);
81
+ }
82
+ }
83
+ async createKey(revalidate) {
84
+ const reference = 'recoverykey_' + randomBytes(16).toString('hex'), key = randomBytes(32);
85
+ try {
86
+ revalidate();
87
+ await this.keys.put(reference, key);
88
+ revalidate();
89
+ return reference;
90
+ }
91
+ catch (e) {
92
+ try {
93
+ await this.keys.delete(reference);
94
+ }
95
+ catch { /* dedicated orphan reference only; never delete an older key */ }
96
+ throw e;
97
+ }
98
+ finally {
99
+ key.fill(0);
100
+ }
101
+ }
102
+ list() {
103
+ this.r.assertProject();
104
+ const targets = this.db.prepare('SELECT * FROM recovery_config_targets WHERE workspace_id=? ORDER BY id').all(this.s.workspaceId)
105
+ .map(t => ({ id: t.id, revision: t.revision, enabled: Boolean(t.enabled), definition: this.definition(t), keyStorage: 'os', plaintextExport: false }));
106
+ const backups = this.db.prepare('SELECT id,target_id,bytes,created_at FROM recovery_config_backups WHERE workspace_id=? ORDER BY created_at DESC LIMIT 100').all(this.s.workspaceId);
107
+ const restores = this.db.prepare('SELECT id,target_id,snapshot_id,before_id,state FROM recovery_config_restores WHERE workspace_id=? ORDER BY rowid DESC LIMIT 100').all(this.s.workspaceId);
108
+ return { targets, backups, restores, sourceBackupIncludesSecrets: false, automaticRestart: false,
109
+ retention: 'unreferenced_backups_only', referencedBackupsProtected: true, quotaBytes: 32 * 1024 * 1024 };
110
+ }
111
+ async configure(raw, revalidate) {
112
+ const input = z.object({ targetId: z.string().max(128).optional(), expectedRevision: z.number().int().nonnegative(), enabled: z.boolean(),
113
+ definition: Definition, confirmEncryptedPrivateBackup: z.literal(true) }).strict().parse(raw);
114
+ return this.s.mutations.run(async () => {
115
+ this.r.assertProject();
116
+ revalidate();
117
+ const old = input.targetId ? this.target(input.targetId) : undefined;
118
+ if ((old?.revision ?? 0) !== input.expectedRevision)
119
+ throw new DodoError('CONFLICT', 'private config target revision changed');
120
+ if (!old && this.list().targets.length >= 10)
121
+ throw new DodoError('RESOURCE_LIMIT', 'private config target limit reached');
122
+ if (old && this.definition(old).path !== input.definition.path)
123
+ throw new DodoError('CONFLICT', 'create a separate target for a different private file');
124
+ const target = { id: old?.id ?? newId('configtarget'), workspace_id: this.s.workspaceId, revision: input.expectedRevision + 1,
125
+ enabled: Number(input.enabled), payload: JSON.stringify(input.definition), key_ref: old?.key_ref ?? '' };
126
+ // Disabling an already registered missing file must remain possible.
127
+ if (input.enabled || !old)
128
+ this.resolve(target);
129
+ if (!target.key_ref)
130
+ target.key_ref = await this.createKey(revalidate);
131
+ else if (input.enabled) {
132
+ const key = await this.keys.get(target.key_ref);
133
+ key.fill(0);
134
+ }
135
+ revalidate();
136
+ this.db.prepare(`INSERT INTO recovery_config_targets VALUES (?,?,?,?,?,?) ON CONFLICT(id) DO UPDATE SET revision=excluded.revision,
137
+ enabled=excluded.enabled,payload=excluded.payload`).run(target.id, target.workspace_id, target.revision, target.enabled, target.payload, target.key_ref);
138
+ // Owner lowering the separate retention can release ciphertext quota.
139
+ // Plans/receipts always retain their source and pre-restore backups.
140
+ this.prune(target);
141
+ this.audit('configure', target.id, digestOf(input));
142
+ return { id: target.id, revision: target.revision, enabled: input.enabled, keyStorage: 'os' };
143
+ });
144
+ }
145
+ async seal(target, bytes, revalidate) {
146
+ const used = this.db.prepare('SELECT COALESCE(SUM(bytes),0) n FROM recovery_config_backups WHERE workspace_id=?').get(this.s.workspaceId).n;
147
+ if (used + bytes.length > 32 * 1024 * 1024)
148
+ throw new DodoError('RESOURCE_LIMIT', 'encrypted private config quota reached; review retention first');
149
+ const id = newId('configbackup'), key = await this.keys.get(target.key_ref), iv = randomBytes(12);
150
+ const header = { version: 1, id, workspaceId: this.s.workspaceId, root: rootIdentity(this.s.wfs.root), targetId: target.id, targetRevision: target.revision, keyRef: target.key_ref };
151
+ try {
152
+ revalidate();
153
+ const cipher = createCipheriv('aes-256-gcm', key, iv);
154
+ cipher.setAAD(Buffer.from(JSON.stringify(header)));
155
+ const encrypted = Buffer.concat([cipher.update(bytes), cipher.final()]);
156
+ const sealed = JSON.stringify({ header, iv: iv.toString('base64'), tag: cipher.getAuthTag().toString('base64'), ciphertext: encrypted.toString('base64') });
157
+ revalidate();
158
+ this.db.prepare('INSERT INTO recovery_config_backups VALUES (?,?,?,?,?,?,?,?)').run(id, this.s.workspaceId, target.id, target.revision, target.key_ref, sealed, bytes.length, Date.now());
159
+ return id;
160
+ }
161
+ finally {
162
+ key.fill(0);
163
+ }
164
+ }
165
+ backupRow(id) {
166
+ const row = this.db.prepare('SELECT * FROM recovery_config_backups WHERE id=? AND workspace_id=?').get(id, this.s.workspaceId);
167
+ if (!row)
168
+ throw new DodoError('NOT_FOUND', 'encrypted private backup unavailable');
169
+ return row;
170
+ }
171
+ async open(row, target, revalidate) {
172
+ const key = await this.keys.get(row.key_ref);
173
+ try {
174
+ revalidate();
175
+ const data = z.object({ header: z.object({ version: z.literal(1), id: z.literal(row.id), workspaceId: z.literal(this.s.workspaceId), root: z.literal(rootIdentity(this.s.wfs.root)),
176
+ targetId: z.literal(target.id), targetRevision: z.literal(row.target_revision), keyRef: z.literal(row.key_ref) }).strict(),
177
+ iv: z.string().max(24), tag: z.string().max(32), ciphertext: z.string().max(2 * MAX_BYTES) }).strict().parse(JSON.parse(row.sealed));
178
+ if (row.target_id !== target.id || row.bytes > MAX_BYTES)
179
+ throw new Error();
180
+ const iv = Buffer.from(data.iv, 'base64'), tag = Buffer.from(data.tag, 'base64');
181
+ if (iv.length !== 12 || tag.length !== 16)
182
+ throw new Error();
183
+ const decipher = createDecipheriv('aes-256-gcm', key, iv);
184
+ decipher.setAAD(Buffer.from(JSON.stringify(data.header)));
185
+ decipher.setAuthTag(tag);
186
+ const partial = decipher.update(Buffer.from(data.ciphertext, 'base64'));
187
+ let bytes, returned = false;
188
+ try {
189
+ bytes = Buffer.concat([partial, decipher.final()]);
190
+ if (bytes.length !== row.bytes)
191
+ throw new Error();
192
+ revalidate();
193
+ returned = true;
194
+ return bytes;
195
+ }
196
+ finally {
197
+ partial.fill(0);
198
+ if (!returned)
199
+ bytes?.fill(0);
200
+ }
201
+ }
202
+ catch {
203
+ throw new DodoError('RECOVERY_REQUIRED', 'encrypted backup integrity or current authority could not be verified; plaintext withheld');
204
+ }
205
+ finally {
206
+ key.fill(0);
207
+ }
208
+ }
209
+ async backup(targetId, revalidate) {
210
+ return this.s.mutations.run(async () => {
211
+ revalidate();
212
+ const target = this.target(targetId);
213
+ if (!target.enabled)
214
+ throw new DodoError('FORBIDDEN', 'private config backup is disabled');
215
+ const current = this.read(target);
216
+ try {
217
+ const id = await this.seal(target, current.bytes, revalidate);
218
+ this.audit('backup', id, digestOf({ targetId }));
219
+ this.prune(target);
220
+ return { id, bytes: current.bytes.length, encrypted: true };
221
+ }
222
+ finally {
223
+ current.bytes.fill(0);
224
+ }
225
+ });
226
+ }
227
+ prune(target) {
228
+ const rows = this.db.prepare('SELECT id FROM recovery_config_backups WHERE target_id=? ORDER BY created_at DESC,id DESC').all(target.id);
229
+ for (const row of rows.slice(this.definition(target).retention)) {
230
+ if (this.db.prepare('SELECT 1 FROM recovery_config_restores WHERE snapshot_id=? OR before_id=? LIMIT 1').get(row.id, row.id))
231
+ continue;
232
+ this.db.prepare('DELETE FROM recovery_config_backups WHERE id=?').run(row.id);
233
+ }
234
+ }
235
+ async rotate(targetId, expectedRevision, revalidate) {
236
+ return this.s.mutations.run(async () => {
237
+ revalidate();
238
+ const target = this.target(targetId);
239
+ if (target.revision !== expectedRevision)
240
+ throw new DodoError('CONFLICT', 'private config target revision changed');
241
+ const reference = await this.createKey(revalidate);
242
+ revalidate();
243
+ this.db.prepare('UPDATE recovery_config_targets SET key_ref=?,revision=revision+1 WHERE id=?').run(reference, target.id);
244
+ this.audit('rotate', target.id, digestOf({ expectedRevision }));
245
+ return { targetId, revision: expectedRevision + 1, oldBackupsKeepOldOSKeys: true, oldBackupsReencrypted: false };
246
+ });
247
+ }
248
+ async preview(backupId, revalidate) {
249
+ return this.s.mutations.run(async () => {
250
+ revalidate();
251
+ if (this.db.prepare('SELECT COUNT(*) n FROM recovery_config_restores WHERE workspace_id=?').get(this.s.workspaceId).n >= 1000)
252
+ throw new DodoError('RESOURCE_LIMIT', 'private config restore history limit reached');
253
+ const backup = this.backupRow(backupId), target = this.target(backup.target_id);
254
+ if (!target.enabled)
255
+ throw new DodoError('FORBIDDEN', 'private config target is disabled');
256
+ let desired, current, key;
257
+ try {
258
+ desired = await this.open(backup, target, revalidate);
259
+ current = this.read(target);
260
+ key = await this.keys.get(target.key_ref);
261
+ revalidate();
262
+ const plan = { id: newId('configrestore'), targetId: target.id, targetRevision: target.revision, backupId,
263
+ epoch: this.s.epoch, root: rootIdentity(this.s.wfs.root), expiresAt: Date.now() + 10 * 60000, keyRef: target.key_ref,
264
+ expectedMac: createHmac('sha256', key).update(current.bytes).digest('hex'), fileIdentity: fileIdentity(current.stat), backupDigest: digestOf(backup.sealed) };
265
+ const planHash = digestOf(plan);
266
+ this.db.prepare("INSERT INTO recovery_config_restores VALUES (?,?,?,?,NULL,?,?,'PREVIEW',NULL)").run(plan.id, this.s.workspaceId, target.id, backup.id, JSON.stringify(plan), planHash);
267
+ return { planId: plan.id, planHash, expiresAt: plan.expiresAt, targetId: target.id, changed: !current.bytes.equals(desired),
268
+ bytesBefore: current.bytes.length, bytesAfter: desired.length, plaintext: 'REDACTED', encryptedPreRestoreBackupRequired: true,
269
+ restartRequired: 'owner_managed', writeMode: 'single_existing_private_file_journaled_in_place' };
270
+ }
271
+ finally {
272
+ current?.bytes.fill(0);
273
+ desired?.fill(0);
274
+ key?.fill(0);
275
+ }
276
+ });
277
+ }
278
+ async apply(planId, planHash, revalidate) {
279
+ return this.s.mutations.run(async () => {
280
+ revalidate();
281
+ this.r.assertProject();
282
+ const row = this.db.prepare('SELECT * FROM recovery_config_restores WHERE id=? AND workspace_id=?').get(planId, this.s.workspaceId);
283
+ if (!row)
284
+ throw new DodoError('NOT_FOUND', 'private config restore unavailable');
285
+ const plan = JSON.parse(row.payload);
286
+ if (digestOf(plan) !== row.hash || row.hash !== planHash)
287
+ throw new DodoError('PLAN_HASH_MISMATCH', 'review exact private config restore');
288
+ if (row.state !== 'PREVIEW')
289
+ return { state: row.state, outcome: row.result ? JSON.parse(row.result) : null, replayed: true };
290
+ const target = this.target(plan.targetId), backup = this.backupRow(plan.backupId);
291
+ if (!target.enabled || target.revision !== plan.targetRevision || target.key_ref !== plan.keyRef || plan.epoch !== this.s.epoch || plan.expiresAt < Date.now()
292
+ || plan.root !== rootIdentity(this.s.wfs.root) || digestOf(backup.sealed) !== plan.backupDigest)
293
+ throw new DodoError('CONFLICT', 'private config plan or authority changed; preview again');
294
+ let desired, current, key;
295
+ let fd, started = false;
296
+ try {
297
+ desired = await this.open(backup, target, revalidate);
298
+ current = this.read(target);
299
+ key = await this.keys.get(target.key_ref);
300
+ if (fileIdentity(current.stat) !== plan.fileIdentity || createHmac('sha256', key).update(current.bytes).digest('hex') !== plan.expectedMac)
301
+ throw new DodoError('FILE_CHANGED', 'private config changed after preview');
302
+ const beforeId = await this.seal(target, current.bytes, revalidate), file = this.resolve(target);
303
+ fd = fs.openSync(file.absolute, fs.constants.O_RDWR | (fs.constants.O_NOFOLLOW ?? 0));
304
+ const opened = fs.fstatSync(fd);
305
+ if (fileIdentity(opened) !== plan.fileIdentity || opened.nlink !== 1 || opened.size !== current.stat.size || opened.mtimeMs !== current.stat.mtimeMs || opened.ctimeMs !== current.stat.ctimeMs)
306
+ throw new DodoError('FILE_CHANGED', 'private config changed before write');
307
+ revalidate();
308
+ this.db.prepare("UPDATE recovery_config_restores SET before_id=?,state='UNKNOWN',result=? WHERE id=?").run(beforeId, JSON.stringify({ retryAllowed: false, reason: 'write_may_have_started', encryptedBeforeId: beforeId }), plan.id);
309
+ started = true;
310
+ let n = 0;
311
+ while (n < desired.length) {
312
+ const count = fs.writeSync(fd, desired, n, desired.length - n, n);
313
+ if (!count)
314
+ throw new Error('short config write');
315
+ n += count;
316
+ }
317
+ fs.ftruncateSync(fd, desired.length);
318
+ fs.fsyncSync(fd);
319
+ fs.closeSync(fd);
320
+ fd = undefined;
321
+ revalidate();
322
+ const verified = this.read(target);
323
+ try {
324
+ if (!verified.bytes.equals(desired))
325
+ throw new DodoError('FILE_CHANGED', 'private config read-back differs');
326
+ }
327
+ finally {
328
+ verified.bytes.fill(0);
329
+ }
330
+ const outcome = { verified: true, encryptedBeforeId: beforeId, restarted: false };
331
+ this.db.prepare("UPDATE recovery_config_restores SET state='APPLIED',result=? WHERE id=?").run(JSON.stringify(outcome), plan.id);
332
+ this.audit('restore', plan.id, planHash);
333
+ return { state: 'APPLIED', outcome, replayed: false };
334
+ }
335
+ catch (e) {
336
+ if (!started)
337
+ throw e;
338
+ return { state: 'UNKNOWN', retryAllowed: false, recovery: 'inspect the private file locally; a new reviewed restore may use the encrypted pre-restore backup', replayed: false };
339
+ }
340
+ finally {
341
+ try {
342
+ if (fd !== undefined)
343
+ fs.closeSync(fd);
344
+ }
345
+ finally {
346
+ current?.bytes.fill(0);
347
+ desired?.fill(0);
348
+ key?.fill(0);
349
+ }
350
+ }
351
+ });
352
+ }
353
+ reconcile() {
354
+ // UNKNOWN is written before the first byte: never repeat it after restart.
355
+ this.db.prepare("UPDATE recovery_config_restores SET state='EXPIRED' WHERE workspace_id=? AND state='PREVIEW' AND json_extract(payload,'$.epoch')<>?").run(this.s.workspaceId, this.s.epoch);
356
+ }
357
+ audit(action, id, digest) {
358
+ this.s.store.audit({ principal: 'local-config-owner', workspaceId: this.s.workspaceId, tool: `recovery.config.${action}`, refId: id, inputDigest: digest, result: 'encrypted_owner_action' });
359
+ }
360
+ }
361
+ //# sourceMappingURL=configVault.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configVault.js","sourceRoot":"","sources":["../../../src/services/recovery/configVault.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { z } from 'zod';
2
+ const GiB = 1024 ** 3;
3
+ export const RecoveryPolicySchema = z.object({
4
+ version: z.literal(1).default(1),
5
+ enabled: z.boolean().default(true),
6
+ scanMs: z.number().int().min(100).max(60000).default(15000),
7
+ massFiles: z.number().int().min(1).max(100000).default(20),
8
+ massPercent: z.number().min(0).max(100).default(20),
9
+ deletedFiles: z.number().int().min(1).max(100000).default(10),
10
+ gitRequired: z.boolean().default(false),
11
+ gitDirectory: z.string().max(2048).nullable().default(null),
12
+ projectBytes: z.number().int().min(1024 * 1024).max(100 * GiB).default(5 * GiB),
13
+ retentionDays: z.number().int().min(1).max(3650).default(30),
14
+ retainedPoints: z.number().int().min(1).max(10000).default(200),
15
+ fileBytes: z.number().int().min(1024).max(GiB).default(64 * 1024 * 1024),
16
+ maxEntries: z.number().int().min(1).max(1000000).default(100000),
17
+ dataRoots: z.array(z.string().min(1).max(1024)).max(100).default([]),
18
+ }).strict();
19
+ export const RecoveryInstallationSchema = z.object({
20
+ version: z.literal(1).default(1),
21
+ storageBytes: z.number().int().min(1024 * 1024).max(1024 * GiB).default(20 * GiB),
22
+ freeFloorBytes: z.number().int().min(0).max(100 * GiB).default(500 * 1024 * 1024),
23
+ }).strict();
24
+ //# sourceMappingURL=contracts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts.js","sourceRoot":"","sources":["../../../src/services/recovery/contracts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,179 @@
1
+ import fs from 'node:fs';
2
+ import Database from 'better-sqlite3';
3
+ import { z } from 'zod';
4
+ import { DodoError } from '../../errors.js';
5
+ import { digestOf, newId } from '../../util/hash.js';
6
+ const identifier = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,63}$/);
7
+ const migrationId = z.string().min(1).max(128).regex(/^[A-Za-z0-9_.-]+$/);
8
+ export const DatabaseTargetSchema = z.object({
9
+ name: z.string().min(1).max(80), adapter: z.literal('sqlite-migration-table'),
10
+ databaseFile: z.string().min(1).max(1024).regex(/\.(?:db|sqlite|sqlite3)$/i),
11
+ table: identifier, column: identifier,
12
+ onMismatch: z.enum(['block', 'warn']).default('block'),
13
+ }).strict();
14
+ const identity = (s) => `${s.dev}:${s.ino}:${s.birthtimeMs}`;
15
+ /** Read-only metadata adapter, never a generic SQL executor or database undo. */
16
+ export class RecoveryDatabases {
17
+ s;
18
+ r;
19
+ constructor(s, r) {
20
+ this.s = s;
21
+ this.r = r;
22
+ }
23
+ get db() { return this.s.store.db; }
24
+ file(definition, expected) {
25
+ const rel = this.s.wfs.normalizeRel(definition.databaseFile);
26
+ if (rel !== definition.databaseFile)
27
+ throw new DodoError('PATH_DENIED', 'database path must be canonical and project-relative');
28
+ const file = this.s.wfs.resolve(rel), st = file.stat;
29
+ if (!st?.isFile() || st.nlink !== 1 || (expected && identity(st) !== expected))
30
+ throw new DodoError('PATH_DENIED', 'registered database identity changed or is not a regular unlinked file');
31
+ for (const suffix of ['-wal', '-shm', '-journal']) {
32
+ const side = this.s.wfs.resolve(rel + suffix, { allowMissing: true });
33
+ if (side.stat && (!side.stat.isFile() || side.stat.nlink !== 1))
34
+ throw new DodoError('PATH_DENIED', 'database sidecar is not a regular unlinked file');
35
+ }
36
+ return { ...file, stat: st };
37
+ }
38
+ row(id) {
39
+ this.r.assertProject();
40
+ const row = this.db.prepare('SELECT * FROM recovery_database_targets WHERE id=? AND workspace_id=?').get(id, this.s.workspaceId);
41
+ if (!row)
42
+ throw new DodoError('NOT_FOUND', 'database target unavailable for this project');
43
+ return row;
44
+ }
45
+ list() {
46
+ this.r.assertProject();
47
+ return this.db.prepare('SELECT * FROM recovery_database_targets WHERE workspace_id=? ORDER BY id').all(this.s.workspaceId)
48
+ .map(row => ({ id: row.id, revision: row.revision, enabled: Boolean(row.enabled), definition: DatabaseTargetSchema.parse(JSON.parse(row.definition)) }));
49
+ }
50
+ summary() {
51
+ const count = this.db.prepare('SELECT COUNT(*) AS n FROM recovery_database_targets WHERE workspace_id=? AND enabled=1').get(this.s.workspaceId).n;
52
+ return { configured: count > 0, targetCount: count, state: 'UNKNOWN', databaseRollback: 'NOT_SUPPORTED', mutationsPerformed: false };
53
+ }
54
+ read(row) {
55
+ const definition = DatabaseTargetSchema.parse(JSON.parse(row.definition));
56
+ const before = this.file(definition, row.file_identity);
57
+ // No URI options, credentials, extension loading, migrations or writable connection.
58
+ const db = new Database(before.abs, { readonly: true, fileMustExist: true, timeout: 1000 });
59
+ try {
60
+ db.pragma('query_only=ON');
61
+ db.pragma('trusted_schema=OFF');
62
+ db.exec('BEGIN');
63
+ const table = db.prepare('SELECT type,sql FROM sqlite_schema WHERE name=?').get(definition.table);
64
+ if (!table || table.type !== 'table' || /CREATE\s+VIRTUAL\s+TABLE/i.test(table.sql))
65
+ throw new DodoError('NOT_SUPPORTED', 'migration metadata must be an ordinary SQLite table');
66
+ // Identifiers are strictly parsed above; no user-supplied SQL clauses.
67
+ const values = db.prepare(`SELECT m."${definition.column}" AS migration_id FROM "${definition.table}" AS m LIMIT 1001`).all();
68
+ if (values.length > 1000)
69
+ throw new DodoError('RESOURCE_LIMIT', 'migration metadata exceeds the bounded adapter limit');
70
+ const ids = values.map(value => migrationId.parse(value.migration_id));
71
+ if (new Set(ids).size !== ids.length)
72
+ throw new DodoError('CONFLICT', 'migration metadata contains duplicate IDs');
73
+ db.exec('COMMIT');
74
+ this.file(definition, row.file_identity);
75
+ return ids.sort();
76
+ }
77
+ catch {
78
+ throw new DodoError('RECOVERY_REQUIRED', 'migration metadata could not be verified; database contents and SQL errors are withheld');
79
+ }
80
+ finally {
81
+ db.close();
82
+ }
83
+ }
84
+ /** Authenticated owner only; caller must supply a current owner lease. */
85
+ async configure(raw, revalidate) {
86
+ const input = z.object({ targetId: z.string().max(128).optional(), expectedRevision: z.number().int().nonnegative(),
87
+ enabled: z.boolean(), confirmReadOnlyAccess: z.literal(true), definition: DatabaseTargetSchema }).strict().parse(raw);
88
+ return this.s.mutations.run(async () => {
89
+ this.r.assertProject();
90
+ revalidate();
91
+ const old = input.targetId ? this.row(input.targetId) : undefined;
92
+ if ((old?.revision ?? 0) !== input.expectedRevision)
93
+ throw new DodoError('CONFLICT', 'database target revision changed');
94
+ if (!old && this.list().length >= 4)
95
+ throw new DodoError('RESOURCE_LIMIT', 'database target limit reached');
96
+ const file = this.file(input.definition), id = old?.id ?? newId('dbtarget'), revision = input.expectedRevision + 1;
97
+ const row = { id, workspace_id: this.s.workspaceId, revision, enabled: Number(input.enabled), definition: JSON.stringify(input.definition), file_identity: identity(file.stat) };
98
+ if (input.enabled)
99
+ this.read(row); // Readiness first; never save a false ready state.
100
+ revalidate();
101
+ this.db.prepare(`INSERT INTO recovery_database_targets VALUES (?,?,?,?,?,?) ON CONFLICT(id) DO UPDATE SET revision=excluded.revision,
102
+ enabled=excluded.enabled,definition=excluded.definition,file_identity=excluded.file_identity`)
103
+ .run(id, this.s.workspaceId, revision, row.enabled, row.definition, row.file_identity);
104
+ this.audit('configure', id, digestOf(input));
105
+ return { id, revision, enabled: input.enabled, readOnly: true };
106
+ });
107
+ }
108
+ inspectOwner(id, revalidate) {
109
+ revalidate();
110
+ const row = this.row(id), ids = this.read(row);
111
+ revalidate();
112
+ return { targetId: id, revision: row.revision, appliedMigrationIds: ids, appliedDigest: digestOf(ids), databaseRollback: 'NOT_SUPPORTED' };
113
+ }
114
+ async bind(raw, revalidate) {
115
+ const input = z.object({ targetId: z.string().max(128), expectedRevision: z.number().int().positive(), checkpointId: z.string().max(128),
116
+ requiredMigrationIds: z.array(migrationId).max(1000), allowExtra: z.boolean().default(false), confirmCompatibilityRule: z.literal(true) }).strict().parse(raw);
117
+ return this.s.mutations.run(async () => {
118
+ revalidate();
119
+ const row = this.row(input.targetId);
120
+ if (!row.enabled || row.revision !== input.expectedRevision)
121
+ throw new DodoError('CONFLICT', 'database target disabled or revision changed');
122
+ if (new Set(input.requiredMigrationIds).size !== input.requiredMigrationIds.length)
123
+ throw new DodoError('INVALID_INPUT', 'duplicate required migration ID');
124
+ const manifest = await this.r.history.manifest(input.checkpointId, { id: 'local-config-owner', owner: true });
125
+ revalidate();
126
+ this.db.prepare(`INSERT INTO recovery_database_bindings VALUES (?,?,?,?,?,?) ON CONFLICT(target_id,snapshot_id) DO UPDATE SET
127
+ target_revision=excluded.target_revision,manifest_hash=excluded.manifest_hash,expected_ids=excluded.expected_ids,allow_extra=excluded.allow_extra`)
128
+ .run(row.id, manifest.id, row.revision, digestOf(manifest), JSON.stringify([...input.requiredMigrationIds].sort()), Number(input.allowExtra));
129
+ this.audit('bind', row.id, digestOf(input));
130
+ return { targetId: row.id, checkpointId: manifest.id, requiredCount: input.requiredMigrationIds.length, ruleSource: 'explicit_owner', databaseChanged: false };
131
+ });
132
+ }
133
+ async compatibility(snapshotIds) {
134
+ this.r.assertProject();
135
+ const rows = this.db.prepare('SELECT * FROM recovery_database_targets WHERE workspace_id=? AND enabled=1 ORDER BY id').all(this.s.workspaceId);
136
+ const items = [];
137
+ for (const row of rows) {
138
+ const definition = DatabaseTargetSchema.parse(JSON.parse(row.definition));
139
+ let result = { targetId: row.id, revision: row.revision, state: 'UNKNOWN', reason: 'no_single_snapshot_compatibility_rule', blocking: definition.onMismatch === 'block' };
140
+ try {
141
+ if (snapshotIds.length === 1) {
142
+ const binding = this.db.prepare('SELECT * FROM recovery_database_bindings WHERE target_id=? AND snapshot_id=?').get(row.id, snapshotIds[0]);
143
+ if (binding && binding.target_revision === row.revision) {
144
+ const manifest = await this.r.history.manifest(snapshotIds[0], { id: 'local-config-owner', owner: true });
145
+ if (binding.manifest_hash !== digestOf(manifest))
146
+ throw new DodoError('RECOVERY_REQUIRED', 'manifest binding changed');
147
+ const applied = this.read(row), required = z.array(migrationId).max(1000).parse(JSON.parse(binding.expected_ids));
148
+ const compatible = required.every(id => applied.includes(id)) && (Boolean(binding.allow_extra) || required.length === applied.length);
149
+ result = { ...result, state: compatible ? 'COMPATIBLE' : 'INCOMPATIBLE', reason: compatible ? 'owner_rule_matches_applied_ids' : 'applied_ids_differ_from_owner_rule',
150
+ blocking: !compatible && definition.onMismatch === 'block', appliedDigest: digestOf(applied), appliedCount: applied.length, requiredCount: required.length };
151
+ }
152
+ }
153
+ }
154
+ catch {
155
+ result = { ...result, reason: 'metadata_or_manifest_unavailable' };
156
+ }
157
+ items.push(result);
158
+ }
159
+ const value = { configured: rows.length > 0, state: !rows.length || items.some(i => i.state === 'UNKNOWN') ? 'UNKNOWN' : items.some(i => i.state === 'INCOMPATIBLE') ? 'INCOMPATIBLE' : 'COMPATIBLE',
160
+ items, blocking: items.some(i => i.blocking), databaseChanged: false, databaseRollback: 'NOT_SUPPORTED' };
161
+ return { ...value, digest: digestOf(value) };
162
+ }
163
+ async unbind(raw, revalidate) {
164
+ const input = z.object({ targetId: z.string().max(128), expectedRevision: z.number().int().positive(), checkpointId: z.string().max(128), confirmCompatibilityRemoval: z.literal(true) }).strict().parse(raw);
165
+ return this.s.mutations.run(async () => {
166
+ revalidate();
167
+ const target = this.row(input.targetId);
168
+ if (target.revision !== input.expectedRevision)
169
+ throw new DodoError('CONFLICT', 'database target revision changed');
170
+ const changed = this.db.prepare('DELETE FROM recovery_database_bindings WHERE target_id=? AND snapshot_id=?').run(target.id, input.checkpointId).changes;
171
+ this.audit('unbind', target.id, digestOf(input));
172
+ return { targetId: target.id, checkpointId: input.checkpointId, removed: changed > 0, databaseChanged: false };
173
+ });
174
+ }
175
+ audit(action, id, digest) {
176
+ this.s.store.audit({ principal: 'local-config-owner', workspaceId: this.s.workspaceId, tool: `recovery.database.${action}`, refId: id, inputDigest: digest, result: 'owner_readonly_rule' });
177
+ }
178
+ }
179
+ //# sourceMappingURL=databaseAwareness.js.map