Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
461da53
fix(runtime): fence retained Compose volume incarnations
Oct 8, 2026
0472d23
Merge remote-tracking branch 'origin/next' into fix/native-compose-re…
Oct 8, 2026
4bd6dc8
docs(runtime): state retained-volume metadata proof limits
Oct 8, 2026
26599ea
feat: add native storage witness protocol foundation
Oct 8, 2026
0908d14
test: verify storage witness control parameters
Oct 8, 2026
3bce0a3
fix: fence storage witness async authority and archive numbers
Oct 8, 2026
c20e48c
feat: bind storage witness intent to required generation receipts
Oct 8, 2026
a9ab3f5
fix: require issued proof for storage witness enrollment
Oct 8, 2026
7de393d
test(runtime): model durable storage witness enrollment
Oct 8, 2026
2449a62
docs: map witness model to required receipt foundation
Oct 8, 2026
b5c949a
Merge remote-tracking branch 'origin/next' into feat/native-compose-s…
Oct 8, 2026
f044d4d
merge: reconcile witness foundation with retained storage next
Oct 8, 2026
9829037
feat(runtime): define directory xattr witness carrier source
Oct 8, 2026
8cc7c3d
chore: reconcile carrier with canonical witness foundation
Oct 8, 2026
2edcc9a
feat: bind storage witnesses to finite xattr proofs
Oct 8, 2026
4f3888a
feat: retain finite storage carrier uncertainty
Oct 8, 2026
00bff58
fix: request close-on-exec for storage witness root
Oct 8, 2026
5a24f99
chore: reconcile storage witness carrier with next
Oct 8, 2026
67c5902
feat: prepare finite Docker storage witness transport
Oct 8, 2026
7218d87
fix: close storage carrier holder and descriptor boundaries
Oct 8, 2026
ea46a6f
fix: create bounded empty storage command leaves
Oct 8, 2026
44e071b
chore: qualify storage carrier source and formatting
Oct 8, 2026
2a17f59
Merge remote-tracking branch 'origin/next' into feat/native-compose-s…
Oct 8, 2026
9a140d4
fix: separate host Git admission from carrier quotas
Oct 8, 2026
b9e7bf5
fix: settle Git streams before refusing spawn observations
Oct 8, 2026
49dd0f1
fix: close sparse storage image inspection JSON
Oct 8, 2026
73b6564
fix: project storage carrier volume selections
Oct 8, 2026
b3ef772
fix: mount witness carriers with exact named volumes
Oct 9, 2026
096574f
Merge remote-tracking branch 'origin/next' into feat/native-compose-s…
Oct 9, 2026
b522f10
fix: accept omitted empty carrier tmpfs metadata
Oct 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/reference/native-compose-storage-witness.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,13 @@ compiled CLI coverage and real engine acceptance remain required gates. No carri
or helper download is implemented by this foundation. Store and source-CLI tests
use synthetic observation ports and engine transports; they do not qualify Docker
write or archive semantics.

The separate [directory-xattr owner and helper source](native-compose-storage-xattr-carrier.md)
uses a create-only token without a PostgreSQL data-root directory entry. Required
version-three expectation/reference records and tagged artifact/journal-bound intent keep
it distinct from USTAR under the same version-three receipt owner. It adds
injectable cold provisioning and fresh read-only proof ports. The candidate Docker
port remains unactivated pending its persistent-volume qualification; no CLI caller
enrolls storage yet. Required finite carrier intent survives unknown
helper outcomes even after enrollment; saved recovery stops resources without
retiring that anchor. Its offline controls do not qualify a carrier.
253 changes: 253 additions & 0 deletions docs/reference/native-compose-storage-xattr-carrier.md

Large diffs are not rendered by default.

89 changes: 89 additions & 0 deletions scripts/generate-native-storage-witness-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { createHash } from "node:crypto";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { NATIVE_STORAGE_DOCKER_ARTIFACT } from "../src/lib/native-compose-storage-witness-docker-artifact.ts";
import { NATIVE_STORAGE_WITNESS_HELPER } from "../src/lib/native-compose-storage-witness-helper-bundle.ts";
import { run } from "../src/lib/shell.ts";

/** Explicit maintenance build only; never execute the helper or update its approved
* artifact pin automatically. Changed bytes require separate runtime qualification. */
const root = await mkdtemp(join(tmpdir(), "hack-storage-helper-build-"));
let group = 0,
settled = false,
started = false;
try {
if (
Bun.version !== "1.4.2" ||
!["--check", "--write"].includes(process.argv[2] ?? "")
) {
throw new Error("Pinned helper generation refused");
}
const output = join(root, "helper.mjs");
started = true;
const exit = await run(
[
process.execPath,
"build",
"scripts/native-storage-witness-helper.ts",
"--target=bun",
"--outfile",
output,
],
{
stdin: "ignore",
timeoutMs: 60_000,
stdout: "ignore",
stderr: "ignore",
onSpawn: (event) => {
group = event.ownsProcessGroup
? (event.processGroupId ?? event.pid)
: 0;
return Promise.resolve();
},
}
);
const deadline = Date.now() + 3000;
while (group > 1 && Date.now() <= deadline) {
try {
process.kill(-group, 0);
} catch (error: unknown) {
if (
typeof error === "object" &&
error !== null &&
"code" in error &&
error.code === "ESRCH"
) {
settled = true;
break;
}
throw new Error("Helper build disposition uncertain");
}
await new Promise((resolve) => setTimeout(resolve, 20));
}
if (!settled) {
throw new Error("Helper build disposition uncertain");
}
if (exit !== 0) {
throw new Error("Pinned helper build refused");
}
const bytes = await readFile(output);
if (
bytes.length > 128 * 1024 ||
createHash("sha256").update(bytes).digest("hex") !==
NATIVE_STORAGE_DOCKER_ARTIFACT.helperHash
) {
throw new Error("Helper artifact changed; separate qualification required");
}
const path = "src/lib/native-compose-storage-witness-helper-bundle.ts";
const source = `// Generated from scripts/native-storage-witness-helper.ts. Do not edit the payload.\nexport const NATIVE_STORAGE_WITNESS_HELPER =\n // biome-ignore lint/suspicious/noTemplateCurlyInString: Preserve qualified helper bytes without host interpolation.\n ${JSON.stringify(bytes.toString("utf8"))};\n`;
if (process.argv[2] === "--write") {
await writeFile(path, source);
} else if (NATIVE_STORAGE_WITNESS_HELPER !== bytes.toString("utf8")) {
throw new Error("Generated helper source differs");
}
} finally {
if (settled || !started) {
await rm(root, { recursive: true, force: true });
}
}
81 changes: 81 additions & 0 deletions scripts/native-storage-witness-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import {
encodeNativeComposeStorageXattrResponse,
NATIVE_STORAGE_XATTR_INPUT_LIMIT,
NATIVE_STORAGE_XATTR_KIND,
NATIVE_STORAGE_XATTR_VERSION,
type NativeComposeStorageXattrResponse,
parseNativeComposeStorageXattrRequest,
refuseNativeComposeStorageXattr,
} from "../src/lib/native-compose-storage-witness-xattr-codec.ts";
import { runNativeComposeStorageXattrHelper } from "../src/lib/native-compose-storage-witness-xattr-helper.ts";
import { createNativeComposeStorageXattrLinuxKernel } from "../src/lib/native-compose-storage-witness-xattr-linux.ts";

async function input(): Promise<string> {
const reader = Bun.stdin.stream().getReader();
const chunks: Uint8Array[] = [];
let length = 0;
try {
while (true) {
const next = await reader.read();
if (next.done) {
break;
}
length += next.value.byteLength;
if (length > NATIVE_STORAGE_XATTR_INPUT_LIMIT) {
await reader.cancel();
return refuseNativeComposeStorageXattr();
}
chunks.push(next.value);
}
} finally {
reader.releaseLock();
}
return new TextDecoder("utf-8", { fatal: true }).decode(
Buffer.concat(chunks)
);
}
function refused(): NativeComposeStorageXattrResponse {
return {
kind: NATIVE_STORAGE_XATTR_KIND,
version: NATIVE_STORAGE_XATTR_VERSION,
outcome: "refused",
};
}
/** Bundling and exact artifact/ABI/engine qualification are prerequisites; this entry is never launched by the CLI. */
export async function nativeStorageWitnessHelperMain(): Promise<number> {
let response = refused();
let close: (() => void) | null = null;
try {
const request = parseNativeComposeStorageXattrRequest(await input());
const selected = await createNativeComposeStorageXattrLinuxKernel();
close = selected.close;
response = runNativeComposeStorageXattrHelper({
request,
kernel: selected.kernel,
});
} catch {
response = refused();
} finally {
if (close) {
try {
close();
} catch {
response = refused();
}
}
}
// The owner must force logging=none and capture this bounded response privately.
try {
await Bun.write(
Bun.stdout,
encodeNativeComposeStorageXattrResponse(response)
);
} catch {
// A truncated private response is uncertain, not a successful helper proof.
return 1;
}
return response.outcome === "refused" ? 1 : 0;
}
if (import.meta.main) {
process.exitCode = await nativeStorageWitnessHelperMain();
}
13 changes: 11 additions & 2 deletions src/lib/native-compose-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,7 @@ async function savedCommand(opts: {
};
if (options.operation === "down") {
return await store.withMutation(async (mutation) => {
const recoveryState = await store.loadCurrent();
const saved = await savedRouteDocuments(store);
const prepared = await prepareSavedStop({
store,
Expand All @@ -651,7 +652,9 @@ async function savedCommand(opts: {
.runEffect({
generation,
operation: "down",
recoverPending: options.recover === true && pending !== null,
recoverPending:
options.recover === true &&
(pending !== null || recoveryState.storageWitnessesPending),
assertOwned,
captureStorage: prepared.captureStorage,
assertFresh: hooks?.assertFresh,
Expand Down Expand Up @@ -777,7 +780,8 @@ async function savedCommand(opts: {
data: {
composeProject: generation.identity.composeProject,
stopped: state.stopped,
pending: state.pending !== null,
pending:
state.pending !== null || state.storageWitnessesPending,
beforeHooksPending: state.beforeHooksPending,
hostHookPhase: state.hostHookPhase,
services: observed.containers.map(
Expand All @@ -799,6 +803,11 @@ async function savedCommand(opts: {
"Native host hook completion remains uncertain; hook recovery is not supported in this slice.\n"
);
}
if (state.storageWitnessesPending) {
process.stderr.write(
"Native storage continuity or helper completion remains uncertain; saved recovery retains its anchors. Values omitted.\n"
);
}
return await run([...composeArgs(generation), "ps"], {
cwd: base.cwd,
env: base.env,
Expand Down
Loading
Loading