@run402/sdk 4.39.1 → 4.41.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.
- package/README.md +4 -4
- package/dist/errors.d.ts +5 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/namespaces/gitvault.crypto.d.ts +10 -0
- package/dist/namespaces/gitvault.crypto.d.ts.map +1 -1
- package/dist/namespaces/gitvault.crypto.js +10 -0
- package/dist/namespaces/gitvault.crypto.js.map +1 -1
- package/dist/namespaces/gitvault.d.ts +285 -5
- package/dist/namespaces/gitvault.d.ts.map +1 -1
- package/dist/namespaces/gitvault.js +369 -8
- package/dist/namespaces/gitvault.js.map +1 -1
- package/dist/node/gitvault-apply.d.ts +1 -1
- package/dist/node/gitvault-apply.d.ts.map +1 -1
- package/dist/node/gitvault-apply.js +6 -6
- package/dist/node/gitvault-apply.js.map +1 -1
- package/dist/node/gitvault-creation-journal.js +1 -1
- package/dist/node/gitvault-creation-journal.js.map +1 -1
- package/dist/node/gitvault-deploy.js +2 -2
- package/dist/node/gitvault-deploy.js.map +1 -1
- package/dist/node/gitvault-keystore.d.ts +11 -1
- package/dist/node/gitvault-keystore.d.ts.map +1 -1
- package/dist/node/gitvault-keystore.js.map +1 -1
- package/dist/node/gitvault-mirror-backend.d.ts +88 -0
- package/dist/node/gitvault-mirror-backend.d.ts.map +1 -0
- package/dist/node/gitvault-mirror-backend.js +374 -0
- package/dist/node/gitvault-mirror-backend.js.map +1 -0
- package/dist/node/gitvault-mirror-config.d.ts +53 -0
- package/dist/node/gitvault-mirror-config.d.ts.map +1 -0
- package/dist/node/gitvault-mirror-config.js +112 -0
- package/dist/node/gitvault-mirror-config.js.map +1 -0
- package/dist/node/gitvault-mirror.d.ts +120 -0
- package/dist/node/gitvault-mirror.d.ts.map +1 -0
- package/dist/node/gitvault-mirror.js +464 -0
- package/dist/node/gitvault-mirror.js.map +1 -0
- package/dist/node/gitvault-publication.d.ts +153 -3
- package/dist/node/gitvault-publication.d.ts.map +1 -1
- package/dist/node/gitvault-publication.js +182 -13
- package/dist/node/gitvault-publication.js.map +1 -1
- package/dist/node/gitvault-recover.d.ts +136 -0
- package/dist/node/gitvault-recover.d.ts.map +1 -0
- package/dist/node/gitvault-recover.js +412 -0
- package/dist/node/gitvault-recover.js.map +1 -0
- package/dist/node/gitvault-snapshot.d.ts +8 -0
- package/dist/node/gitvault-snapshot.d.ts.map +1 -1
- package/dist/node/gitvault-snapshot.js +11 -0
- package/dist/node/gitvault-snapshot.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gitvault-mirror-and-recover — the mirror writer + reconcile/sync engine +
|
|
3
|
+
* capture-time dual-push hook (design D1/D6/D7, tasks 2.2–2.4).
|
|
4
|
+
*
|
|
5
|
+
* ADMISSION-ORDER WRITE DISCIPLINE (D1's whole point). The gateway's objects
|
|
6
|
+
* listing (`GET /gitvault/v1/vaults/:vault_id/objects`) is a flat, unordered
|
|
7
|
+
* set of `{key, object_kind, sha256, size_bytes}` — it does not group objects
|
|
8
|
+
* by generation. The correct, SUFFICIENT ordering rule does not need that
|
|
9
|
+
* grouping: write EVERY plain object first (any order among themselves — a
|
|
10
|
+
* WAL pack, a checkpoint pack, a prune intent never depend on one another),
|
|
11
|
+
* THEN write every `admissions/<gen>` and `head/<gen>` pair, interleaved and
|
|
12
|
+
* strictly ascending by generation, admission always immediately before its
|
|
13
|
+
* head. Any interruption therefore lands the mirror in one of exactly two
|
|
14
|
+
* shapes:
|
|
15
|
+
* - mid-objects: no admission/head has been written yet, so nothing in the
|
|
16
|
+
* mirror references anything absent — a completely inert, valid state
|
|
17
|
+
* (recovery just sees the mirror's newest COMPLETE prior generation, if
|
|
18
|
+
* any was mirrored on an earlier pass).
|
|
19
|
+
* - mid-admissions/heads at generation K: every object is present (phase
|
|
20
|
+
* one finished), every generation < K is fully written (admission +
|
|
21
|
+
* head), and K itself has AT MOST its admission record without its head
|
|
22
|
+
* — which recovery reads exactly as "the mirror's newest generation is
|
|
23
|
+
* K-1", a completely ordinary and valid earlier state (protocol §5A step
|
|
24
|
+
* 4: a record existing without its head PUT is a normal in-flight shape,
|
|
25
|
+
* never corruption).
|
|
26
|
+
* This is exactly the "torn mirror ≡ some earlier valid generation" property
|
|
27
|
+
* design D1 requires, and it needs no per-generation object grouping to prove.
|
|
28
|
+
*
|
|
29
|
+
* Read path: `GET …/objects` (paginated) for the listing, then the SAME
|
|
30
|
+
* presigned-read machinery the SDK's publication module uses for live pushes
|
|
31
|
+
* (`POST …/object-reads` for object-kind reads, `GET …/heads/:gen` /
|
|
32
|
+
* `GET …/admissions/:gen` for the two generation-addressed raw-byte routes) —
|
|
33
|
+
* reimplemented here as a THIN, self-contained reader (not exported from
|
|
34
|
+
* `gitvault-publication.ts`, which keeps those helpers module-private) so
|
|
35
|
+
* this file has no dependency on the live-push module and works from nothing
|
|
36
|
+
* but a `Client` + `repo_id`.
|
|
37
|
+
*
|
|
38
|
+
* MIRROR LAYOUT (task 5.3 fix — do not lose this again). The gateway's real
|
|
39
|
+
* bucket key for every stored artifact is FULLY QUALIFIED under the vault's
|
|
40
|
+
* own prefix (`storage-keys.ts` `vaultPrefix`): `source/<repo_id>/<rest>`,
|
|
41
|
+
* confirmed live 2026-08-26 (`gitvault-mirror-drill.mjs` against
|
|
42
|
+
* `api.run402.com`). The gateway's own `GET …/objects` listing (task 1.2)
|
|
43
|
+
* echoes that FULL key back on every entry — it never returns a
|
|
44
|
+
* repo-relative key. This module's own working representation, and the
|
|
45
|
+
* mirror BACKEND's key space (`gitvault-mirror-backend.ts`
|
|
46
|
+
* `openGitvaultMirrorBackend`, whose `root`/`prefix` already bake in
|
|
47
|
+
* `source/<repo_id>`), are both REPO-RELATIVE (`head/<gen>`,
|
|
48
|
+
* `wal/<id>.pack.enc`, `key-envelopes/<epoch>/<fp>.env`, …) — the same shape
|
|
49
|
+
* `gitvault-recover.ts` and `gitvault-publication.ts`'s `gitvaultPaths`
|
|
50
|
+
* already use. The mapping is therefore, in ONE place:
|
|
51
|
+
*
|
|
52
|
+
* bucket key = `source/<repo_id>/` + mirror key (repo-relative)
|
|
53
|
+
*
|
|
54
|
+
* `listGitvaultObjectsAll` is the ONLY function that ever sees a full,
|
|
55
|
+
* `source/<repo_id>/`-prefixed key (the raw wire shape, typed
|
|
56
|
+
* `RawVaultObjectEntry` below) — it strips the prefix via
|
|
57
|
+
* `stripSourcePrefix` before anything else in this module (or in
|
|
58
|
+
* `gitvault-recover.ts`, which never talks to the live gateway at all) sees
|
|
59
|
+
* the entry. Every `GitvaultObjectEntry` handed to `generationRouteForKey`,
|
|
60
|
+
* `objectReadRequestForEntry`, `planMirrorWrite`, `reconcileOne`, and the
|
|
61
|
+
* `GitvaultMirrorBackend` itself is repo-relative from that point on. An
|
|
62
|
+
* entry whose key names a DIFFERENT repo id than the one being synced is a
|
|
63
|
+
* listing-isolation violation and is refused loudly (never silently
|
|
64
|
+
* stripped-and-kept or silently dropped) — see `stripSourcePrefix`.
|
|
65
|
+
*
|
|
66
|
+
* Two chain kinds are part of this SAME listing (the pre-fix gateway omitted
|
|
67
|
+
* them entirely, which was task 5.3 finding (a); the listing now includes
|
|
68
|
+
* them end to end), both fetched via the exact-bytes generation routes
|
|
69
|
+
* (`.../heads/:generation`, `.../admissions/:generation`), never through
|
|
70
|
+
* `object-reads`:
|
|
71
|
+
* - `head/<generation>` — `object_kind: "head"` for every ordinary
|
|
72
|
+
* generation, but the GENESIS entry at generation `0000000000000000`
|
|
73
|
+
* carries `object_kind: "vault_genesis"` (the ledger's own
|
|
74
|
+
* `admitted_object_kind`) instead. Both route through the SAME
|
|
75
|
+
* `.../heads/:generation` fetch — the kind distinguishes the CONTENT,
|
|
76
|
+
* not the route.
|
|
77
|
+
* - `admissions/<generation>` — `object_kind: "admission_record"` at
|
|
78
|
+
* EVERY generation, including genesis (genesis has an admission record
|
|
79
|
+
* too).
|
|
80
|
+
* `size_bytes` is an EXACT decimal string for every kind, including these
|
|
81
|
+
* two (the ledger enforces the admitted byte count via CHECK constraints and
|
|
82
|
+
* the gateway serves it as `octet_length`) — never `null`. `reconcileOne`
|
|
83
|
+
* still diffs by size first (cheap) and hash-verifies on every actual copy
|
|
84
|
+
* (belt-and-suspenders, unconditional — never skipped just because a size
|
|
85
|
+
* happened to match).
|
|
86
|
+
*/
|
|
87
|
+
import { createHash } from "node:crypto";
|
|
88
|
+
import { LocalError, isRun402Error } from "../errors.js";
|
|
89
|
+
import { GITVAULT_GENESIS_GENERATION, GITVAULT_MIRROR_KEYSTORE_STILL_REQUIRED_STATEMENT, GITVAULT_MIRROR_VALIDITY_NOT_FRESHNESS_STATEMENT, } from "../namespaces/gitvault.crypto.js";
|
|
90
|
+
import { GitvaultKeystore } from "./gitvault-keystore.js";
|
|
91
|
+
import { formatMirrorDestination, readMirrorConfig } from "./gitvault-mirror-config.js";
|
|
92
|
+
import { openGitvaultMirrorBackend, resolveMirrorCredentials } from "./gitvault-mirror-backend.js";
|
|
93
|
+
function fail(code, message, context, details, nextActions) {
|
|
94
|
+
throw new LocalError(message, context, { code, details, ...(nextActions ? { next_actions: nextActions } : {}) });
|
|
95
|
+
}
|
|
96
|
+
function sha256Hex(bytes) {
|
|
97
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
98
|
+
}
|
|
99
|
+
/** protocol §3 (`storage-keys.ts` `GITVAULT_SOURCE_PREFIX`) — duplicated here because the SDK carries no dependency on the gateway package. */
|
|
100
|
+
const GITVAULT_SOURCE_PREFIX = "source";
|
|
101
|
+
/**
|
|
102
|
+
* Turn one WIRE key (`source/<repo_id>/<rest>`) into the REPO-RELATIVE key
|
|
103
|
+
* every other function in this module expects. A key naming a DIFFERENT repo
|
|
104
|
+
* id than the one being synced is a listing-isolation violation — refused
|
|
105
|
+
* loudly rather than silently stripped-and-kept (it could only mean a
|
|
106
|
+
* gateway bug or a cross-tenant data leak, and a partial sync that quietly
|
|
107
|
+
* dropped it would misreport as a clean success). A key with no `source/`
|
|
108
|
+
* prefix at all is a listing-shape violation of the same severity.
|
|
109
|
+
*/
|
|
110
|
+
function stripSourcePrefix(repoId, key) {
|
|
111
|
+
const expected = `${GITVAULT_SOURCE_PREFIX}/${repoId}/`;
|
|
112
|
+
if (key.startsWith(expected))
|
|
113
|
+
return key.slice(expected.length);
|
|
114
|
+
const foreign = /^source\/([^/]+)\//.exec(key);
|
|
115
|
+
if (foreign) {
|
|
116
|
+
fail("GITVAULT_MIRROR_FOREIGN_REPO_KEY", `the objects listing for ${repoId} returned a key addressed to a different vault (${foreign[1]}): ${key}`, "listing gitvault vault objects", { key, repo_id: repoId, foreign_repo_id: foreign[1] });
|
|
117
|
+
}
|
|
118
|
+
fail("GITVAULT_MIRROR_LISTING_INCONSISTENT", `listed key is not in the expected source/<repo_id>/… layout: ${key}`, "listing gitvault vault objects", { key, repo_id: repoId });
|
|
119
|
+
}
|
|
120
|
+
/** Page through `GET /gitvault/v1/vaults/:vault_id/objects`, collecting every entry, stripped to repo-relative keys. Store-and-echo cursor per D3a — never parsed here either. */
|
|
121
|
+
export async function listGitvaultObjectsAll(client, repoId) {
|
|
122
|
+
const base = `/gitvault/v1/vaults/${encodeURIComponent(repoId)}/objects`;
|
|
123
|
+
const out = [];
|
|
124
|
+
let cursor;
|
|
125
|
+
for (;;) {
|
|
126
|
+
const qs = cursor ? `?cursor=${encodeURIComponent(cursor)}` : "";
|
|
127
|
+
const page = await client.request(`${base}${qs}`, { context: "listing gitvault vault objects" });
|
|
128
|
+
for (const raw of page.objects) {
|
|
129
|
+
out.push({ key: stripSourcePrefix(repoId, raw.key), object_kind: raw.object_kind, sha256: raw.sha256, size_bytes: raw.size_bytes });
|
|
130
|
+
}
|
|
131
|
+
if (!page.has_more)
|
|
132
|
+
break;
|
|
133
|
+
if (!page.next_cursor)
|
|
134
|
+
fail("GITVAULT_MIRROR_LISTING_INCONSISTENT", "the objects listing reported has_more with no next_cursor", "listing gitvault vault objects");
|
|
135
|
+
cursor = page.next_cursor;
|
|
136
|
+
}
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
/** Read one object's bytes by key, using the same wire identity every stored kind already carries (§3 layout). `null` when the gateway reports the object absent. */
|
|
140
|
+
export async function readGitvaultObjectBytes(client, repoId, entry) {
|
|
141
|
+
const base = `/gitvault/v1/vaults/${encodeURIComponent(repoId)}`;
|
|
142
|
+
const generationRoute = generationRouteForKey(entry.key);
|
|
143
|
+
if (generationRoute) {
|
|
144
|
+
// Defense in depth: the key SHAPE alone is enough to route the read, but
|
|
145
|
+
// cross-checking the listing's own declared object_kind catches a
|
|
146
|
+
// mislabeled entry (gateway bug) loudly instead of silently reading the
|
|
147
|
+
// wrong thing under the right generation. `admissions/<gen>` is always
|
|
148
|
+
// `admission_record`; `head/<gen>` is `vault_genesis` ONLY at generation
|
|
149
|
+
// zero and `head` at every later generation — the kind names the
|
|
150
|
+
// CONTENT, both fetch through the same route.
|
|
151
|
+
const expectedKind = generationRoute.route === "admissions" ? "admission_record" : generationRoute.generation === GITVAULT_GENESIS_GENERATION ? "vault_genesis" : "head";
|
|
152
|
+
if (entry.object_kind !== expectedKind) {
|
|
153
|
+
fail("GITVAULT_MIRROR_KEY_UNRECOGNIZED", `listed object_kind '${entry.object_kind}' does not match its generation-addressed key ${entry.key} (expected '${expectedKind}')`, "reading gitvault mirror source object", { key: entry.key, object_kind: entry.object_kind, expected_object_kind: expectedKind });
|
|
154
|
+
}
|
|
155
|
+
const path = `${base}/${generationRoute.route}/${encodeURIComponent(generationRoute.generation)}`;
|
|
156
|
+
const auth = (await client.credentials.getAuth(path, { method: `gitvault.read_${generationRoute.route}` })) ?? {};
|
|
157
|
+
const r = await client.fetch(`${client.apiBase}${path}`, { method: "GET", headers: { ...auth, accept: "application/json" } });
|
|
158
|
+
if (r.status === 404)
|
|
159
|
+
return null;
|
|
160
|
+
if (!r.ok)
|
|
161
|
+
fail("GITVAULT_MIRROR_READ_FAILED", `${entry.key} read failed (HTTP ${r.status})`, "reading gitvault mirror source object", { key: entry.key, status: r.status });
|
|
162
|
+
return new Uint8Array(await r.arrayBuffer());
|
|
163
|
+
}
|
|
164
|
+
const read = objectReadRequestForEntry(entry);
|
|
165
|
+
let presigned;
|
|
166
|
+
try {
|
|
167
|
+
presigned = await client.request(`${base}/object-reads`, { method: "POST", body: { objects: [read] }, context: "resolving gitvault mirror source object" });
|
|
168
|
+
}
|
|
169
|
+
catch (e) {
|
|
170
|
+
if (isRun402Error(e) && (e.status === 404 || e.code === "RESOURCE_NOT_FOUND"))
|
|
171
|
+
return null;
|
|
172
|
+
throw e;
|
|
173
|
+
}
|
|
174
|
+
const target = presigned.reads[0];
|
|
175
|
+
if (!target)
|
|
176
|
+
return null;
|
|
177
|
+
const r = await client.fetch(target.url, { method: "GET" });
|
|
178
|
+
if (r.status === 404)
|
|
179
|
+
return null;
|
|
180
|
+
if (!r.ok)
|
|
181
|
+
fail("GITVAULT_MIRROR_READ_FAILED", `${entry.key} GET failed (HTTP ${r.status})`, "reading gitvault mirror source object", { key: entry.key, status: r.status });
|
|
182
|
+
return new Uint8Array(await r.arrayBuffer());
|
|
183
|
+
}
|
|
184
|
+
/** Exported for the key-shape conformance test (`gitvault-mirror-recover.test.ts`) — never called for its own sake outside this module. */
|
|
185
|
+
export function generationRouteForKey(key) {
|
|
186
|
+
const head = /^head\/([0-9a-f]{16})$/.exec(key);
|
|
187
|
+
if (head)
|
|
188
|
+
return { route: "heads", generation: head[1] };
|
|
189
|
+
const adm = /^admissions\/([0-9a-f]{16})$/.exec(key);
|
|
190
|
+
if (adm)
|
|
191
|
+
return { route: "admissions", generation: adm[1] };
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Repo-relative key shapes for every id-addressed, non-generation-addressed
|
|
196
|
+
* kind (`storage-keys.ts` `objectKeyFor` + `upload-sessions.ts`
|
|
197
|
+
* `UPLOADABLE_KINDS`, mirrored here since the SDK has no dependency on the
|
|
198
|
+
* gateway package). ANCHORED (`^…$`) on purpose — the bug this table
|
|
199
|
+
* replaces (task 5.3 finding (b)) was an UNANCHORED fallback regex that
|
|
200
|
+
* greedily matched the REPO ID itself (`src_<32hex>` satisfies the same
|
|
201
|
+
* `[a-z0-9_]+_[0-9a-f]{32}` shape as a real object id) out of a
|
|
202
|
+
* `source/<repo_id>/…` key, sending the wrong object_id to the gateway.
|
|
203
|
+
* Keyed by the listing's own declared `object_kind` (trusted metadata from
|
|
204
|
+
* the gateway) — the pattern then both extracts the id AND validates that
|
|
205
|
+
* the key's shape actually matches its declared kind.
|
|
206
|
+
*/
|
|
207
|
+
const NON_GENERATION_KEY_SPECS = [
|
|
208
|
+
{ kind: "wal_pack", pattern: /^wal\/(wal_[0-9a-f]{32})\.pack\.enc$/ },
|
|
209
|
+
{ kind: "ref_state", pattern: /^refs\/(refs_[0-9a-f]{32})\.enc$/ },
|
|
210
|
+
{ kind: "retention_roots", pattern: /^retention\/(rr_[0-9a-f]{32})\.enc$/ },
|
|
211
|
+
{ kind: "checkpoint_manifest", pattern: /^checkpoints\/(chk_[0-9a-f]{32})\.manifest\.enc$/ },
|
|
212
|
+
{ kind: "checkpoint_pack", pattern: /^checkpoints\/(ckp_[0-9a-f]{32})\.pack\.enc$/ },
|
|
213
|
+
{ kind: "checkpoint_claim_set", pattern: /^checkpoints\/(ccs_[0-9a-f]{32})\.claims\.json$/ },
|
|
214
|
+
{ kind: "maintenance_stage_claim_set", pattern: /^maintenance\/(msc_[0-9a-f]{32})\.stage\.json$/ },
|
|
215
|
+
{ kind: "maintenance_stage_page", pattern: /^maintenance\/(msp_[0-9a-f]{32})\.page\.json$/ },
|
|
216
|
+
{ kind: "maintenance_completion_cut", pattern: /^maintenance\/cuts\/(adm_[0-9a-f]{32})\.json$/ },
|
|
217
|
+
{ kind: "retention_cutoff", pattern: /^retention\/(rc_[0-9a-f]{32})\.ticket\.json$/ },
|
|
218
|
+
{ kind: "prune_intent", pattern: /^prune\/(pi_[0-9a-f]{32})\.intent\.json$/ },
|
|
219
|
+
// prune_completion is filed under the INTENT's own object_id (storage-keys.ts
|
|
220
|
+
// `pruneCompletionKey`) — its key literally carries a `pi_…` id, not `pc_…`.
|
|
221
|
+
{ kind: "prune_completion", pattern: /^prune\/(pi_[0-9a-f]{32})\.completion\.json$/ },
|
|
222
|
+
{ kind: "verifier_receipt", pattern: /^verifier-receipts\/(vr_[0-9a-f]{32})\.json$/ },
|
|
223
|
+
{ kind: "maintenance_cycle_issuance", pattern: /^maintenance\/issued\/(mc_[0-9a-f]{32})\.json$/ },
|
|
224
|
+
{ kind: "maintenance_cycle_terminal", pattern: /^maintenance\/terminals\/(mc_[0-9a-f]{32})\.terminal\.json$/ },
|
|
225
|
+
];
|
|
226
|
+
/**
|
|
227
|
+
* `key_envelope` is path-addressed by `(epoch, recipient_fingerprint)`, not a
|
|
228
|
+
* plain object_id — every other kind uses its object_id, read straight from
|
|
229
|
+
* the key's own filename via an ANCHORED, kind-specific pattern (never a
|
|
230
|
+
* loose, unanchored fallback — see {@link NON_GENERATION_KEY_SPECS}).
|
|
231
|
+
* Exported for the key-shape conformance test.
|
|
232
|
+
*/
|
|
233
|
+
export function objectReadRequestForEntry(entry) {
|
|
234
|
+
if (entry.object_kind === "key_envelope") {
|
|
235
|
+
// `key-envelopes/<epoch>/<recipient_fingerprint>.env` — the GATEWAY's
|
|
236
|
+
// real §3 wire key (`packages/gateway/src/services/gitvault/
|
|
237
|
+
// upload-sessions.ts` `UPLOADABLE_KINDS.key_envelope.key()`, mirrored by
|
|
238
|
+
// `storage-keys.ts` `objectKeyFor`). This is what the live objects
|
|
239
|
+
// listing actually returns, and is DIFFERENT from the SDK's own
|
|
240
|
+
// `gitvault-creation-journal.ts` envelope `path` string
|
|
241
|
+
// (`envelopes/<epoch>/<fp>`, no `key-` prefix, no `.env` suffix) — that
|
|
242
|
+
// string is CLIENT-LOCAL addressing for the upload manifest only ("never
|
|
243
|
+
// rides the wire", per its own doc comment); the gateway derives its own
|
|
244
|
+
// key independently and never echoes the client's `path` back. Do not
|
|
245
|
+
// re-derive this from that SDK constant.
|
|
246
|
+
const m = /^key-envelopes\/([0-9a-f]{16})\/(ek_[0-9a-f]{32})\.env$/.exec(entry.key);
|
|
247
|
+
if (!m)
|
|
248
|
+
fail("GITVAULT_MIRROR_KEY_UNRECOGNIZED", `key_envelope key does not match the expected layout: ${entry.key}`, "building gitvault mirror read request", { key: entry.key });
|
|
249
|
+
return { object_kind: "key_envelope", epoch: m[1], recipient_fingerprint: m[2] };
|
|
250
|
+
}
|
|
251
|
+
const spec = NON_GENERATION_KEY_SPECS.find((s) => s.kind === entry.object_kind);
|
|
252
|
+
if (!spec) {
|
|
253
|
+
fail("GITVAULT_MIRROR_KEY_UNRECOGNIZED", `unrecognized object_kind '${entry.object_kind}' for mirror read (key ${entry.key})`, "building gitvault mirror read request", { key: entry.key, object_kind: entry.object_kind });
|
|
254
|
+
}
|
|
255
|
+
const m = spec.pattern.exec(entry.key);
|
|
256
|
+
if (!m)
|
|
257
|
+
fail("GITVAULT_MIRROR_KEY_UNRECOGNIZED", `${entry.object_kind} key does not match the expected §3 layout: ${entry.key}`, "building gitvault mirror read request", { key: entry.key, object_kind: entry.object_kind });
|
|
258
|
+
return { object_kind: entry.object_kind, object_id: m[1] };
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Task 5.3 follow-up (found via the live drill 2026-08-26, gateway diagnosis
|
|
262
|
+
* confirmed correct — NOT a bug): the recipient-only read gate on
|
|
263
|
+
* `key_envelope` means a mirror can only ever hold envelopes wrapped for
|
|
264
|
+
* THIS machine's own identity. A multi-recipient vault (gitvault-human-
|
|
265
|
+
* envelopes) makes a foreign-recipient envelope the NORM, not an anomaly —
|
|
266
|
+
* it must never be synced as a failure. This extracts the `ek_` fingerprint
|
|
267
|
+
* straight from the key_envelope's own key (no network round-trip needed to
|
|
268
|
+
* classify it), reusing the exact pattern `objectReadRequestForEntry` uses;
|
|
269
|
+
* `null` for a key that doesn't match the expected layout at all (a real,
|
|
270
|
+
* separate problem — that case falls through to the normal read-and-fail
|
|
271
|
+
* path so `GITVAULT_MIRROR_KEY_UNRECOGNIZED` still surfaces).
|
|
272
|
+
*/
|
|
273
|
+
function keyEnvelopeRecipientFingerprintFromKey(key) {
|
|
274
|
+
const m = /^key-envelopes\/[0-9a-f]{16}\/(ek_[0-9a-f]{32})\.env$/.exec(key);
|
|
275
|
+
return m ? m[1] : null;
|
|
276
|
+
}
|
|
277
|
+
/** The local keystore's own `ek_` fingerprint, or `null` when no identity exists on this machine (keyless / never-`gitvault init`-ed). */
|
|
278
|
+
function localEncryptionFingerprint(keystore) {
|
|
279
|
+
return keystore.readIdentity()?.encryption_fingerprint ?? null;
|
|
280
|
+
}
|
|
281
|
+
/** Sort a flat listing into the admission-order write plan (module doc above). */
|
|
282
|
+
export function planMirrorWrite(entries) {
|
|
283
|
+
const objects = [];
|
|
284
|
+
const byGeneration = new Map();
|
|
285
|
+
for (const e of entries) {
|
|
286
|
+
const route = generationRouteForKey(e.key);
|
|
287
|
+
if (!route) {
|
|
288
|
+
objects.push(e);
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
const slot = byGeneration.get(route.generation) ?? {};
|
|
292
|
+
if (route.route === "admissions")
|
|
293
|
+
slot.admission = e;
|
|
294
|
+
else
|
|
295
|
+
slot.head = e;
|
|
296
|
+
byGeneration.set(route.generation, slot);
|
|
297
|
+
}
|
|
298
|
+
const generations = [...byGeneration.keys()].sort();
|
|
299
|
+
const admissionsAndHeads = [];
|
|
300
|
+
for (const gen of generations) {
|
|
301
|
+
const slot = byGeneration.get(gen);
|
|
302
|
+
if (slot.admission)
|
|
303
|
+
admissionsAndHeads.push(slot.admission);
|
|
304
|
+
if (slot.head)
|
|
305
|
+
admissionsAndHeads.push(slot.head);
|
|
306
|
+
}
|
|
307
|
+
return { objects, admissionsAndHeads };
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Copy one entry (presigned GET → hash-verify → mirror create-only PUT) if
|
|
311
|
+
* the mirror does not already hold it at the same size. Idempotent: a
|
|
312
|
+
* matching size is treated as already-present WITHOUT a hash check (D7 —
|
|
313
|
+
* "hash-verified on copy" means on copy, not on every skip, and applies
|
|
314
|
+
* uniformly to every kind now that `size_bytes` is exact for all of them —
|
|
315
|
+
* see the module doc); a size mismatch re-copies and hash-verifies. Every
|
|
316
|
+
* failure is tagged with its `object_kind` so a sync summary's `errors[]` is
|
|
317
|
+
* self-explaining without cross-referencing the listing (task 5.3's "no
|
|
318
|
+
* per-entry reason surfaced" gap).
|
|
319
|
+
*
|
|
320
|
+
* `ownFingerprint` (task 5.3 follow-up): a `key_envelope` entry addressed to
|
|
321
|
+
* a DIFFERENT recipient is classified `skipped_foreign_recipient` BEFORE any
|
|
322
|
+
* network read is attempted (the fingerprint is parsed straight out of the
|
|
323
|
+
* entry's own key) — never a failure, and never even reaches the gateway,
|
|
324
|
+
* which would correctly 403 it under the recipient-only read gate.
|
|
325
|
+
*/
|
|
326
|
+
async function reconcileOne(client, repoId, backend, entry, ownFingerprint) {
|
|
327
|
+
const kindTag = `[${entry.object_kind}]`;
|
|
328
|
+
if (entry.object_kind === "key_envelope") {
|
|
329
|
+
const fp = keyEnvelopeRecipientFingerprintFromKey(entry.key);
|
|
330
|
+
if (fp !== null && fp !== ownFingerprint) {
|
|
331
|
+
return { key: entry.key, outcome: "skipped_foreign_recipient" };
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
const existing = await backend.head(entry.key);
|
|
336
|
+
if (existing && existing.size_bytes === entry.size_bytes) {
|
|
337
|
+
return { key: entry.key, outcome: "already_present" };
|
|
338
|
+
}
|
|
339
|
+
const bytes = await readGitvaultObjectBytes(client, repoId, entry);
|
|
340
|
+
if (!bytes)
|
|
341
|
+
return { key: entry.key, outcome: "failed", error: `${kindTag} the source no longer serves this object (listed but unreadable)` };
|
|
342
|
+
const hash = sha256Hex(bytes);
|
|
343
|
+
if (hash !== entry.sha256) {
|
|
344
|
+
return { key: entry.key, outcome: "failed", error: `${kindTag} hash mismatch on copy: expected ${entry.sha256}, got ${hash}` };
|
|
345
|
+
}
|
|
346
|
+
const put = await backend.putCreateOnly(entry.key, bytes);
|
|
347
|
+
if (!put.created) {
|
|
348
|
+
// A concurrent writer (another sync, or the dual-push hook) won the
|
|
349
|
+
// create-only race — benign; the mirror now holds SOME valid bytes at
|
|
350
|
+
// this key, and re-checking their size closes the loop honestly.
|
|
351
|
+
const after = await backend.head(entry.key);
|
|
352
|
+
if (after && after.size_bytes === entry.size_bytes)
|
|
353
|
+
return { key: entry.key, outcome: "already_present" };
|
|
354
|
+
return { key: entry.key, outcome: "failed", error: `${kindTag} mirror key already exists with a different size — refusing to overwrite (content-addressed keys should never collide; investigate)` };
|
|
355
|
+
}
|
|
356
|
+
return { key: entry.key, outcome: "copied", size_bytes: entry.size_bytes };
|
|
357
|
+
}
|
|
358
|
+
catch (e) {
|
|
359
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
360
|
+
return { key: entry.key, outcome: "failed", error: message.startsWith(kindTag) ? message : `${kindTag} ${message}` };
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* `run402 gitvault mirror sync` (task 2.3): list the vault's stored objects,
|
|
365
|
+
* diff against the mirror by key+size, fetch and hash-verify what's missing,
|
|
366
|
+
* write it in admission order. Resumable and idempotent — running it twice in
|
|
367
|
+
* a row with nothing new copies nothing the second time.
|
|
368
|
+
*/
|
|
369
|
+
export async function mirrorSync(client, repoId, options = {}) {
|
|
370
|
+
const keystore = options.keystore ?? GitvaultKeystore.open();
|
|
371
|
+
const backend = options.backend ?? openBackendFromConfig(keystore, repoId);
|
|
372
|
+
const config = readMirrorConfig(keystore, repoId);
|
|
373
|
+
const destination = options.backend ? backend.describe() : formatMirrorDestination(config.destination);
|
|
374
|
+
const ownFingerprint = localEncryptionFingerprint(keystore);
|
|
375
|
+
const entries = await listGitvaultObjectsAll(client, repoId);
|
|
376
|
+
const plan = planMirrorWrite(entries);
|
|
377
|
+
const ordered = [...plan.objects, ...plan.admissionsAndHeads];
|
|
378
|
+
let copied = 0;
|
|
379
|
+
let already = 0;
|
|
380
|
+
let skippedForeign = 0;
|
|
381
|
+
let failed = 0;
|
|
382
|
+
let bytesCopied = 0n;
|
|
383
|
+
const errors = [];
|
|
384
|
+
const skippedForeignKeys = [];
|
|
385
|
+
for (const entry of ordered) {
|
|
386
|
+
const result = await reconcileOne(client, repoId, backend, entry, ownFingerprint);
|
|
387
|
+
if (result.outcome === "copied") {
|
|
388
|
+
copied += 1;
|
|
389
|
+
bytesCopied += BigInt(entry.size_bytes);
|
|
390
|
+
}
|
|
391
|
+
else if (result.outcome === "already_present") {
|
|
392
|
+
already += 1;
|
|
393
|
+
}
|
|
394
|
+
else if (result.outcome === "skipped_foreign_recipient") {
|
|
395
|
+
skippedForeign += 1;
|
|
396
|
+
skippedForeignKeys.push(result.key);
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
failed += 1;
|
|
400
|
+
errors.push({ key: result.key, error: result.error ?? "unknown failure" });
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return {
|
|
404
|
+
repo_id: repoId,
|
|
405
|
+
destination,
|
|
406
|
+
objects_listed: entries.length,
|
|
407
|
+
objects_copied: copied,
|
|
408
|
+
objects_already_present: already,
|
|
409
|
+
objects_skipped_foreign_recipient: skippedForeign,
|
|
410
|
+
skipped_foreign_recipient_keys: skippedForeignKeys,
|
|
411
|
+
objects_failed: failed,
|
|
412
|
+
bytes_copied: bytesCopied.toString(),
|
|
413
|
+
errors,
|
|
414
|
+
validity_not_freshness: GITVAULT_MIRROR_VALIDITY_NOT_FRESHNESS_STATEMENT,
|
|
415
|
+
keystore_still_required: GITVAULT_MIRROR_KEYSTORE_STILL_REQUIRED_STATEMENT,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
function openBackendFromConfig(keystore, repoId) {
|
|
419
|
+
const config = readMirrorConfig(keystore, repoId);
|
|
420
|
+
if (!config)
|
|
421
|
+
fail("GITVAULT_MIRROR_NOT_CONFIGURED", `no mirror is configured for ${repoId}`, "opening gitvault mirror", { repo_id: repoId }, [{ action: "run402 repos mirror <destination>" }]);
|
|
422
|
+
if (config.destination.kind === "s3" && config.credential) {
|
|
423
|
+
// Fail fast on a missing/misconfigured credential BEFORE any network call,
|
|
424
|
+
// so a sync's error names the credential problem instead of a confusing
|
|
425
|
+
// downstream 403.
|
|
426
|
+
resolveMirrorCredentials(config.credential);
|
|
427
|
+
}
|
|
428
|
+
return openGitvaultMirrorBackend(config.destination, repoId, config.credential);
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Fires after an ordinary vault push/deploy produces a new generation. NEVER
|
|
432
|
+
* throws and NEVER alters the caller's deploy/push outcome (design D6) — a
|
|
433
|
+
* mirror is strictly extra durability, and its failure is reported BESIDE the
|
|
434
|
+
* vault result as a distinct line, not folded into it. Fires only when a
|
|
435
|
+
* mirror is configured for this vault; a vault with no mirror gets
|
|
436
|
+
* `skipped_no_mirror` (not an error) so callers can log it uniformly without
|
|
437
|
+
* branching on whether mirroring was ever opted into.
|
|
438
|
+
*
|
|
439
|
+
* Implemented as a full (but INCREMENTAL, idempotent) sync rather than a
|
|
440
|
+
* push-scoped upload: the objects listing + diff-by-size machinery already
|
|
441
|
+
* makes an all-caught-up sync cheap (one listing page, zero copies), and this
|
|
442
|
+
* keeps the admission-order write discipline in exactly one place instead of
|
|
443
|
+
* a second, push-scoped code path that could drift from it.
|
|
444
|
+
*/
|
|
445
|
+
export async function mirrorPushForGeneration(client, repoId, options = {}) {
|
|
446
|
+
let keystore;
|
|
447
|
+
try {
|
|
448
|
+
keystore = options.keystore ?? GitvaultKeystore.open();
|
|
449
|
+
if (!options.backend && !readMirrorConfig(keystore, repoId)) {
|
|
450
|
+
return { attempted: false, outcome: "skipped_no_mirror" };
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
catch (e) {
|
|
454
|
+
return { attempted: false, outcome: "skipped_no_mirror", error: e instanceof Error ? e.message : String(e) };
|
|
455
|
+
}
|
|
456
|
+
try {
|
|
457
|
+
const summary = await mirrorSync(client, repoId, { ...options, keystore });
|
|
458
|
+
return { attempted: true, outcome: summary.objects_failed > 0 ? "failed" : "pushed", summary };
|
|
459
|
+
}
|
|
460
|
+
catch (e) {
|
|
461
|
+
return { attempted: true, outcome: "failed", error: e instanceof Error ? e.message : String(e) };
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
//# sourceMappingURL=gitvault-mirror.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitvault-mirror.js","sourceRoot":"","sources":["../../src/node/gitvault-mirror.ts"],"names":[],"mappings":"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"}
|