Files
quixos-protocol/src/capability-language/source-backup.ts
T
Timothy J. Aveni ad2a851b94 Consolidate Reader source ownership and pinned application instances
Add exact recursive source checkout, independent Git/jj developer workbenches and source-only backup/recovery commands, with Workspace instance binding outside source. Qualify two empty Reader Graphs, packaged source commands, Agent VM compilation, exact Gitea read grants and reconciled Reader settings/annotation stability. Publish checked Reader Package/root revisions to canonical review branches and retention tags, retain all 57 repositories independently in private versioned storage, verify downloaded-backup recovery and advance the example pin.

Add bounded Central pin acquisition and selection-specific read-only identities, qualified against all 57 canonical repositories without write authority. Replace regex fork dependency discovery with compiler-parsed lock imports and rewrite proposals; preserve source identities and unrelated files, passing instance binding through packed and ordinary host checks.

Hosted run-pin versus authoring preparation and maintained duplicate removal remain pending. Live source selections, Template defaults and default source branches are unchanged.
2026-10-10 18:38:57 -07:00

118 lines
5.5 KiB
TypeScript

import { execFile as callback } from "node:child_process";
import { promisify } from "node:util";
import { createHash } from "node:crypto";
import { createReadStream } from "node:fs";
import { lstat, mkdir, mkdtemp, readFile, rename, rm, writeFile } from "node:fs/promises";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { retentionTagForCommit } from "../resource-lock/index.js";
import { materializeWorkspaceSources, workspaceSourcePin, type WorkspaceSourcePin } from "./source-closure.js";
const exec = promisify(callback);
type SourceBackup = {
formatVersion: 1;
root: WorkspaceSourcePin;
repositories: Array<{ repository: string; commits: string[]; bundle: string; sha256: string }>;
};
async function hash(filename: string) {
const digest = createHash("sha256");
for await (const chunk of createReadStream(filename)) digest.update(chunk);
return digest.digest("hex");
}
/** Source-only backup: no Graph state, checkout instructions or credentials.
* Git bundles retain exact commits and their ancestors without needing a forge.
* Storage/retention of the resulting directory belongs to the operator. */
export async function backupWorkspaceSources(options: {
source: WorkspaceSourcePin;
directory: string;
output: string;
locations?: Record<string, string>;
}): Promise<SourceBackup> {
const output = path.resolve(options.output);
if (
await lstat(output).catch((error: NodeJS.ErrnoException) => {
if (error.code !== "ENOENT") throw error;
return null;
})
)
throw Error("Source backup destination must not exist");
const closure = await materializeWorkspaceSources(options);
const grouped = new Map<string, Map<string, string>>();
for (const entry of closure.resources) {
const commits = grouped.get(entry.repository) ?? new Map<string, string>();
commits.set(entry.commit, path.join(path.resolve(options.directory), entry.directory));
grouped.set(entry.repository, commits);
}
await mkdir(path.dirname(output), { recursive: true });
const staging = await mkdtemp(path.join(path.dirname(output), ".source-backup-"));
const manifest: SourceBackup = { formatVersion: 1, root: closure.root, repositories: [] };
try {
for (const [repository, commits] of [...grouped].sort(([a], [b]) => a.localeCompare(b))) {
const bundle = createHash("sha256").update(repository).digest("hex") + ".bundle";
const gitDirectory = await mkdtemp(path.join(staging, ".git-"));
try {
await exec("git", ["init", "--bare", gitDirectory]);
const refs: string[] = [];
for (const [commit, checkout] of commits) {
const ref = retentionTagForCommit(commit);
await exec("git", ["-C", gitDirectory, "fetch", "--no-tags", "--", checkout, `${ref}:${ref}`]);
refs.push(ref);
}
await exec("git", ["-C", gitDirectory, "bundle", "create", path.join(staging, bundle), ...refs]);
} finally {
await rm(gitDirectory, { recursive: true, force: true });
}
manifest.repositories.push({
repository,
commits: [...commits.keys()].sort(),
bundle,
sha256: await hash(path.join(staging, bundle)),
});
}
await writeFile(path.join(staging, "backup.json"), JSON.stringify(manifest, null, 2) + "\n", { mode: 0o600 });
await rename(staging, output);
return manifest;
} finally {
await rm(staging, { recursive: true, force: true });
}
}
export async function restoreWorkspaceSources(options: { backup: string; directory: string }) {
const backup = path.resolve(options.backup);
const filename = path.join(backup, "backup.json");
const info = await lstat(filename);
if (!info.isFile() || info.isSymbolicLink() || info.size > 1024 * 1024) throw Error("Invalid source backup manifest");
const manifest = JSON.parse(await readFile(filename, "utf8")) as SourceBackup;
if (
manifest.formatVersion !== 1 ||
!Array.isArray(manifest.repositories) ||
!manifest.repositories.length ||
manifest.repositories.length > 256
)
throw Error("Invalid source backup manifest");
const root = workspaceSourcePin(manifest.root);
const locations: Record<string, string> = {};
for (const entry of manifest.repositories) {
if (!entry || !Array.isArray(entry.commits) || !entry.commits.length || entry.commits.length > 256)
throw Error("Invalid source backup entry");
for (const commit of entry.commits) workspaceSourcePin({ repository: entry.repository, commit });
if (Object.hasOwn(locations, entry.repository)) throw Error("Duplicate source backup repository");
const expected = createHash("sha256").update(entry.repository).digest("hex") + ".bundle";
if (entry.bundle !== expected || !/^[a-f0-9]{64}$/.test(entry.sha256))
throw Error("Invalid source backup bundle identity");
const bundle = path.join(backup, expected);
const stat = await lstat(bundle);
if (!stat.isFile() || stat.isSymbolicLink() || (await hash(bundle)) !== entry.sha256)
throw Error("Source backup bundle integrity mismatch");
locations[entry.repository] = pathToFileURL(bundle).href;
}
if (
!manifest.repositories.some((entry) => entry.repository === root.repository && entry.commits.includes(root.commit))
)
throw Error("Source backup does not retain its root commit");
// Every discovered dependency must use the backup. Never fall through to the
// original forge if a manifest omits a required repository.
return materializeWorkspaceSources({ source: root, directory: options.directory, locations, requireLocations: true });
}