@uns-kit/core 3.0.45 → 3.0.46
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.
- package/MIGRATIONS.md +12 -0
- package/dist/runtime/retained-store-copy.d.mts +3 -0
- package/dist/runtime/retained-store-copy.mjs +133 -0
- package/dist/runtime/retained-store-guide.md +44 -2
- package/dist/runtime/retained-store-job.d.mts +1 -0
- package/dist/runtime/retained-store-job.mjs +28 -0
- package/dist/runtime/retained-store-storage.d.mts +7 -0
- package/dist/runtime/retained-store-storage.mjs +66 -21
- package/dist/runtime/retained-store.d.ts +12 -3
- package/dist/runtime/retained-store.d.ts.map +1 -1
- package/dist/runtime/retained-store.js +74 -19
- package/dist/runtime/retained-store.js.map +1 -1
- package/package.json +1 -1
package/MIGRATIONS.md
CHANGED
|
@@ -9,6 +9,18 @@ Agents must inspect the application's existing ownership and shutdown flow
|
|
|
9
9
|
before editing it. The examples below describe the intended behavior, not a
|
|
10
10
|
mechanical search-and-replace operation.
|
|
11
11
|
|
|
12
|
+
## 3.0.46 - Verified retained-store copy and recovery
|
|
13
|
+
|
|
14
|
+
When crossing `<3.0.46` to `>=3.0.46`, existing rename and unbound consumers
|
|
15
|
+
remain compatible. An explicit
|
|
16
|
+
`transfer: "copy-verified"` adds stopped-source review and bounded asynchronous
|
|
17
|
+
opaque copying between local mounts. Use the same operation/checksum after
|
|
18
|
+
interruption; return current work and retain the original copy. Full Runtime
|
|
19
|
+
recovery requires the matching verified offline retained root before RTT owners
|
|
20
|
+
can be activated. See `docs/retained-store-adoption.md`, bundled as the core
|
|
21
|
+
retained-store guide. No application-specific Controller logic, queue conversion,
|
|
22
|
+
new default handover protocol or automatic disk persistence is introduced.
|
|
23
|
+
|
|
12
24
|
## 3.0.45 - Opt-in retained-store storage protocol
|
|
13
25
|
|
|
14
26
|
When crossing `<3.0.45` to `>=3.0.45`, ordinary applications keep their current
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export type RetainedCopyFingerprint = { digest: string; bytes: number; entries: number };
|
|
2
|
+
export function inspectRetainedCopy(root: string, kind: "file" | "directory", limits: unknown): RetainedCopyFingerprint;
|
|
3
|
+
export function applyRetainedCopy(plan: unknown, direction: "adopt" | "return", limits: unknown, afterMove?: (id: string) => void, onFile?: (file: string) => void): void;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { safePath, owned, sha, sync, entry, relative, checkLimit } from "./retained-store-storage.mjs";
|
|
5
|
+
|
|
6
|
+
function inventory(root, kind, limits) {
|
|
7
|
+
checkLimit(limits);
|
|
8
|
+
const entries = [];
|
|
9
|
+
let bytes = 0;
|
|
10
|
+
function visit(file, rel, depth) {
|
|
11
|
+
if (depth > limits.maximumDepth || (rel && !relative(rel)) || entries.length >= limits.maximumEntries) throw Error("Retained copy path/count limit");
|
|
12
|
+
const stat = entry(file);
|
|
13
|
+
if (!stat) throw Error("Retained copy source is missing");
|
|
14
|
+
if (stat.isDirectory()) {
|
|
15
|
+
owned(file, true);
|
|
16
|
+
entries.push({ path: rel, type: "directory" });
|
|
17
|
+
for (const name of fs.readdirSync(file).sort()) visit(path.join(file, name), rel ? rel + "/" + name : name, depth + 1);
|
|
18
|
+
} else {
|
|
19
|
+
const value = owned(file);
|
|
20
|
+
bytes += value.size;
|
|
21
|
+
if (bytes > limits.maximumBytes || value.size > limits.maximumFileBytes) throw Error("Retained copy byte limit");
|
|
22
|
+
entries.push({ path: rel, type: "file", size: value.size, sha256: sha(file) });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
safePath(root);
|
|
26
|
+
if ((kind === "directory") !== !!entry(root)?.isDirectory()) throw Error("Retained copy resource kind differs");
|
|
27
|
+
visit(root, "", 0);
|
|
28
|
+
return { entries, fingerprint: { digest: createHash("sha256").update(JSON.stringify(entries)).digest("hex"), bytes, entries: entries.length } };
|
|
29
|
+
}
|
|
30
|
+
export function inspectRetainedCopy(root, kind, limits) { return inventory(root, kind, limits).fingerprint; }
|
|
31
|
+
function same(a, b) { return a?.digest === b?.digest && a?.bytes === b?.bytes && a?.entries === b?.entries; }
|
|
32
|
+
function assertFingerprint(file, move, limits) {
|
|
33
|
+
const stat = entry(file);
|
|
34
|
+
if (!stat || stat.dev !== move.device || stat.ino !== move.inode || !same(inspectRetainedCopy(file, move.kind, limits), move.content)) throw Error("Retained copy source changed after review");
|
|
35
|
+
}
|
|
36
|
+
function privateStage(stage) {
|
|
37
|
+
if (!entry(stage)) { fs.mkdirSync(stage, { mode: 0o700 }); sync(path.dirname(stage)); }
|
|
38
|
+
if (owned(stage, true).mode & 0o077) throw Error("Retained copy staging must be private");
|
|
39
|
+
}
|
|
40
|
+
function checkStage(stage, entries) {
|
|
41
|
+
const allowed = new Set(["payload", ...entries.map(e => e.path ? "payload/" + e.path : "payload")]);
|
|
42
|
+
function visit(dir, prefix) {
|
|
43
|
+
for (const name of fs.readdirSync(dir)) {
|
|
44
|
+
const rel = prefix ? prefix + "/" + name : name, file = path.join(dir, name);
|
|
45
|
+
if (!allowed.has(rel)) throw Error("Unexpected retained copy staging entry");
|
|
46
|
+
const stat = entry(file);
|
|
47
|
+
owned(file, !!stat?.isDirectory());
|
|
48
|
+
if (stat.isDirectory()) visit(file, rel);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
visit(stage, "");
|
|
52
|
+
}
|
|
53
|
+
function copyFile(source, destination, expected) {
|
|
54
|
+
if (entry(destination)) owned(destination);
|
|
55
|
+
const input = fs.openSync(source, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
56
|
+
let output;
|
|
57
|
+
try {
|
|
58
|
+
output = fs.openSync(destination, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_TRUNC | fs.constants.O_NOFOLLOW, 0o600);
|
|
59
|
+
const h = createHash("sha256"), buffer = Buffer.alloc(65536);
|
|
60
|
+
let count = 0, size;
|
|
61
|
+
while ((size = fs.readSync(input, buffer, 0, buffer.length, null))) {
|
|
62
|
+
count += size;
|
|
63
|
+
if (count > expected.size) throw Error("Retained copy source grew");
|
|
64
|
+
h.update(buffer.subarray(0, size));
|
|
65
|
+
let offset = 0;
|
|
66
|
+
while (offset < size) offset += fs.writeSync(output, buffer, offset, size - offset);
|
|
67
|
+
}
|
|
68
|
+
fs.fchmodSync(output, 0o600); fs.fsyncSync(output);
|
|
69
|
+
if (count !== expected.size || h.digest("hex") !== expected.sha256) throw Error("Retained copy source changed while reading");
|
|
70
|
+
} finally { fs.closeSync(input); if(output!==undefined)fs.closeSync(output); }
|
|
71
|
+
}
|
|
72
|
+
function copyClosed(source, destination, stage, move, limits, onFile) {
|
|
73
|
+
assertFingerprint(source, move, limits);
|
|
74
|
+
const snapshot = inventory(source, move.kind, limits);
|
|
75
|
+
if (entry(destination)) {
|
|
76
|
+
if (!same(inspectRetainedCopy(destination, move.kind, limits), move.content)) throw Error("Published retained copy differs; preserve for review");
|
|
77
|
+
if (entry(stage)) { checkStage(stage, snapshot.entries); fs.rmSync(stage, { recursive: true }); sync(path.dirname(stage)); }
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
privateStage(stage); checkStage(stage, snapshot.entries);
|
|
81
|
+
const payload = path.join(stage, "payload");
|
|
82
|
+
for (const item of snapshot.entries) {
|
|
83
|
+
const from = item.path ? path.join(source, item.path) : source, to = item.path ? path.join(payload, item.path) : payload;
|
|
84
|
+
if (item.type === "directory") {
|
|
85
|
+
if (!entry(to)) fs.mkdirSync(to, { mode: 0o700 });
|
|
86
|
+
if (owned(to, true).mode & 0o077) throw Error("Retained stage directory must be private");
|
|
87
|
+
} else {
|
|
88
|
+
if (entry(to) && owned(to).size === item.size && sha(to) === item.sha256) continue;
|
|
89
|
+
copyFile(from, to, item); onFile?.(item.path);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
assertFingerprint(source, move, limits);
|
|
93
|
+
if (!same(inspectRetainedCopy(payload, move.kind, limits), move.content)) throw Error("Retained staged copy differs");
|
|
94
|
+
for (const item of [...snapshot.entries].reverse()) if (item.type === "directory") sync(item.path ? path.join(payload, item.path) : payload);
|
|
95
|
+
fs.renameSync(payload, destination); sync(stage); sync(path.dirname(destination));
|
|
96
|
+
fs.rmdirSync(stage); sync(path.dirname(stage));
|
|
97
|
+
}
|
|
98
|
+
export function applyRetainedCopy(plan, direction, limits, afterMove, onFile) {
|
|
99
|
+
checkLimit(limits); safePath(plan.source); safePath(plan.directory);
|
|
100
|
+
for (const move of plan.moves) {
|
|
101
|
+
const legacy = path.join(plan.source, move.relative), stable = path.join(plan.directory, move.id);
|
|
102
|
+
const stage = direction === "adopt" ? path.join(plan.directory, ".retained-copy-" + move.id) : path.join(plan.source, ".retained-return-" + plan.storeId + "-" + move.id);
|
|
103
|
+
if (direction === "adopt") copyClosed(legacy, stable, stage, move, limits, onFile);
|
|
104
|
+
else {
|
|
105
|
+
assertFingerprint(stable, move, limits);
|
|
106
|
+
const original = plan.originalMoves?.find(m => m.id === move.id), saved = path.join(plan.source, ".retained-original-" + plan.storeId + "-" + move.id);
|
|
107
|
+
if (entry(legacy) && !same(inspectRetainedCopy(legacy, move.kind, limits), move.content)) {
|
|
108
|
+
if (!original || entry(saved)) throw Error("Return would overwrite unreviewed legacy work");
|
|
109
|
+
assertFingerprint(legacy, original, limits);
|
|
110
|
+
fs.renameSync(legacy, saved); sync(path.dirname(legacy));
|
|
111
|
+
}
|
|
112
|
+
if (entry(saved)) {
|
|
113
|
+
if (!original) throw Error("Unreviewed original retained copy");
|
|
114
|
+
assertFingerprint(saved, original, limits);
|
|
115
|
+
}
|
|
116
|
+
copyClosed(stable, legacy, stage, move, limits, onFile);
|
|
117
|
+
}
|
|
118
|
+
afterMove?.(move.id);
|
|
119
|
+
}
|
|
120
|
+
if (direction === "return") {
|
|
121
|
+
for (const original of plan.originalMoves ?? []) {
|
|
122
|
+
const stage = path.join(plan.directory, ".retained-copy-" + original.id);
|
|
123
|
+
if (!entry(stage)) continue;
|
|
124
|
+
const legacy = path.join(plan.source, original.relative);
|
|
125
|
+
if (!plan.moves.some(m => m.id === original.id)) assertFingerprint(legacy, original, limits);
|
|
126
|
+
const reference = plan.moves.some(m => m.id === original.id) ? path.join(plan.directory, original.id) : legacy;
|
|
127
|
+
const snapshot = inventory(reference, original.kind, limits);
|
|
128
|
+
checkStage(stage, snapshot.entries);
|
|
129
|
+
// Only incomplete scratch is discarded; all original bytes remain proven.
|
|
130
|
+
fs.rmSync(stage, { recursive: true }); sync(plan.directory);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Retained stores: opt-in same-host adoption
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Retained-store adoption starts in UNS Kit 3.0.45. The 3.0.46 extension adds
|
|
4
|
+
explicit copying between local mounts and verification of recovered stores.
|
|
5
|
+
Managed activation requires coordinated Controller and Runtime releases. Existing applications do not change behavior.
|
|
5
6
|
This is a filesystem capability, separate from the small JSON
|
|
6
7
|
handover-state contract and MQTT output persistence. It does not change the
|
|
7
8
|
default handover protocol, enable disk queues, or transfer data between hosts.
|
|
@@ -120,3 +121,44 @@ cannot be used as a complete queue restore. Restore retains store/owner/generati
|
|
|
120
121
|
identity at the same logical root on another offline volume, and leaves an applying
|
|
121
122
|
fence until complete. Known private partial staging files may be re-created only
|
|
122
123
|
from the verified snapshot; differing published work is never overwritten.
|
|
124
|
+
|
|
125
|
+
## Cross-mount transfer and recovery verification in 3.0.46
|
|
126
|
+
|
|
127
|
+
Same-filesystem rename remains the default.
|
|
128
|
+
For initial relocation to a separately mounted Runtime volume, the host may
|
|
129
|
+
explicitly select `transfer: "copy-verified"`. Stop the source cleanly **before**
|
|
130
|
+
reviewing this transport. Review pins content hashes, aggregate byte/file counts,
|
|
131
|
+
resource identities, both installed artifacts, configuration and supervisor epoch.
|
|
132
|
+
There is no automatic `EXDEV` fallback or caller-selected filesystem path.
|
|
133
|
+
|
|
134
|
+
The host uses `prepareRetainedStoreMoveAsync`, `applyRetainedStoreMoveAsync` and
|
|
135
|
+
`prepareRetainedStoreReturnAsync`. Bounded worker jobs perform opaque hashing and
|
|
136
|
+
copying while the host event loop can serve health and recovery reads. Persist the
|
|
137
|
+
complete plan before mutation and hold the existing maintenance/lifecycle fences
|
|
138
|
+
through the durable outcome. Jobs have a 128MiB old-generation heap and a 300s
|
|
139
|
+
bound; uncertainty always requires reconciliation of the same operation.
|
|
140
|
+
|
|
141
|
+
Copy stages and fsyncs each declared resource on its receiving mount, verifies
|
|
142
|
+
its exact contents and atomically publishes it. Source files remain unchanged and
|
|
143
|
+
fenced; allow space for both copies. Known incomplete scratch can be resumed from
|
|
144
|
+
the unchanged reviewed source. Unknown entries, links, changed source bytes and
|
|
145
|
+
differing published files are preserved and refused. The existing limits remain
|
|
146
|
+
32 stores, 100,000 entries, 64GiB aggregate files and 32GiB per file.
|
|
147
|
+
|
|
148
|
+
Return copies the **current** stopped store back. If current work differs from
|
|
149
|
+
the original, the original is first preserved as
|
|
150
|
+
`.retained-original-<store UUID>-<resource ID>` beside its legacy path. The retired
|
|
151
|
+
stable copy remains inert and is included by full backup; it cannot acquire a
|
|
152
|
+
runtime lease. No cleanup, resource merge, conversion or cross-host transfer is
|
|
153
|
+
implied. Runtime disk capacity and the application's destination acknowledgement
|
|
154
|
+
semantics remain independent operational requirements.
|
|
155
|
+
|
|
156
|
+
Offline restore now retains a verified local snapshot manifest. The data-only
|
|
157
|
+
`verify-restored` / `verify-root` actions rehash exact declared payloads under an
|
|
158
|
+
exclusive root lease before Runtime activation. A completed restore retry never
|
|
159
|
+
replaces newer application data. Full RTT stage/activation/completion binds the
|
|
160
|
+
verified root, signed backup digest and local manifest digest into its protected
|
|
161
|
+
receipts. Missing, excluded, unfinished or altered storage cannot enable bound
|
|
162
|
+
owners. After application work advances, do not activate an old snapshot: preserve
|
|
163
|
+
current storage and use a fresh reviewed owner transition. Historic operation
|
|
164
|
+
plans pin original physical files and are not rebased after a full restore.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function retainedStoreJob(job: unknown, onProgress?: (id: string) => void): Promise<unknown>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Worker, isMainThread, parentPort, workerData } from "node:worker_threads";
|
|
2
|
+
import { inspectRetainedCopy, applyRetainedCopy } from "./retained-store-copy.mjs";
|
|
3
|
+
if (!isMainThread) {
|
|
4
|
+
const job = workerData;
|
|
5
|
+
if (job.action === "inspect") parentPort.postMessage({ done: true, result: inspectRetainedCopy(job.path, job.kind, job.limits) });
|
|
6
|
+
else if (job.action === "apply") {
|
|
7
|
+
applyRetainedCopy(job.plan, job.direction, job.limits, id => parentPort.postMessage({ progress: id }));
|
|
8
|
+
parentPort.postMessage({ done: true });
|
|
9
|
+
} else throw Error("Unknown retained storage job");
|
|
10
|
+
}
|
|
11
|
+
export function retainedStoreJob(job, onProgress) {
|
|
12
|
+
if (!isMainThread) throw Error("Nested retained storage job");
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const worker = new Worker(new URL(import.meta.url), { workerData: job, execArgv: [], env: { NODE_NO_WARNINGS: "1" }, resourceLimits: { maxOldGenerationSizeMb: 128 } });
|
|
15
|
+
let done = false;
|
|
16
|
+
const timer = setTimeout(() => finish(Error("Retained storage job timed out; reconcile the same operation")), 300000);
|
|
17
|
+
const finish = (error, result) => {
|
|
18
|
+
if (done) return; done = true; clearTimeout(timer);
|
|
19
|
+
void worker.terminate().finally(() => error ? reject(error) : resolve(result));
|
|
20
|
+
};
|
|
21
|
+
worker.on("message", message => {
|
|
22
|
+
if (message.progress) { try { onProgress?.(message.progress); } catch (error) { finish(error); } }
|
|
23
|
+
if (message.done) finish(undefined, message.result);
|
|
24
|
+
});
|
|
25
|
+
worker.once("error", error => finish(error));
|
|
26
|
+
worker.once("exit", () => { if (!done) finish(Error("Retained storage job exited without confirmation; reconcile the same operation")); });
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -15,3 +15,10 @@ export function restoreRetained(
|
|
|
15
15
|
afterPublish?: (id: string) => void,
|
|
16
16
|
): { state: string; stores: number };
|
|
17
17
|
export function available(root: string): void;
|
|
18
|
+
export function owned(path: string, directory?: boolean): import("node:fs").Stats;
|
|
19
|
+
export function sha(path: string): string;
|
|
20
|
+
export function sync(path: string): void;
|
|
21
|
+
export function relative(path: string): boolean;
|
|
22
|
+
export function checkLimit(limits: unknown): void;
|
|
23
|
+
export function verifyRestoredRetained(source:string,root:string,backupDigest:string,limits:unknown):{state:string;stores:number;backupDigest:string;manifestDigest:string;root:string};
|
|
24
|
+
export function verifyRetainedRecoveryRoot(root:string,backupDigest:string,manifestDigest:string,limits:unknown):{state:string;stores:number;backupDigest:string;manifestDigest:string;root:string};
|
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { DatabaseSync } from "node:sqlite";
|
|
5
5
|
|
|
6
6
|
export const RESTORE_FILE = "retained-store-restore.json";
|
|
7
|
+
export const RESTORE_MANIFEST = "retained-store-restore-manifest.json";
|
|
7
8
|
export const DECLARATION = "store-resources.json";
|
|
8
9
|
export const OPERATION = "store-operation.json";
|
|
9
10
|
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
@@ -21,7 +22,7 @@ export function safePath(p) {
|
|
|
21
22
|
if (!path.isAbsolute(p) || path.normalize(p) !== p) throw Error("Unsafe retained path");
|
|
22
23
|
for (let a = p; a !== path.dirname(a); a = path.dirname(a)) if (entry(a)?.isSymbolicLink()) throw Error("Linked retained path");
|
|
23
24
|
}
|
|
24
|
-
function owned(p, directory = false) {
|
|
25
|
+
export function owned(p, directory = false) {
|
|
25
26
|
safePath(p);
|
|
26
27
|
const s = entry(p);
|
|
27
28
|
if (!s || s.uid !== process.getuid?.() || s.isSymbolicLink() || (directory ? !s.isDirectory() : !s.isFile() || s.nlink !== 1))
|
|
@@ -38,7 +39,7 @@ export function readJSON(p, limit = 131072) {
|
|
|
38
39
|
if (s.size > limit) throw Error("Retained metadata limit");
|
|
39
40
|
return JSON.parse(fs.readFileSync(p, "utf8"));
|
|
40
41
|
}
|
|
41
|
-
function sync(p) {
|
|
42
|
+
export function sync(p) {
|
|
42
43
|
const fd = fs.openSync(p, "r");
|
|
43
44
|
try {
|
|
44
45
|
fs.fsyncSync(fd);
|
|
@@ -147,7 +148,7 @@ export function runtimeLease(binding) {
|
|
|
147
148
|
throw e;
|
|
148
149
|
}
|
|
149
150
|
}
|
|
150
|
-
function sha(file) {
|
|
151
|
+
export function sha(file) {
|
|
151
152
|
const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
152
153
|
const h = createHash("sha256"),
|
|
153
154
|
buffer = Buffer.alloc(65536);
|
|
@@ -159,7 +160,7 @@ function sha(file) {
|
|
|
159
160
|
fs.closeSync(fd);
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
|
-
function relative(p) {
|
|
163
|
+
export function relative(p) {
|
|
163
164
|
return (
|
|
164
165
|
typeof p === "string" &&
|
|
165
166
|
p.length <= 4096 &&
|
|
@@ -212,7 +213,7 @@ function declaration(value, format) {
|
|
|
212
213
|
}
|
|
213
214
|
return value;
|
|
214
215
|
}
|
|
215
|
-
function checkLimit(limits) {
|
|
216
|
+
export function checkLimit(limits) {
|
|
216
217
|
if (
|
|
217
218
|
limits.schemaVersion !== 1 ||
|
|
218
219
|
limits.maximumStores !== 32 ||
|
|
@@ -258,7 +259,7 @@ export function captureRetained(root, destination, mode, limits) {
|
|
|
258
259
|
}
|
|
259
260
|
const names = fs.readdirSync(root).sort();
|
|
260
261
|
for (const id of names) {
|
|
261
|
-
if (["retained-root-lease.sqlite", RESTORE_FILE].includes(id)) continue;
|
|
262
|
+
if (["retained-root-lease.sqlite", RESTORE_FILE,RESTORE_MANIFEST].includes(id)) continue;
|
|
262
263
|
if (!uuid.test(id) || manifest.stores.length >= limits.maximumStores) throw Error("Unexpected retained root entry");
|
|
263
264
|
const dir = path.join(root, id);
|
|
264
265
|
privateRoot(dir);
|
|
@@ -267,30 +268,25 @@ export function captureRetained(root, destination, mode, limits) {
|
|
|
267
268
|
if (entry(path.join(dir, OPERATION))) throw Error("Retained operation is incomplete");
|
|
268
269
|
const b = binding(readJSON(path.join(dir, "store.json")), root, id);
|
|
269
270
|
const d = declaration(readJSON(path.join(dir, DECLARATION)), b.format);
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
throw Error("Undeclared returned-store entry");
|
|
271
|
+
const retired = !!entry(path.join(dir, "store-returned.json"));
|
|
272
|
+
if (retired) {
|
|
273
273
|
const r = readJSON(path.join(dir, "store-returned.json"));
|
|
274
|
-
if (r.operationId !== b.operationId || r.generation !== b.generation
|
|
274
|
+
if (r.operationId !== b.operationId || r.generation !== b.generation)
|
|
275
275
|
throw Error("Uncertain returned store");
|
|
276
|
-
fs.mkdirSync(path.join(destination, id), { mode: 0o700 });
|
|
277
|
-
manifest.entries.push({ path: id, type: "directory" });
|
|
278
|
-
for (const name of ["store.json", DECLARATION, "store-returned.json"]) copy(path.join(dir, name), id + "/" + name);
|
|
279
|
-
manifest.stores.push({ binding: b, storeId: id, generation: b.generation, owner: b.owner, format: b.format, resources: [], retired: true });
|
|
280
|
-
continue;
|
|
281
276
|
}
|
|
282
|
-
const allowed = new Set(["store.json", DECLARATION, "store-lease.sqlite", ...d.resources.map((x) => x.id)]);
|
|
277
|
+
const allowed = new Set(["store.json", DECLARATION, "store-lease.sqlite", ...(retired?["store-returned.json"]:[]), ...d.resources.map((x) => x.id)]);
|
|
283
278
|
if (fs.readdirSync(dir).some((n) => !allowed.has(n))) throw Error("Undeclared retained entry");
|
|
284
279
|
fs.mkdirSync(path.join(destination, id), { mode: 0o700 });
|
|
285
280
|
manifest.entries.push({ path: id, type: "directory" });
|
|
286
281
|
copy(path.join(dir, "store.json"), id + "/store.json");
|
|
287
282
|
copy(path.join(dir, DECLARATION), id + "/" + DECLARATION);
|
|
283
|
+
if(retired)copy(path.join(dir,"store-returned.json"),id+"/store-returned.json");
|
|
288
284
|
const resources = [];
|
|
289
285
|
for (const r of d.resources) {
|
|
290
286
|
const p = path.join(dir, r.id),
|
|
291
287
|
s = entry(p);
|
|
292
288
|
if (!s) {
|
|
293
|
-
if (r.required) throw Error("Required retained resource missing");
|
|
289
|
+
if (r.required && !retired) throw Error("Required retained resource missing");
|
|
294
290
|
continue;
|
|
295
291
|
}
|
|
296
292
|
if ((r.kind === "directory") !== s.isDirectory()) throw Error("Retained resource kind mismatch");
|
|
@@ -298,7 +294,7 @@ export function captureRetained(root, destination, mode, limits) {
|
|
|
298
294
|
resources.push({ id: r.id, backup: r.backup ?? "payload", included });
|
|
299
295
|
if (included) copy(p, id + "/" + r.id);
|
|
300
296
|
}
|
|
301
|
-
manifest.stores.push({ binding: b, storeId: id, generation: b.generation, owner: b.owner, format: b.format, resources });
|
|
297
|
+
manifest.stores.push({ binding: b, storeId: id, generation: b.generation, owner: b.owner, format: b.format, resources, ...(retired?{retired:true}:{}) });
|
|
302
298
|
}
|
|
303
299
|
const raw = JSON.stringify(manifest);
|
|
304
300
|
if (Buffer.byteLength(raw) > limits.maximumManifestBytes) throw Error("Retained manifest limit");
|
|
@@ -363,7 +359,7 @@ function verifySnapshot(source, limits) {
|
|
|
363
359
|
if (stores.size !== m.stores.length || m.entries.some((e) => !stores.has(e.path.split("/")[0]))) throw Error("Undeclared snapshot store");
|
|
364
360
|
for (const st of m.stores) {
|
|
365
361
|
const b = binding(readJSON(path.join(source, st.storeId, "store.json")), m.sourceRoot, st.storeId);
|
|
366
|
-
declaration(readJSON(path.join(source, st.storeId, DECLARATION)), b.format);
|
|
362
|
+
const declared=declaration(readJSON(path.join(source, st.storeId, DECLARATION)), b.format);
|
|
367
363
|
if (st.retired) {
|
|
368
364
|
const r = readJSON(path.join(source, st.storeId, "store-returned.json"));
|
|
369
365
|
if (
|
|
@@ -371,7 +367,7 @@ function verifySnapshot(source, limits) {
|
|
|
371
367
|
r.generation !== b.generation ||
|
|
372
368
|
m.entries.some(
|
|
373
369
|
(e) =>
|
|
374
|
-
e.path.startsWith(st.storeId + "/") && !["store.json", DECLARATION, "store-returned.json"].includes(e.path.slice(st.storeId.length + 1)),
|
|
370
|
+
e.path.startsWith(st.storeId + "/") && !["store.json", DECLARATION, "store-returned.json",...declared.resources.map(r=>r.id)].includes(e.path.slice(st.storeId.length + 1).split("/")[0]),
|
|
375
371
|
)
|
|
376
372
|
)
|
|
377
373
|
throw Error("Invalid retired-store receipt");
|
|
@@ -397,6 +393,7 @@ export function restoreRetained(source, root, backupDigest, limits, afterPublish
|
|
|
397
393
|
if (new Set(ids).size !== ids.length || ids.some((id) => !uuid.test(id))) throw Error("Invalid snapshot store identities");
|
|
398
394
|
if (marker?.state === "complete") {
|
|
399
395
|
for (const id of ids) binding(readJSON(path.join(root, id, "store.json")), root, id);
|
|
396
|
+
persistRecoveryManifest(source,root,manifestDigest);
|
|
400
397
|
return { state: "complete", stores: ids.length };
|
|
401
398
|
}
|
|
402
399
|
if (!marker) {
|
|
@@ -404,10 +401,11 @@ export function restoreRetained(source, root, backupDigest, limits, afterPublish
|
|
|
404
401
|
marker = { protocol: 1, state: "applying", backupDigest, manifestDigest };
|
|
405
402
|
atomicJSON(markerFile, marker);
|
|
406
403
|
}
|
|
404
|
+
persistRecoveryManifest(source,root,manifestDigest);
|
|
407
405
|
const stage = path.join(root, "retained-restore-stage");
|
|
408
406
|
if (!entry(stage)) fs.mkdirSync(stage, { mode: 0o700 });
|
|
409
407
|
privateRoot(stage);
|
|
410
|
-
const allowed = new Set(["retained-root-lease.sqlite", RESTORE_FILE, "retained-restore-stage", ...ids]);
|
|
408
|
+
const allowed = new Set(["retained-root-lease.sqlite", RESTORE_FILE,RESTORE_MANIFEST, "retained-restore-stage", ...ids]);
|
|
411
409
|
if (fs.readdirSync(root).some((n) => !allowed.has(n))) throw Error("Unexpected restore target entry");
|
|
412
410
|
for (const e of m.entries) {
|
|
413
411
|
const id = e.path.split("/")[0],
|
|
@@ -486,8 +484,55 @@ export function retainedStorageMain() {
|
|
|
486
484
|
? captureRetained(root, out, modeOrDigest, limits)
|
|
487
485
|
: action === "restore"
|
|
488
486
|
? restoreRetained(root, out, modeOrDigest, limits)
|
|
487
|
+
: action === "verify-restored"
|
|
488
|
+
? verifyRestoredRetained(root,out,modeOrDigest,limits)
|
|
489
|
+
: action === "verify-root"
|
|
490
|
+
? verifyRetainedRecoveryRoot(root,modeOrDigest,out,limits)
|
|
489
491
|
: (() => {
|
|
490
492
|
throw Error("Unknown retained operation");
|
|
491
493
|
})();
|
|
492
494
|
process.stdout.write(JSON.stringify(result));
|
|
493
495
|
}
|
|
496
|
+
|
|
497
|
+
/** Offline proof tied to one verified backup, used before an RTT tree can activate. */
|
|
498
|
+
export function verifyRestoredRetained(source,root,backupDigest,limits){
|
|
499
|
+
checkLimit(limits);
|
|
500
|
+
const m=verifySnapshot(source,limits);
|
|
501
|
+
if(m.mode!=="include"||m.sourceRoot!==root||!hash.test(backupDigest))throw Error("Retained recovery scope differs");
|
|
502
|
+
return verifyRetainedRecoveryRoot(root,backupDigest,sha(path.join(source,"manifest.json")),limits);
|
|
503
|
+
}
|
|
504
|
+
function persistRecoveryManifest(source,root,digest){
|
|
505
|
+
const target=path.join(root,RESTORE_MANIFEST);
|
|
506
|
+
const temporary=target+".part";
|
|
507
|
+
if(entry(temporary)){owned(temporary);fs.unlinkSync(temporary);sync(root);}
|
|
508
|
+
if(entry(target)){if(sha(target)!==digest)throw Error("Retained recovery manifest differs");return;}
|
|
509
|
+
try{fs.copyFileSync(path.join(source,"manifest.json"),temporary,fs.constants.COPYFILE_EXCL);fs.chmodSync(temporary,0o600);sync(temporary);if(sha(temporary)!==digest)throw Error("Retained recovery manifest changed");fs.renameSync(temporary,target);sync(root);}finally{if(entry(temporary))fs.unlinkSync(temporary);}
|
|
510
|
+
}
|
|
511
|
+
export function verifyRetainedRecoveryRoot(root,backupDigest,digest,limits){
|
|
512
|
+
checkLimit(limits);
|
|
513
|
+
if(!hash.test(backupDigest)||!hash.test(digest))throw Error("Invalid retained recovery proof");
|
|
514
|
+
const release=rootLease(root,true,true);
|
|
515
|
+
try{
|
|
516
|
+
const marker=readJSON(path.join(root,RESTORE_FILE));
|
|
517
|
+
if(marker.state!=="complete"||marker.backupDigest!==backupDigest||marker.manifestDigest!==digest)throw Error("Matching completed retained recovery is required");
|
|
518
|
+
const file=path.join(root,RESTORE_MANIFEST);
|
|
519
|
+
if(sha(file)!==digest)throw Error("Retained recovery manifest differs");
|
|
520
|
+
const m=readJSON(file,limits.maximumManifestBytes);
|
|
521
|
+
if(m.schemaVersion!==1||m.kind!=="uns-retained-store-snapshot"||m.mode!=="include"||m.sourceRoot!==root||!Array.isArray(m.stores)||m.stores.length>limits.maximumStores||!Array.isArray(m.entries)||m.entries.length>limits.maximumEntries)throw Error("Invalid retained recovery manifest");
|
|
522
|
+
const ids=m.stores.map(s=>s.storeId);
|
|
523
|
+
if(new Set(ids).size!==ids.length||ids.some(id=>!uuid.test(id)))throw Error("Invalid recovered stores");
|
|
524
|
+
if(fs.readdirSync(root).some(n=>!["retained-root-lease.sqlite",RESTORE_FILE,RESTORE_MANIFEST,...ids].includes(n)))throw Error("Unexpected retained recovery root entry");
|
|
525
|
+
const seen=new Set();let total=0;
|
|
526
|
+
for(const e of m.entries){
|
|
527
|
+
if(!relative(e.path)||!ids.includes(e.path.split('/')[0])||seen.has(e.path)||!["file","directory"].includes(e.type))throw Error("Invalid retained recovery path");seen.add(e.path);
|
|
528
|
+
const p=path.join(root,e.path),s=owned(p,e.type==="directory");
|
|
529
|
+
if(e.type==="file"){total+=s.size;if(total>limits.maximumBytes||s.size>limits.maximumFileBytes||s.size!==e.size||sha(p)!==e.sha256)throw Error("Restored retained payload changed before activation");}
|
|
530
|
+
}
|
|
531
|
+
for(const id of ids){
|
|
532
|
+
const prefix=id+"/",dir=path.join(root,id),allowed=new Set(m.entries.filter(e=>e.path.startsWith(prefix)).map(e=>e.path.slice(prefix.length)));
|
|
533
|
+
if(entry(path.join(dir,"store-lease.sqlite"))){owned(path.join(dir,"store-lease.sqlite"));allowed.add("store-lease.sqlite");}
|
|
534
|
+
exactTree(dir,allowed);
|
|
535
|
+
}
|
|
536
|
+
return{state:"verified",stores:ids.length,backupDigest,manifestDigest:digest,root};
|
|
537
|
+
}finally{release();}
|
|
538
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { type RetainedCopyFingerprint } from "./retained-store-copy.mjs";
|
|
3
4
|
export declare const RETAINED_STORE_MANIFEST = "retained-store.manifest.json";
|
|
4
5
|
export declare const RETAINED_STORE_BINDING = "retained-store-binding.json";
|
|
5
6
|
export declare const RETAINED_STORE_GUARD = "retained-store-guard.json";
|
|
@@ -45,6 +46,7 @@ export type ResourceMove = {
|
|
|
45
46
|
kind: "file" | "directory";
|
|
46
47
|
device: number;
|
|
47
48
|
inode: number;
|
|
49
|
+
content?: RetainedCopyFingerprint;
|
|
48
50
|
};
|
|
49
51
|
export type StoreMovePlan = {
|
|
50
52
|
storeId: string;
|
|
@@ -54,6 +56,8 @@ export type StoreMovePlan = {
|
|
|
54
56
|
directory: string;
|
|
55
57
|
slots: RetainedStoreManifest["resources"];
|
|
56
58
|
moves: ResourceMove[];
|
|
59
|
+
transfer?: "copy-verified";
|
|
60
|
+
originalMoves?: ResourceMove[];
|
|
57
61
|
};
|
|
58
62
|
export declare function retainedStoreDigest(value: unknown): string;
|
|
59
63
|
export declare function retainedStoreEntry(file: string): fs.Stats | undefined;
|
|
@@ -62,10 +66,10 @@ export declare function readRetainedStoreJson(file: string): unknown;
|
|
|
62
66
|
export declare function syncRetainedDirectory(directory: string): void;
|
|
63
67
|
export declare function writeRetainedStoreJson(file: string, value: unknown): void;
|
|
64
68
|
export declare function readRetainedStoreManifest(packageDirectory: string): RetainedStoreManifest;
|
|
65
|
-
export declare function prepareRetainedStoreMove(source: string, root: string, storeId: string, manifest: RetainedStoreManifest): StoreMovePlan;
|
|
69
|
+
export declare function prepareRetainedStoreMove(source: string, root: string, storeId: string, manifest: RetainedStoreManifest, transfer?: "copy-verified", deferContent?: boolean): StoreMovePlan;
|
|
66
70
|
/** Caller durably records the complete plan and holds its resource reservation first.
|
|
67
71
|
* Each rename is atomic; the multi-resource operation is deliberately resumable. */
|
|
68
|
-
export declare function applyRetainedStoreMove(plan: StoreMovePlan, direction: "adopt" | "return", afterMove?: (id: string) => void): void;
|
|
72
|
+
export declare function applyRetainedStoreMove(plan: StoreMovePlan, direction: "adopt" | "return", afterMove?: (id: string) => void, onFile?: (file: string) => void, validateOnly?: boolean): void;
|
|
69
73
|
export declare function writeRetainedStoreBinding(instanceDirectory: string, binding: StoreBinding): void;
|
|
70
74
|
export declare function loadRetainedStoreBinding(instanceDirectory: string, operationId?: string): StoreBinding | undefined;
|
|
71
75
|
/** No application payload/schema inspection is performed by this helper. */
|
|
@@ -80,6 +84,7 @@ export declare const retainedStoreSelectionSchema: z.ZodObject<{
|
|
|
80
84
|
sourceInstanceId: z.ZodString;
|
|
81
85
|
targetVersion: z.ZodString;
|
|
82
86
|
targetInstanceId: z.ZodString;
|
|
87
|
+
transfer: z.ZodOptional<z.ZodLiteral<"copy-verified">>;
|
|
83
88
|
}, z.core.$strict>;
|
|
84
89
|
export declare const retainedStoreRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
85
90
|
action: z.ZodLiteral<"review">;
|
|
@@ -90,6 +95,7 @@ export declare const retainedStoreRequestSchema: z.ZodDiscriminatedUnion<[z.ZodO
|
|
|
90
95
|
sourceInstanceId: z.ZodString;
|
|
91
96
|
targetVersion: z.ZodString;
|
|
92
97
|
targetInstanceId: z.ZodString;
|
|
98
|
+
transfer: z.ZodOptional<z.ZodLiteral<"copy-verified">>;
|
|
93
99
|
}, z.core.$strict>;
|
|
94
100
|
}, z.core.$strict>, z.ZodObject<{
|
|
95
101
|
action: z.ZodLiteral<"get">;
|
|
@@ -104,12 +110,15 @@ export declare const retainedStoreRequestSchema: z.ZodDiscriminatedUnion<[z.ZodO
|
|
|
104
110
|
}, z.core.$strict>], "action">;
|
|
105
111
|
export type RetainedStoreRequest = z.infer<typeof retainedStoreRequestSchema>;
|
|
106
112
|
/** Capture mutable control files from the current stopped owner before persisting return intent. */
|
|
107
|
-
export declare function prepareRetainedStoreReturn(plan: StoreMovePlan): StoreMovePlan;
|
|
113
|
+
export declare function prepareRetainedStoreReturn(plan: StoreMovePlan, deferContent?: boolean): StoreMovePlan;
|
|
108
114
|
export declare function retainedStoreRoot(defaultBase: string, configured?: string): string;
|
|
109
115
|
export declare function openRetainedStoreLease(binding: StoreBinding): {
|
|
110
116
|
close: () => void;
|
|
111
117
|
};
|
|
112
118
|
export declare function openRetainedStoreMaintenance(directory: string): () => void;
|
|
119
|
+
export declare function prepareRetainedStoreMoveAsync(source: string, root: string, id: string, manifest: RetainedStoreManifest, transfer?: "copy-verified"): Promise<StoreMovePlan>;
|
|
120
|
+
export declare function prepareRetainedStoreReturnAsync(plan: StoreMovePlan): Promise<StoreMovePlan>;
|
|
121
|
+
export declare function applyRetainedStoreMoveAsync(plan: StoreMovePlan, direction: "adopt" | "return", afterMove?: (id: string) => void): Promise<void>;
|
|
113
122
|
export declare const retainedStoreBackupSchema: z.ZodObject<{
|
|
114
123
|
schemaVersion: z.ZodLiteral<1>;
|
|
115
124
|
state: z.ZodEnum<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retained-store.d.ts","sourceRoot":"","sources":["../../src/runtime/retained-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"retained-store.d.ts","sourceRoot":"","sources":["../../src/runtime/retained-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,9 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { available, maintenanceLease, runtimeLease } from "./retained-store-storage.mjs";
|
|
6
|
+
import { inspectRetainedCopy, applyRetainedCopy } from "./retained-store-copy.mjs";
|
|
7
|
+
import { retainedStoreJob } from "./retained-store-job.mjs";
|
|
8
|
+
const copyLimits = () => JSON.parse(fs.readFileSync(new URL("./retained-store-storage-contract.json", import.meta.url), "utf8"));
|
|
6
9
|
const segment = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/);
|
|
7
10
|
const relative = z
|
|
8
11
|
.string()
|
|
@@ -137,7 +140,7 @@ export function writeRetainedStoreJson(file, value) {
|
|
|
137
140
|
export function readRetainedStoreManifest(packageDirectory) {
|
|
138
141
|
return retainedStoreManifestSchema.parse(readRetainedStoreJson(path.join(packageDirectory, RETAINED_STORE_MANIFEST)));
|
|
139
142
|
}
|
|
140
|
-
export function prepareRetainedStoreMove(source, root, storeId, manifest) {
|
|
143
|
+
export function prepareRetainedStoreMove(source, root, storeId, manifest, transfer, deferContent = false) {
|
|
141
144
|
z.uuid().parse(storeId);
|
|
142
145
|
retainedStoreManifestSchema.parse(manifest);
|
|
143
146
|
assertRetainedStorePath(source);
|
|
@@ -151,8 +154,10 @@ export function prepareRetainedStoreMove(source, root, storeId, manifest) {
|
|
|
151
154
|
const rootStat = existingRoot ?? fs.statSync(path.dirname(root)), sourceStat = fs.statSync(source);
|
|
152
155
|
if (root === source || root.startsWith(source + path.sep) || source.startsWith(root + path.sep))
|
|
153
156
|
throw Error("Nested retained store roots");
|
|
154
|
-
if (
|
|
155
|
-
throw Error("
|
|
157
|
+
if (transfer !== undefined && transfer !== "copy-verified")
|
|
158
|
+
throw Error("Unsupported retained transfer");
|
|
159
|
+
if (!transfer && rootStat.dev !== sourceStat.dev)
|
|
160
|
+
throw Error("Retained store requires same filesystem; review an explicit verified copy");
|
|
156
161
|
const moves = [];
|
|
157
162
|
for (const resource of manifest.resources) {
|
|
158
163
|
const file = path.join(source, resource.path);
|
|
@@ -164,13 +169,16 @@ export function prepareRetainedStoreMove(source, root, storeId, manifest) {
|
|
|
164
169
|
continue;
|
|
165
170
|
}
|
|
166
171
|
if (s.uid !== process.getuid?.() ||
|
|
167
|
-
s.dev !== rootStat.dev ||
|
|
172
|
+
(!transfer && s.dev !== rootStat.dev) ||
|
|
168
173
|
s.isSymbolicLink() ||
|
|
169
174
|
(resource.kind === "file" ? !s.isFile() || s.nlink !== 1 : !s.isDirectory()))
|
|
170
175
|
throw Error("Unsafe retained resource");
|
|
171
|
-
moves.push({ id: resource.id, relative: resource.path, kind: resource.kind, device: s.dev, inode: s.ino
|
|
176
|
+
moves.push({ id: resource.id, relative: resource.path, kind: resource.kind, device: s.dev, inode: s.ino,
|
|
177
|
+
...(transfer && !deferContent ? { content: inspectRetainedCopy(file, resource.kind, copyLimits()) } : {}) });
|
|
172
178
|
}
|
|
173
|
-
|
|
179
|
+
if (transfer && (moves.reduce((n, m) => n + (m.content?.bytes ?? 0), 0) > copyLimits().maximumBytes || moves.reduce((n, m) => n + (m.content?.entries ?? 0), 0) > copyLimits().maximumEntries))
|
|
180
|
+
throw Error("Retained copy aggregate limit");
|
|
181
|
+
return { storeId, generation: randomUUID(), format: manifest.format, source, directory, slots: manifest.resources, moves, ...(transfer ? { transfer } : {}) };
|
|
174
182
|
}
|
|
175
183
|
function matches(file, move) {
|
|
176
184
|
assertRetainedStorePath(file);
|
|
@@ -184,7 +192,8 @@ function matches(file, move) {
|
|
|
184
192
|
}
|
|
185
193
|
/** Caller durably records the complete plan and holds its resource reservation first.
|
|
186
194
|
* Each rename is atomic; the multi-resource operation is deliberately resumable. */
|
|
187
|
-
export function applyRetainedStoreMove(plan, direction, afterMove) {
|
|
195
|
+
export function applyRetainedStoreMove(plan, direction, afterMove, onFile, validateOnly = false) {
|
|
196
|
+
const resourceMove = z.object({ id: segment, relative, kind: z.enum(["file", "directory"]), device: z.number().int().nonnegative(), inode: z.number().int().nonnegative(), content: z.object({ digest: z.string().regex(/^[a-f0-9]{64}$/), bytes: z.number().int().nonnegative().max(copyLimits().maximumBytes), entries: z.number().int().positive().max(copyLimits().maximumEntries) }).strict().optional() }).strict();
|
|
188
197
|
z.object({
|
|
189
198
|
storeId: z.uuid(),
|
|
190
199
|
generation: z.uuid(),
|
|
@@ -202,17 +211,9 @@ export function applyRetainedStoreMove(plan, direction, afterMove) {
|
|
|
202
211
|
})
|
|
203
212
|
.strict())
|
|
204
213
|
.max(16),
|
|
205
|
-
moves: z
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
id: segment,
|
|
209
|
-
relative,
|
|
210
|
-
kind: z.enum(["file", "directory"]),
|
|
211
|
-
device: z.number().int().nonnegative(),
|
|
212
|
-
inode: z.number().int().nonnegative(),
|
|
213
|
-
})
|
|
214
|
-
.strict())
|
|
215
|
-
.max(16),
|
|
214
|
+
moves: z.array(resourceMove).max(16),
|
|
215
|
+
transfer: z.literal("copy-verified").optional(),
|
|
216
|
+
originalMoves: z.array(resourceMove).max(16).optional(),
|
|
216
217
|
})
|
|
217
218
|
.strict()
|
|
218
219
|
.parse(plan);
|
|
@@ -234,6 +235,20 @@ export function applyRetainedStoreMove(plan, direction, afterMove) {
|
|
|
234
235
|
const directoryStat = fs.lstatSync(plan.directory);
|
|
235
236
|
if (!directoryStat.isDirectory() || directoryStat.uid !== process.getuid?.() || directoryStat.mode & 0o077)
|
|
236
237
|
throw Error("Unsafe retained store directory");
|
|
238
|
+
if (plan.transfer) {
|
|
239
|
+
if (plan.moves.some(m => !m.content) || plan.originalMoves?.some(m => !m.content))
|
|
240
|
+
throw Error("Verified copy requires reviewed content fingerprints");
|
|
241
|
+
for (const entries of [plan.moves, plan.originalMoves ?? []])
|
|
242
|
+
if (entries.reduce((n, m) => n + (m.content?.bytes ?? 0), 0) > copyLimits().maximumBytes || entries.reduce((n, m) => n + (m.content?.entries ?? 0), 0) > copyLimits().maximumEntries)
|
|
243
|
+
throw Error("Retained copy aggregate limit");
|
|
244
|
+
if ((plan.originalMoves ?? []).some(m => !manifest.resources.some(r => r.id === m.id && r.path === m.relative && r.kind === m.kind)))
|
|
245
|
+
throw Error("Original copy differs from declared resources");
|
|
246
|
+
if (!validateOnly)
|
|
247
|
+
applyRetainedCopy(plan, direction, copyLimits(), afterMove, onFile);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (plan.originalMoves)
|
|
251
|
+
throw Error("Original copy receipt requires verified transfer");
|
|
237
252
|
for (const move of plan.moves) {
|
|
238
253
|
const legacy = path.join(plan.source, move.relative), stable = path.join(plan.directory, move.id);
|
|
239
254
|
const [from, to] = direction === "adopt" ? [legacy, stable] : [stable, legacy];
|
|
@@ -314,6 +329,7 @@ export const retainedStoreSelectionSchema = z
|
|
|
314
329
|
sourceInstanceId: segment,
|
|
315
330
|
targetVersion: segment,
|
|
316
331
|
targetInstanceId: segment,
|
|
332
|
+
transfer: z.literal("copy-verified").optional(),
|
|
317
333
|
})
|
|
318
334
|
.strict()
|
|
319
335
|
.refine((x) => x.sourceVersion !== x.targetVersion || x.sourceInstanceId !== x.targetInstanceId, "Source and target must differ");
|
|
@@ -323,8 +339,25 @@ export const retainedStoreRequestSchema = z.discriminatedUnion("action", [
|
|
|
323
339
|
z.object({ action: z.enum(["adopt", "return"]), id: z.uuid(), reviewedChecksum: z.string().regex(/^[a-f0-9]{64}$/) }).strict(),
|
|
324
340
|
]);
|
|
325
341
|
/** Capture mutable control files from the current stopped owner before persisting return intent. */
|
|
326
|
-
export function prepareRetainedStoreReturn(plan) {
|
|
342
|
+
export function prepareRetainedStoreReturn(plan, deferContent = false) {
|
|
327
343
|
const moves = [];
|
|
344
|
+
if (plan.transfer) {
|
|
345
|
+
for (const slot of plan.slots) {
|
|
346
|
+
const stable = path.join(plan.directory, slot.id), legacy = path.join(plan.source, slot.path), a = retainedStoreEntry(stable), b = retainedStoreEntry(legacy);
|
|
347
|
+
if (a) {
|
|
348
|
+
assertRetainedStorePath(stable);
|
|
349
|
+
moves.push({ id: slot.id, relative: slot.path, kind: slot.kind, device: a.dev, inode: a.ino, ...(!deferContent ? { content: inspectRetainedCopy(stable, slot.kind, copyLimits()) } : {}) });
|
|
350
|
+
}
|
|
351
|
+
else if (b) {
|
|
352
|
+
const original = plan.moves.find(m => m.id === slot.id);
|
|
353
|
+
if (!original || !matches(legacy, original) || (!deferContent && retainedStoreDigest(inspectRetainedCopy(legacy, slot.kind, copyLimits())) !== retainedStoreDigest(original.content)))
|
|
354
|
+
throw Error("Original retained copy changed");
|
|
355
|
+
}
|
|
356
|
+
else if (slot.required)
|
|
357
|
+
throw Error("Current retained resource missing");
|
|
358
|
+
}
|
|
359
|
+
return { ...plan, moves, originalMoves: plan.moves };
|
|
360
|
+
}
|
|
328
361
|
for (const slot of plan.slots) {
|
|
329
362
|
const stable = path.join(plan.directory, slot.id), legacy = path.join(plan.source, slot.path);
|
|
330
363
|
assertRetainedStorePath(stable);
|
|
@@ -359,6 +392,28 @@ export function openRetainedStoreLease(binding) {
|
|
|
359
392
|
export function openRetainedStoreMaintenance(directory) {
|
|
360
393
|
return maintenanceLease(directory);
|
|
361
394
|
}
|
|
395
|
+
export async function prepareRetainedStoreMoveAsync(source, root, id, manifest, transfer) {
|
|
396
|
+
const plan = prepareRetainedStoreMove(source, root, id, manifest, transfer, !!transfer);
|
|
397
|
+
if (transfer)
|
|
398
|
+
for (const move of plan.moves)
|
|
399
|
+
move.content = await retainedStoreJob({ action: "inspect", path: path.join(source, move.relative), kind: move.kind, limits: copyLimits() });
|
|
400
|
+
return plan;
|
|
401
|
+
}
|
|
402
|
+
export async function prepareRetainedStoreReturnAsync(plan) {
|
|
403
|
+
const back = prepareRetainedStoreReturn(plan, !!plan.transfer);
|
|
404
|
+
if (plan.transfer)
|
|
405
|
+
for (const move of back.moves)
|
|
406
|
+
move.content = await retainedStoreJob({ action: "inspect", path: path.join(plan.directory, move.id), kind: move.kind, limits: copyLimits() });
|
|
407
|
+
return back;
|
|
408
|
+
}
|
|
409
|
+
export async function applyRetainedStoreMoveAsync(plan, direction, afterMove) {
|
|
410
|
+
if (!plan.transfer) {
|
|
411
|
+
applyRetainedStoreMove(plan, direction, afterMove);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
applyRetainedStoreMove(plan, direction, undefined, undefined, true);
|
|
415
|
+
await retainedStoreJob({ action: "apply", plan, direction, limits: copyLimits() }, afterMove);
|
|
416
|
+
}
|
|
362
417
|
export const retainedStoreBackupSchema = z
|
|
363
418
|
.object({
|
|
364
419
|
schemaVersion: z.literal(1),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retained-store.js","sourceRoot":"","sources":["../../src/runtime/retained-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\n\nimport { available, maintenanceLease, runtimeLease } from \"./retained-store-storage.mjs\";\n\nconst segment = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/);\nconst relative = z\n .string()\n .min(1)\n .max(256)\n .refine((value) => !path.isAbsolute(value) && !value.includes(\"\\\\\") && value.split(\"/\").every((p) => /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(p)));\nexport const RETAINED_STORE_MANIFEST = \"retained-store.manifest.json\";\nexport const RETAINED_STORE_BINDING = \"retained-store-binding.json\";\nexport const RETAINED_STORE_GUARD = \"retained-store-guard.json\";\nexport const retainedStoreManifestSchema = z\n .object({\n protocol: z.literal(1),\n storageProtocol: z.literal(1).optional(),\n format: segment,\n resources: z\n .array(\n z\n .object({\n id: segment,\n path: relative,\n kind: z.enum([\"file\", \"directory\"]),\n required: z.boolean(),\n backup: z.enum([\"payload\", \"metadata\"]).optional(),\n })\n .strict(),\n )\n .min(1)\n .max(16),\n legacyVersions: z.array(segment).max(32).default([]),\n })\n .strict()\n .superRefine((m, ctx) => {\n if (new Set(m.resources.map((r) => r.id)).size !== m.resources.length) ctx.addIssue({ code: \"custom\", message: \"Duplicate resource ID\" });\n for (const r of m.resources) {\n if ([\"store.json\", \"store-resources.json\", \"store-operation.json\", \"store-lease.sqlite\", \"store-returned.json\"].includes(r.id))\n ctx.addIssue({ code: \"custom\", message: \"Reserved store metadata resource\" });\n if ([\"config.json\", \"controller-state.json\", RETAINED_STORE_BINDING, RETAINED_STORE_GUARD].includes(r.path))\n ctx.addIssue({ code: \"custom\", message: \"Reserved instance resource\" });\n if (m.resources.some((o) => o !== r && (o.path === r.path || o.path.startsWith(r.path + \"/\"))))\n ctx.addIssue({ code: \"custom\", message: \"Overlapping resources\" });\n }\n });\nexport type RetainedStoreManifest = z.infer<typeof retainedStoreManifestSchema>;\nexport type StoreOwner = { rttNode: string; version: string; instanceId: string };\nexport type StoreBinding = {\n protocol: 1;\n storeId: string;\n generation: string;\n operationId: string;\n directory: string;\n format: string;\n configurationDigest: string;\n owner: StoreOwner;\n maintenance: true;\n};\nexport type ResourceMove = { id: string; relative: string; kind: \"file\" | \"directory\"; device: number; inode: number };\nexport type StoreMovePlan = {\n storeId: string;\n generation: string;\n format: string;\n source: string;\n directory: string;\n slots: RetainedStoreManifest[\"resources\"];\n moves: ResourceMove[];\n};\nconst ownerSchema = z.object({ rttNode: segment, version: segment, instanceId: segment }).strict();\nconst bindingSchema = z\n .object({\n protocol: z.literal(1),\n storeId: z.uuid(),\n generation: z.uuid(),\n operationId: z.uuid(),\n directory: z.string().max(4096),\n format: segment,\n configurationDigest: z.string().regex(/^[a-f0-9]{64}$/),\n owner: ownerSchema,\n maintenance: z.literal(true),\n })\n .strict();\nexport function retainedStoreDigest(value: unknown): string {\n const stable = (v: any): any =>\n Array.isArray(v)\n ? v.map(stable)\n : v && typeof v === \"object\"\n ? Object.fromEntries(\n Object.keys(v)\n .sort()\n .map((k) => [k, stable(v[k])]),\n )\n : v;\n return createHash(\"sha256\")\n .update(JSON.stringify(stable(value)))\n .digest(\"hex\");\n}\nexport function retainedStoreEntry(file: string): fs.Stats | undefined {\n try {\n return fs.lstatSync(file);\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw e;\n }\n}\nexport function assertRetainedStorePath(file: string): void {\n if (!path.isAbsolute(file) || path.normalize(file) !== file) throw Error(\"Unnormalized retained store path\");\n for (let p = file; p !== path.dirname(p); p = path.dirname(p)) {\n const s = retainedStoreEntry(p);\n if (s?.isSymbolicLink()) throw Error(\"Linked retained store path\");\n }\n}\nexport function readRetainedStoreJson(file: string): unknown {\n assertRetainedStorePath(file);\n const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);\n try {\n const s = fs.fstatSync(fd);\n if (!s.isFile() || s.nlink !== 1 || s.size > 128 * 1024 || s.uid !== process.getuid?.()) throw Error(\"Unsafe retained store metadata\");\n return JSON.parse(fs.readFileSync(fd, \"utf8\"));\n } finally {\n fs.closeSync(fd);\n }\n}\nexport function syncRetainedDirectory(directory: string): void {\n const fd = fs.openSync(directory, \"r\");\n try {\n fs.fsyncSync(fd);\n } finally {\n fs.closeSync(fd);\n }\n}\nexport function writeRetainedStoreJson(file: string, value: unknown): void {\n assertRetainedStorePath(file);\n const temporary = file + \".\" + randomUUID() + \".tmp\";\n const text = JSON.stringify(value);\n if (Buffer.byteLength(text) > 128 * 1024) throw Error(\"Retained store metadata too large\");\n try {\n const fd = fs.openSync(temporary, \"wx\", 0o600);\n try {\n fs.writeFileSync(fd, text);\n fs.fsyncSync(fd);\n } finally {\n fs.closeSync(fd);\n }\n fs.renameSync(temporary, file);\n syncRetainedDirectory(path.dirname(file));\n } finally {\n if (retainedStoreEntry(temporary)) fs.unlinkSync(temporary);\n }\n}\nexport function readRetainedStoreManifest(packageDirectory: string): RetainedStoreManifest {\n return retainedStoreManifestSchema.parse(readRetainedStoreJson(path.join(packageDirectory, RETAINED_STORE_MANIFEST)));\n}\nexport function prepareRetainedStoreMove(source: string, root: string, storeId: string, manifest: RetainedStoreManifest): StoreMovePlan {\n z.uuid().parse(storeId);\n retainedStoreManifestSchema.parse(manifest);\n assertRetainedStorePath(source);\n assertRetainedStorePath(root);\n const directory = path.join(root, storeId);\n if (retainedStoreEntry(directory)) throw Error(\"Retained store destination exists\");\n const existingRoot = retainedStoreEntry(root);\n if (existingRoot && (!existingRoot.isDirectory() || existingRoot.uid !== process.getuid?.() || existingRoot.mode & 0o077))\n throw Error(\"Retained root must be private and owned\");\n const rootStat = existingRoot ?? fs.statSync(path.dirname(root)),\n sourceStat = fs.statSync(source);\n if (root === source || root.startsWith(source + path.sep) || source.startsWith(root + path.sep)) throw Error(\"Nested retained store roots\");\n if (rootStat.dev !== sourceStat.dev) throw Error(\"Retained store requires same filesystem; copy is not permitted\");\n const moves: ResourceMove[] = [];\n for (const resource of manifest.resources) {\n const file = path.join(source, resource.path);\n assertRetainedStorePath(file);\n const s = retainedStoreEntry(file);\n if (!s) {\n if (resource.required) throw Error(\"Required retained resource missing\");\n continue;\n }\n if (\n s.uid !== process.getuid?.() ||\n s.dev !== rootStat.dev ||\n s.isSymbolicLink() ||\n (resource.kind === \"file\" ? !s.isFile() || s.nlink !== 1 : !s.isDirectory())\n )\n throw Error(\"Unsafe retained resource\");\n moves.push({ id: resource.id, relative: resource.path, kind: resource.kind, device: s.dev, inode: s.ino });\n }\n return { storeId, generation: randomUUID(), format: manifest.format, source, directory, slots: manifest.resources, moves };\n}\nfunction matches(file: string, move: ResourceMove): boolean {\n assertRetainedStorePath(file);\n const s = retainedStoreEntry(file);\n return (\n !!s &&\n !s.isSymbolicLink() &&\n s.dev === move.device &&\n s.ino === move.inode &&\n s.uid === process.getuid?.() &&\n (move.kind === \"file\" ? s.isFile() && s.nlink === 1 : s.isDirectory())\n );\n}\n/** Caller durably records the complete plan and holds its resource reservation first.\n * Each rename is atomic; the multi-resource operation is deliberately resumable. */\nexport function applyRetainedStoreMove(plan: StoreMovePlan, direction: \"adopt\" | \"return\", afterMove?: (id: string) => void): void {\n z.object({\n storeId: z.uuid(),\n generation: z.uuid(),\n format: segment,\n source: z.string().max(4096),\n directory: z.string().max(4096),\n slots: z\n .array(\n z\n .object({\n id: segment,\n path: relative,\n kind: z.enum([\"file\", \"directory\"]),\n required: z.boolean(),\n backup: z.enum([\"payload\", \"metadata\"]).optional(),\n })\n .strict(),\n )\n .max(16),\n moves: z\n .array(\n z\n .object({\n id: segment,\n relative,\n kind: z.enum([\"file\", \"directory\"]),\n device: z.number().int().nonnegative(),\n inode: z.number().int().nonnegative(),\n })\n .strict(),\n )\n .max(16),\n })\n .strict()\n .parse(plan);\n z.enum([\"adopt\", \"return\"]).parse(direction);\n const manifest = retainedStoreManifestSchema.parse({ protocol: 1, format: plan.format, resources: plan.slots });\n if (plan.moves.some((move) => !manifest.resources.some((slot) => slot.id === move.id && slot.path === move.relative && slot.kind === move.kind)))\n throw Error(\"Retained move differs from declared resources\");\n if (\n new Set(plan.moves.map((m) => m.id)).size !== plan.moves.length ||\n plan.moves.some((m) => plan.moves.some((o) => o !== m && (m.relative === o.relative || o.relative.startsWith(m.relative + \"/\"))))\n )\n throw Error(\"Overlapping retained moves\");\n if (plan.directory === plan.source || plan.directory.startsWith(plan.source + path.sep) || plan.source.startsWith(plan.directory + path.sep))\n throw Error(\"Nested retained move\");\n assertRetainedStorePath(plan.source);\n assertRetainedStorePath(plan.directory);\n if (!retainedStoreEntry(plan.directory)) {\n fs.mkdirSync(plan.directory, { mode: 0o700 });\n syncRetainedDirectory(path.dirname(plan.directory));\n }\n const directoryStat = fs.lstatSync(plan.directory);\n if (!directoryStat.isDirectory() || directoryStat.uid !== process.getuid?.() || directoryStat.mode & 0o077)\n throw Error(\"Unsafe retained store directory\");\n for (const move of plan.moves) {\n const legacy = path.join(plan.source, move.relative),\n stable = path.join(plan.directory, move.id);\n const [from, to] = direction === \"adopt\" ? [legacy, stable] : [stable, legacy];\n if (!retainedStoreEntry(from) && matches(to, move)) continue;\n if (!matches(from, move) || retainedStoreEntry(to)) throw Error(\"Retained resource changed or destination is occupied\");\n fs.renameSync(from, to);\n syncRetainedDirectory(path.dirname(from));\n syncRetainedDirectory(path.dirname(to));\n if (!matches(to, move)) throw Error(\"Retained resource identity changed\");\n afterMove?.(move.id);\n }\n}\nexport function writeRetainedStoreBinding(instanceDirectory: string, binding: StoreBinding): void {\n bindingSchema.parse(binding);\n writeRetainedStoreJson(path.join(instanceDirectory, RETAINED_STORE_BINDING), binding);\n}\nexport function loadRetainedStoreBinding(instanceDirectory: string, operationId?: string): StoreBinding | undefined {\n const file = path.join(instanceDirectory, RETAINED_STORE_BINDING);\n if (!retainedStoreEntry(file)) return;\n const s = fs.statSync(file);\n if (s.mode & 0o077) throw Error(\"Retained store binding must be private\");\n const binding = bindingSchema.parse(readRetainedStoreJson(file));\n assertRetainedStorePath(binding.directory);\n const runtimeRoot = process.env[\"UNS_RETAINED_STORE_ROOT\"]?.trim();\n if (runtimeRoot && path.dirname(binding.directory) !== runtimeRoot) throw Error(\"Retained binding does not match the configured Runtime root\");\n const directoryStat = retainedStoreEntry(binding.directory);\n if (!directoryStat?.isDirectory() || directoryStat.uid !== process.getuid?.() || directoryStat.mode & 0o077)\n throw Error(\"Unsafe retained store directory\");\n available(path.dirname(binding.directory));\n if (\n retainedStoreEntry(path.join(binding.directory, \"store-operation.json\")) &&\n (readRetainedStoreJson(path.join(binding.directory, \"store-operation.json\")) as any).operationId !== operationId\n )\n throw Error(\"Retained store operation is incomplete\");\n const meta = bindingSchema.parse(readRetainedStoreJson(path.join(binding.directory, \"store.json\")));\n if (retainedStoreDigest(meta) !== retainedStoreDigest(binding)) throw Error(\"Retained store owner or generation changed\");\n return binding;\n}\n/** No application payload/schema inspection is performed by this helper. */\nexport function resolveRetainedStoreResources(\n instanceDirectory: string,\n manifest: RetainedStoreManifest,\n expected?: Partial<StoreOwner>,\n): { binding?: StoreBinding; resources: Record<string, string> } {\n retainedStoreManifestSchema.parse(manifest);\n if (retainedStoreEntry(path.join(instanceDirectory, RETAINED_STORE_GUARD))) throw Error(\"Retained store operation has fenced this instance\");\n const binding = loadRetainedStoreBinding(instanceDirectory);\n if (\n binding &&\n expected &&\n Object.entries(expected).some(([k, v]) =>\n k === \"version\" ? binding.owner.version.replace(/^v/, \"\") !== String(v).replace(/^v/, \"\") : (binding.owner as any)[k] !== v,\n )\n )\n throw Error(\"Retained binding does not name this runtime\");\n if (binding && binding.format !== manifest.format) throw Error(\"Unsupported retained store format\");\n if (binding) {\n if (manifest.storageProtocol !== 1) throw Error(\"Bound retained stores require the shared storage protocol\");\n const declaration = retainedStoreManifestSchema.parse(readRetainedStoreJson(path.join(binding.directory, \"store-resources.json\")));\n if (\n declaration.storageProtocol !== 1 ||\n declaration.format !== manifest.format ||\n retainedStoreDigest(declaration.resources) !== retainedStoreDigest(manifest.resources)\n )\n throw Error(\"Retained store resource declaration changed\");\n for (const resource of manifest.resources)\n if (resource.required && !retainedStoreEntry(path.join(binding.directory, resource.id))) throw Error(\"Required retained resource missing\");\n }\n return {\n binding,\n resources: Object.fromEntries(\n manifest.resources.map((r) => [r.id, binding ? path.join(binding.directory, r.id) : path.join(instanceDirectory, r.path)]),\n ),\n };\n}\n\nexport const retainedStoreSelectionSchema = z\n .object({\n requestId: z.uuid(),\n rttNode: segment,\n sourceVersion: segment,\n sourceInstanceId: segment,\n targetVersion: segment,\n targetInstanceId: segment,\n })\n .strict()\n .refine((x) => x.sourceVersion !== x.targetVersion || x.sourceInstanceId !== x.targetInstanceId, \"Source and target must differ\");\nexport const retainedStoreRequestSchema = z.discriminatedUnion(\"action\", [\n z.object({ action: z.literal(\"review\"), selection: retainedStoreSelectionSchema }).strict(),\n z.object({ action: z.literal(\"get\"), id: z.uuid() }).strict(),\n z.object({ action: z.enum([\"adopt\", \"return\"]), id: z.uuid(), reviewedChecksum: z.string().regex(/^[a-f0-9]{64}$/) }).strict(),\n]);\nexport type RetainedStoreRequest = z.infer<typeof retainedStoreRequestSchema>;\n\n/** Capture mutable control files from the current stopped owner before persisting return intent. */\nexport function prepareRetainedStoreReturn(plan: StoreMovePlan): StoreMovePlan {\n const moves: ResourceMove[] = [];\n for (const slot of plan.slots) {\n const stable = path.join(plan.directory, slot.id),\n legacy = path.join(plan.source, slot.path);\n assertRetainedStorePath(stable);\n assertRetainedStorePath(legacy);\n const a = retainedStoreEntry(stable),\n b = retainedStoreEntry(legacy);\n if (a && b) throw Error(\"Return destination is occupied\");\n if (a) {\n if (a.uid !== process.getuid?.() || a.isSymbolicLink() || (slot.kind === \"file\" ? !a.isFile() || a.nlink !== 1 : !a.isDirectory()))\n throw Error(\"Unsafe current retained resource\");\n moves.push({ id: slot.id, relative: slot.path, kind: slot.kind, device: a.dev, inode: a.ino });\n } else if (b) {\n const original = plan.moves.find((m) => m.id === slot.id);\n if (!original || !matches(legacy, original)) throw Error(\"Return source identity changed\");\n moves.push(original);\n } else if (slot.required) throw Error(\"Current retained resource missing\");\n }\n return { ...plan, moves };\n}\n\nexport function retainedStoreRoot(defaultBase: string, configured?: string): string {\n const root = configured?.trim() || path.join(defaultBase, \"retained-stores\");\n assertRetainedStorePath(root);\n return root;\n}\nexport function openRetainedStoreLease(binding: StoreBinding): { close: () => void } {\n return runtimeLease(binding);\n}\nexport function openRetainedStoreMaintenance(directory: string): () => void {\n return maintenanceLease(directory);\n}\nexport const retainedStoreBackupSchema = z\n .object({\n schemaVersion: z.literal(1),\n state: z.enum([\"included\", \"excluded\", \"empty\"]),\n rootSha256: z.string().regex(/^[a-f0-9]{64}$/),\n stores: z.number().int().min(0).max(32),\n files: z.number().int().min(0).max(100000),\n bytes: z.number().int().positive().max(68753031168),\n sha256: z.string().regex(/^[a-f0-9]{64}$/),\n })\n .strict();\n"]}
|
|
1
|
+
{"version":3,"file":"retained-store.js","sourceRoot":"","sources":["../../src/runtime/retained-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\n\nimport { available, maintenanceLease, runtimeLease } from \"./retained-store-storage.mjs\";\nimport { inspectRetainedCopy, applyRetainedCopy, type RetainedCopyFingerprint } from \"./retained-store-copy.mjs\";\nimport { retainedStoreJob } from \"./retained-store-job.mjs\";\nconst copyLimits = () => JSON.parse(fs.readFileSync(new URL(\"./retained-store-storage-contract.json\", import.meta.url), \"utf8\"));\n\nconst segment = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/);\nconst relative = z\n .string()\n .min(1)\n .max(256)\n .refine((value) => !path.isAbsolute(value) && !value.includes(\"\\\\\") && value.split(\"/\").every((p) => /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(p)));\nexport const RETAINED_STORE_MANIFEST = \"retained-store.manifest.json\";\nexport const RETAINED_STORE_BINDING = \"retained-store-binding.json\";\nexport const RETAINED_STORE_GUARD = \"retained-store-guard.json\";\nexport const retainedStoreManifestSchema = z\n .object({\n protocol: z.literal(1),\n storageProtocol: z.literal(1).optional(),\n format: segment,\n resources: z\n .array(\n z\n .object({\n id: segment,\n path: relative,\n kind: z.enum([\"file\", \"directory\"]),\n required: z.boolean(),\n backup: z.enum([\"payload\", \"metadata\"]).optional(),\n })\n .strict(),\n )\n .min(1)\n .max(16),\n legacyVersions: z.array(segment).max(32).default([]),\n })\n .strict()\n .superRefine((m, ctx) => {\n if (new Set(m.resources.map((r) => r.id)).size !== m.resources.length) ctx.addIssue({ code: \"custom\", message: \"Duplicate resource ID\" });\n for (const r of m.resources) {\n if ([\"store.json\", \"store-resources.json\", \"store-operation.json\", \"store-lease.sqlite\", \"store-returned.json\"].includes(r.id))\n ctx.addIssue({ code: \"custom\", message: \"Reserved store metadata resource\" });\n if ([\"config.json\", \"controller-state.json\", RETAINED_STORE_BINDING, RETAINED_STORE_GUARD].includes(r.path))\n ctx.addIssue({ code: \"custom\", message: \"Reserved instance resource\" });\n if (m.resources.some((o) => o !== r && (o.path === r.path || o.path.startsWith(r.path + \"/\"))))\n ctx.addIssue({ code: \"custom\", message: \"Overlapping resources\" });\n }\n });\nexport type RetainedStoreManifest = z.infer<typeof retainedStoreManifestSchema>;\nexport type StoreOwner = { rttNode: string; version: string; instanceId: string };\nexport type StoreBinding = {\n protocol: 1;\n storeId: string;\n generation: string;\n operationId: string;\n directory: string;\n format: string;\n configurationDigest: string;\n owner: StoreOwner;\n maintenance: true;\n};\nexport type ResourceMove = { id: string; relative: string; kind: \"file\" | \"directory\"; device: number; inode: number; content?: RetainedCopyFingerprint };\nexport type StoreMovePlan = {\n storeId: string;\n generation: string;\n format: string;\n source: string;\n directory: string;\n slots: RetainedStoreManifest[\"resources\"];\n moves: ResourceMove[];\n transfer?: \"copy-verified\";\n originalMoves?: ResourceMove[];\n};\nconst ownerSchema = z.object({ rttNode: segment, version: segment, instanceId: segment }).strict();\nconst bindingSchema = z\n .object({\n protocol: z.literal(1),\n storeId: z.uuid(),\n generation: z.uuid(),\n operationId: z.uuid(),\n directory: z.string().max(4096),\n format: segment,\n configurationDigest: z.string().regex(/^[a-f0-9]{64}$/),\n owner: ownerSchema,\n maintenance: z.literal(true),\n })\n .strict();\nexport function retainedStoreDigest(value: unknown): string {\n const stable = (v: any): any =>\n Array.isArray(v)\n ? v.map(stable)\n : v && typeof v === \"object\"\n ? Object.fromEntries(\n Object.keys(v)\n .sort()\n .map((k) => [k, stable(v[k])]),\n )\n : v;\n return createHash(\"sha256\")\n .update(JSON.stringify(stable(value)))\n .digest(\"hex\");\n}\nexport function retainedStoreEntry(file: string): fs.Stats | undefined {\n try {\n return fs.lstatSync(file);\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw e;\n }\n}\nexport function assertRetainedStorePath(file: string): void {\n if (!path.isAbsolute(file) || path.normalize(file) !== file) throw Error(\"Unnormalized retained store path\");\n for (let p = file; p !== path.dirname(p); p = path.dirname(p)) {\n const s = retainedStoreEntry(p);\n if (s?.isSymbolicLink()) throw Error(\"Linked retained store path\");\n }\n}\nexport function readRetainedStoreJson(file: string): unknown {\n assertRetainedStorePath(file);\n const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);\n try {\n const s = fs.fstatSync(fd);\n if (!s.isFile() || s.nlink !== 1 || s.size > 128 * 1024 || s.uid !== process.getuid?.()) throw Error(\"Unsafe retained store metadata\");\n return JSON.parse(fs.readFileSync(fd, \"utf8\"));\n } finally {\n fs.closeSync(fd);\n }\n}\nexport function syncRetainedDirectory(directory: string): void {\n const fd = fs.openSync(directory, \"r\");\n try {\n fs.fsyncSync(fd);\n } finally {\n fs.closeSync(fd);\n }\n}\nexport function writeRetainedStoreJson(file: string, value: unknown): void {\n assertRetainedStorePath(file);\n const temporary = file + \".\" + randomUUID() + \".tmp\";\n const text = JSON.stringify(value);\n if (Buffer.byteLength(text) > 128 * 1024) throw Error(\"Retained store metadata too large\");\n try {\n const fd = fs.openSync(temporary, \"wx\", 0o600);\n try {\n fs.writeFileSync(fd, text);\n fs.fsyncSync(fd);\n } finally {\n fs.closeSync(fd);\n }\n fs.renameSync(temporary, file);\n syncRetainedDirectory(path.dirname(file));\n } finally {\n if (retainedStoreEntry(temporary)) fs.unlinkSync(temporary);\n }\n}\nexport function readRetainedStoreManifest(packageDirectory: string): RetainedStoreManifest {\n return retainedStoreManifestSchema.parse(readRetainedStoreJson(path.join(packageDirectory, RETAINED_STORE_MANIFEST)));\n}\nexport function prepareRetainedStoreMove(source: string, root: string, storeId: string, manifest: RetainedStoreManifest, transfer?: \"copy-verified\", deferContent=false): StoreMovePlan {\n z.uuid().parse(storeId);\n retainedStoreManifestSchema.parse(manifest);\n assertRetainedStorePath(source);\n assertRetainedStorePath(root);\n const directory = path.join(root, storeId);\n if (retainedStoreEntry(directory)) throw Error(\"Retained store destination exists\");\n const existingRoot = retainedStoreEntry(root);\n if (existingRoot && (!existingRoot.isDirectory() || existingRoot.uid !== process.getuid?.() || existingRoot.mode & 0o077))\n throw Error(\"Retained root must be private and owned\");\n const rootStat = existingRoot ?? fs.statSync(path.dirname(root)),\n sourceStat = fs.statSync(source);\n if (root === source || root.startsWith(source + path.sep) || source.startsWith(root + path.sep)) throw Error(\"Nested retained store roots\");\n if (transfer !== undefined && transfer !== \"copy-verified\") throw Error(\"Unsupported retained transfer\");\n if (!transfer && rootStat.dev !== sourceStat.dev) throw Error(\"Retained store requires same filesystem; review an explicit verified copy\");\n const moves: ResourceMove[] = [];\n for (const resource of manifest.resources) {\n const file = path.join(source, resource.path);\n assertRetainedStorePath(file);\n const s = retainedStoreEntry(file);\n if (!s) {\n if (resource.required) throw Error(\"Required retained resource missing\");\n continue;\n }\n if (\n s.uid !== process.getuid?.() ||\n (!transfer && s.dev !== rootStat.dev) ||\n s.isSymbolicLink() ||\n (resource.kind === \"file\" ? !s.isFile() || s.nlink !== 1 : !s.isDirectory())\n )\n throw Error(\"Unsafe retained resource\");\n moves.push({ id: resource.id, relative: resource.path, kind: resource.kind, device: s.dev, inode: s.ino,\n ...(transfer&&!deferContent ? {content:inspectRetainedCopy(file,resource.kind,copyLimits())} : {}) });\n }\n if(transfer && (moves.reduce((n,m)=>n+(m.content?.bytes??0),0)>copyLimits().maximumBytes || moves.reduce((n,m)=>n+(m.content?.entries??0),0)>copyLimits().maximumEntries))throw Error(\"Retained copy aggregate limit\");\n return { storeId, generation: randomUUID(), format: manifest.format, source, directory, slots: manifest.resources, moves, ...(transfer?{transfer}:{}) };\n}\nfunction matches(file: string, move: ResourceMove): boolean {\n assertRetainedStorePath(file);\n const s = retainedStoreEntry(file);\n return (\n !!s &&\n !s.isSymbolicLink() &&\n s.dev === move.device &&\n s.ino === move.inode &&\n s.uid === process.getuid?.() &&\n (move.kind === \"file\" ? s.isFile() && s.nlink === 1 : s.isDirectory())\n );\n}\n/** Caller durably records the complete plan and holds its resource reservation first.\n * Each rename is atomic; the multi-resource operation is deliberately resumable. */\nexport function applyRetainedStoreMove(plan: StoreMovePlan, direction: \"adopt\" | \"return\", afterMove?: (id: string) => void, onFile?: (file:string)=>void, validateOnly=false): void {\n const resourceMove = z.object({id:segment,relative,kind:z.enum([\"file\",\"directory\"]),device:z.number().int().nonnegative(),inode:z.number().int().nonnegative(),content:z.object({digest:z.string().regex(/^[a-f0-9]{64}$/),bytes:z.number().int().nonnegative().max(copyLimits().maximumBytes),entries:z.number().int().positive().max(copyLimits().maximumEntries)}).strict().optional()}).strict();\n z.object({\n storeId: z.uuid(),\n generation: z.uuid(),\n format: segment,\n source: z.string().max(4096),\n directory: z.string().max(4096),\n slots: z\n .array(\n z\n .object({\n id: segment,\n path: relative,\n kind: z.enum([\"file\", \"directory\"]),\n required: z.boolean(),\n backup: z.enum([\"payload\", \"metadata\"]).optional(),\n })\n .strict(),\n )\n .max(16),\n moves:z.array(resourceMove).max(16),\n transfer:z.literal(\"copy-verified\").optional(),\n originalMoves:z.array(resourceMove).max(16).optional(),\n })\n .strict()\n .parse(plan);\n z.enum([\"adopt\", \"return\"]).parse(direction);\n const manifest = retainedStoreManifestSchema.parse({ protocol: 1, format: plan.format, resources: plan.slots });\n if (plan.moves.some((move) => !manifest.resources.some((slot) => slot.id === move.id && slot.path === move.relative && slot.kind === move.kind)))\n throw Error(\"Retained move differs from declared resources\");\n if (\n new Set(plan.moves.map((m) => m.id)).size !== plan.moves.length ||\n plan.moves.some((m) => plan.moves.some((o) => o !== m && (m.relative === o.relative || o.relative.startsWith(m.relative + \"/\"))))\n )\n throw Error(\"Overlapping retained moves\");\n if (plan.directory === plan.source || plan.directory.startsWith(plan.source + path.sep) || plan.source.startsWith(plan.directory + path.sep))\n throw Error(\"Nested retained move\");\n assertRetainedStorePath(plan.source);\n assertRetainedStorePath(plan.directory);\n if (!retainedStoreEntry(plan.directory)) {\n fs.mkdirSync(plan.directory, { mode: 0o700 });\n syncRetainedDirectory(path.dirname(plan.directory));\n }\n const directoryStat = fs.lstatSync(plan.directory);\n if (!directoryStat.isDirectory() || directoryStat.uid !== process.getuid?.() || directoryStat.mode & 0o077)\n throw Error(\"Unsafe retained store directory\");\n if(plan.transfer){\n if(plan.moves.some(m=>!m.content) || plan.originalMoves?.some(m=>!m.content))throw Error(\"Verified copy requires reviewed content fingerprints\");\n for(const entries of [plan.moves,plan.originalMoves??[]])if(entries.reduce((n,m)=>n+(m.content?.bytes??0),0)>copyLimits().maximumBytes||entries.reduce((n,m)=>n+(m.content?.entries??0),0)>copyLimits().maximumEntries)throw Error(\"Retained copy aggregate limit\");\n if((plan.originalMoves??[]).some(m=>!manifest.resources.some(r=>r.id===m.id&&r.path===m.relative&&r.kind===m.kind)))throw Error(\"Original copy differs from declared resources\");\n if(!validateOnly)applyRetainedCopy(plan,direction,copyLimits(),afterMove,onFile); return;\n }\n if(plan.originalMoves)throw Error(\"Original copy receipt requires verified transfer\");\n for (const move of plan.moves) {\n const legacy = path.join(plan.source, move.relative),\n stable = path.join(plan.directory, move.id);\n const [from, to] = direction === \"adopt\" ? [legacy, stable] : [stable, legacy];\n if (!retainedStoreEntry(from) && matches(to, move)) continue;\n if (!matches(from, move) || retainedStoreEntry(to)) throw Error(\"Retained resource changed or destination is occupied\");\n fs.renameSync(from, to);\n syncRetainedDirectory(path.dirname(from));\n syncRetainedDirectory(path.dirname(to));\n if (!matches(to, move)) throw Error(\"Retained resource identity changed\");\n afterMove?.(move.id);\n }\n}\nexport function writeRetainedStoreBinding(instanceDirectory: string, binding: StoreBinding): void {\n bindingSchema.parse(binding);\n writeRetainedStoreJson(path.join(instanceDirectory, RETAINED_STORE_BINDING), binding);\n}\nexport function loadRetainedStoreBinding(instanceDirectory: string, operationId?: string): StoreBinding | undefined {\n const file = path.join(instanceDirectory, RETAINED_STORE_BINDING);\n if (!retainedStoreEntry(file)) return;\n const s = fs.statSync(file);\n if (s.mode & 0o077) throw Error(\"Retained store binding must be private\");\n const binding = bindingSchema.parse(readRetainedStoreJson(file));\n assertRetainedStorePath(binding.directory);\n const runtimeRoot = process.env[\"UNS_RETAINED_STORE_ROOT\"]?.trim();\n if (runtimeRoot && path.dirname(binding.directory) !== runtimeRoot) throw Error(\"Retained binding does not match the configured Runtime root\");\n const directoryStat = retainedStoreEntry(binding.directory);\n if (!directoryStat?.isDirectory() || directoryStat.uid !== process.getuid?.() || directoryStat.mode & 0o077)\n throw Error(\"Unsafe retained store directory\");\n available(path.dirname(binding.directory));\n if (\n retainedStoreEntry(path.join(binding.directory, \"store-operation.json\")) &&\n (readRetainedStoreJson(path.join(binding.directory, \"store-operation.json\")) as any).operationId !== operationId\n )\n throw Error(\"Retained store operation is incomplete\");\n const meta = bindingSchema.parse(readRetainedStoreJson(path.join(binding.directory, \"store.json\")));\n if (retainedStoreDigest(meta) !== retainedStoreDigest(binding)) throw Error(\"Retained store owner or generation changed\");\n return binding;\n}\n/** No application payload/schema inspection is performed by this helper. */\nexport function resolveRetainedStoreResources(\n instanceDirectory: string,\n manifest: RetainedStoreManifest,\n expected?: Partial<StoreOwner>,\n): { binding?: StoreBinding; resources: Record<string, string> } {\n retainedStoreManifestSchema.parse(manifest);\n if (retainedStoreEntry(path.join(instanceDirectory, RETAINED_STORE_GUARD))) throw Error(\"Retained store operation has fenced this instance\");\n const binding = loadRetainedStoreBinding(instanceDirectory);\n if (\n binding &&\n expected &&\n Object.entries(expected).some(([k, v]) =>\n k === \"version\" ? binding.owner.version.replace(/^v/, \"\") !== String(v).replace(/^v/, \"\") : (binding.owner as any)[k] !== v,\n )\n )\n throw Error(\"Retained binding does not name this runtime\");\n if (binding && binding.format !== manifest.format) throw Error(\"Unsupported retained store format\");\n if (binding) {\n if (manifest.storageProtocol !== 1) throw Error(\"Bound retained stores require the shared storage protocol\");\n const declaration = retainedStoreManifestSchema.parse(readRetainedStoreJson(path.join(binding.directory, \"store-resources.json\")));\n if (\n declaration.storageProtocol !== 1 ||\n declaration.format !== manifest.format ||\n retainedStoreDigest(declaration.resources) !== retainedStoreDigest(manifest.resources)\n )\n throw Error(\"Retained store resource declaration changed\");\n for (const resource of manifest.resources)\n if (resource.required && !retainedStoreEntry(path.join(binding.directory, resource.id))) throw Error(\"Required retained resource missing\");\n }\n return {\n binding,\n resources: Object.fromEntries(\n manifest.resources.map((r) => [r.id, binding ? path.join(binding.directory, r.id) : path.join(instanceDirectory, r.path)]),\n ),\n };\n}\n\nexport const retainedStoreSelectionSchema = z\n .object({\n requestId: z.uuid(),\n rttNode: segment,\n sourceVersion: segment,\n sourceInstanceId: segment,\n targetVersion: segment,\n targetInstanceId: segment,\n transfer:z.literal(\"copy-verified\").optional(),\n })\n .strict()\n .refine((x) => x.sourceVersion !== x.targetVersion || x.sourceInstanceId !== x.targetInstanceId, \"Source and target must differ\");\nexport const retainedStoreRequestSchema = z.discriminatedUnion(\"action\", [\n z.object({ action: z.literal(\"review\"), selection: retainedStoreSelectionSchema }).strict(),\n z.object({ action: z.literal(\"get\"), id: z.uuid() }).strict(),\n z.object({ action: z.enum([\"adopt\", \"return\"]), id: z.uuid(), reviewedChecksum: z.string().regex(/^[a-f0-9]{64}$/) }).strict(),\n]);\nexport type RetainedStoreRequest = z.infer<typeof retainedStoreRequestSchema>;\n\n/** Capture mutable control files from the current stopped owner before persisting return intent. */\nexport function prepareRetainedStoreReturn(plan: StoreMovePlan,deferContent=false): StoreMovePlan {\n const moves: ResourceMove[] = [];\n if(plan.transfer){\n for(const slot of plan.slots){\n const stable=path.join(plan.directory,slot.id),legacy=path.join(plan.source,slot.path),a=retainedStoreEntry(stable),b=retainedStoreEntry(legacy);\n if(a){assertRetainedStorePath(stable);moves.push({id:slot.id,relative:slot.path,kind:slot.kind,device:a.dev,inode:a.ino,...(!deferContent?{content:inspectRetainedCopy(stable,slot.kind,copyLimits())}:{})});}\n else if(b){const original=plan.moves.find(m=>m.id===slot.id);if(!original||!matches(legacy,original)||(!deferContent&&retainedStoreDigest(inspectRetainedCopy(legacy,slot.kind,copyLimits()))!==retainedStoreDigest(original.content)))throw Error(\"Original retained copy changed\");}\n else if(slot.required)throw Error(\"Current retained resource missing\");\n }\n return{...plan,moves,originalMoves:plan.moves};\n }\n for (const slot of plan.slots) {\n const stable = path.join(plan.directory, slot.id),\n legacy = path.join(plan.source, slot.path);\n assertRetainedStorePath(stable);\n assertRetainedStorePath(legacy);\n const a = retainedStoreEntry(stable),\n b = retainedStoreEntry(legacy);\n if (a && b) throw Error(\"Return destination is occupied\");\n if (a) {\n if (a.uid !== process.getuid?.() || a.isSymbolicLink() || (slot.kind === \"file\" ? !a.isFile() || a.nlink !== 1 : !a.isDirectory()))\n throw Error(\"Unsafe current retained resource\");\n moves.push({ id: slot.id, relative: slot.path, kind: slot.kind, device: a.dev, inode: a.ino });\n } else if (b) {\n const original = plan.moves.find((m) => m.id === slot.id);\n if (!original || !matches(legacy, original)) throw Error(\"Return source identity changed\");\n moves.push(original);\n } else if (slot.required) throw Error(\"Current retained resource missing\");\n }\n return { ...plan, moves };\n}\n\nexport function retainedStoreRoot(defaultBase: string, configured?: string): string {\n const root = configured?.trim() || path.join(defaultBase, \"retained-stores\");\n assertRetainedStorePath(root);\n return root;\n}\nexport function openRetainedStoreLease(binding: StoreBinding): { close: () => void } {\n return runtimeLease(binding);\n}\nexport function openRetainedStoreMaintenance(directory: string): () => void {\n return maintenanceLease(directory);\n}\nexport async function prepareRetainedStoreMoveAsync(source:string,root:string,id:string,manifest:RetainedStoreManifest,transfer?:\"copy-verified\"):Promise<StoreMovePlan>{\n const plan=prepareRetainedStoreMove(source,root,id,manifest,transfer,!!transfer);\n if(transfer)for(const move of plan.moves)move.content=await retainedStoreJob({action:\"inspect\",path:path.join(source,move.relative),kind:move.kind,limits:copyLimits()}) as RetainedCopyFingerprint;\n return plan;\n}\nexport async function prepareRetainedStoreReturnAsync(plan:StoreMovePlan):Promise<StoreMovePlan>{\n const back=prepareRetainedStoreReturn(plan,!!plan.transfer);\n if(plan.transfer)for(const move of back.moves)move.content=await retainedStoreJob({action:\"inspect\",path:path.join(plan.directory,move.id),kind:move.kind,limits:copyLimits()}) as RetainedCopyFingerprint;\n return back;\n}\nexport async function applyRetainedStoreMoveAsync(plan:StoreMovePlan,direction:\"adopt\"|\"return\",afterMove?:(id:string)=>void):Promise<void>{\n if(!plan.transfer){applyRetainedStoreMove(plan,direction,afterMove);return;}\n applyRetainedStoreMove(plan,direction,undefined,undefined,true);\n await retainedStoreJob({action:\"apply\",plan,direction,limits:copyLimits()},afterMove);\n}\nexport const retainedStoreBackupSchema = z\n .object({\n schemaVersion: z.literal(1),\n state: z.enum([\"included\", \"excluded\", \"empty\"]),\n rootSha256: z.string().regex(/^[a-f0-9]{64}$/),\n stores: z.number().int().min(0).max(32),\n files: z.number().int().min(0).max(100000),\n bytes: z.number().int().positive().max(68753031168),\n sha256: z.string().regex(/^[a-f0-9]{64}$/),\n })\n .strict();\n"]}
|