Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
94 changes: 93 additions & 1 deletion packages/cli/src/range-seal-bind.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { existsSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { writeConfig } from "./config.js";
Expand All @@ -17,6 +17,42 @@ import type { RangeSealReceipt } from "./types.js";
import { git, withTempRepo, writeFile, commitAll } from "./test-helpers.js";

describe("sealed head binding trust", () => {
it("ignores a stale seal after a newer pass without its own sealed tip", () => {
const { root, cleanup } = withTempRepo("kc-bind-read-supersede-");
try {
writeFile(root, "f.txt", "base\n");
commitAll(root, "sealed batch tip");
const sealedTip = git(root, ["rev-parse", "HEAD"]);
mkdirSync(join(root, ".know-code"), { recursive: true });
writeConfig(root, { ...DEFAULT_CONFIG, level: "lite", requireAttest: false });
writeRangeSeal(root, {
version: 1,
diffHash: "a".repeat(64),
rangeFromOid: sealedTip,
commitCount: 1,
sealMode: "receipt",
gateKeyId: "unsigned",
sealedAt: "2026-01-01T00:00:00.000Z",
sealedHeadOid: sealedTip,
});
writeGate(root, {
version: 1,
diffHash: "b".repeat(64),
level: "lite",
passedAt: "2026-01-02T00:00:00.000Z",
commitRange: `${sealedTip}..HEAD`,
baseRef: sealedTip,
headRef: sealedTip,
gatedTreeOid: git(root, ["rev-parse", "HEAD^{tree}"]),
});

assert.equal(sealedHeadBinding(root), null);
assert.equal(headMatchesRangeSeal(root), true);
} finally {
cleanup();
}
});

it("ignores unsigned forged range-seal when requireAttest is true", async () => {
const attestHome = mkdtempSync(join(tmpdir(), "kc-bind-home-"));
const prevHome = process.env.KNOW_CODE_ATTEST_HOME;
Expand Down Expand Up @@ -78,6 +114,62 @@ describe("sealed head binding trust", () => {
}
});

it("supersedes a signed standalone binding only with a newer signed unbound gate", async () => {
const attestHome = mkdtempSync(join(tmpdir(), "kc-bind-supersede-"));
const prevHome = process.env.KNOW_CODE_ATTEST_HOME;
const { root, cleanup } = withTempRepo("kc-bind-signed-supersede-");
try {
process.env.KNOW_CODE_ATTEST_HOME = attestHome;
initAttestKey(root, "bind-pass");
writeFile(root, "f.txt", "base\n");
commitAll(root, "sealed batch tip");
const sealedTip = git(root, ["rev-parse", "HEAD"]);
mkdirSync(join(root, ".know-code"), { recursive: true });
writeConfig(root, { ...DEFAULT_CONFIG, level: "lite", requireAttest: true });

const binding = await sealPayload(
root,
{
version: 1,
sealedHeadOid: sealedTip,
boundAt: "2026-01-01T00:00:00.000Z",
},
{ passphrase: "bind-pass" },
);
writeFileSync(
join(root, ".know-code", "sealed-head-binding.json"),
`${JSON.stringify(binding, null, 2)}\n`,
);

const newerGate = {
version: 1 as const,
diffHash: "b".repeat(64),
level: "lite" as const,
passedAt: "2026-01-02T00:00:00.000Z",
commitRange: `${sealedTip}..HEAD`,
baseRef: sealedTip,
headRef: sealedTip,
gatedTreeOid: git(root, ["rev-parse", "HEAD^{tree}"]),
};
// An agent-written/unsigned gate cannot supersede a human-signed binding.
writeGate(root, newerGate);
assert.equal(sealedHeadBinding(root), sealedTip);

const signedGate = await sealPayload(
root,
newerGate as unknown as Record<string, unknown>,
{ passphrase: "bind-pass" },
);
writeGate(root, signedGate as typeof newerGate);
assert.equal(sealedHeadBinding(root), null);
} finally {
if (prevHome === undefined) delete process.env.KNOW_CODE_ATTEST_HOME;
else process.env.KNOW_CODE_ATTEST_HOME = prevHome;
rmSync(attestHome, { recursive: true, force: true });
cleanup();
}
});

it("fresh pass consumes stale seal artifacts; active tip binding is kept", async () => {
const attestHome = mkdtempSync(join(tmpdir(), "kc-bind-home-"));
const prevHome = process.env.KNOW_CODE_ATTEST_HOME;
Expand Down
27 changes: 23 additions & 4 deletions packages/cli/src/range-seal-bind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,38 @@ function trustedSealedOid(
}
}

/** A newer valid pass without its own sealed tip supersedes stale seal artifacts. */
function newerGateSupersedesSeal(repoRoot: string, sealedAt: string | undefined): boolean {
if (!sealedAt) return false;
const gate = readGateSafe(repoRoot);
if (!gate?.passedAt || gate.sealedHeadOid) return false;
if (readConfig(repoRoot).requireAttest) {
try {
assertSigned(repoRoot, "gate.json", gate as unknown as Record<string, unknown> & { sig?: string; keyId?: string });
} catch {
return false;
}
}
return Date.parse(gate.passedAt) > Date.parse(sealedAt);
}

/** Bound tip from range-seal, sealed-head-binding, or gate (redundant sources). */
export function sealedHeadBinding(repoRoot: string): string | null {
const config = readConfig(repoRoot);
// Prefer signed range-seal; then gate. Standalone binding file only when attest
// is on (unsigned agent-minted binding must not bind HEAD when attest is off).
const seal = readRangeSeal(repoRoot);
const binding = readSealedHeadBindingFile(repoRoot);
return (
trustedSealedOid(repoRoot, "range-seal.json", readRangeSeal(repoRoot)) ??
(!newerGateSupersedesSeal(repoRoot, seal?.sealedAt)
? trustedSealedOid(repoRoot, "range-seal.json", seal)
: null) ??
(config.requireAttest
? trustedSealedOid(
? !newerGateSupersedesSeal(repoRoot, binding?.boundAt) ? trustedSealedOid(
repoRoot,
"sealed-head-binding.json",
readSealedHeadBindingFile(repoRoot),
)
binding,
) : null
: null) ??
trustedSealedOid(repoRoot, "gate.json", readGateSafe(repoRoot))
);
Expand Down