Verify a handler supply chain
Bind the compiled AIR, manifest, actual handler, dependency lockfile and declared local data by digest. Detect changes before dispatch, without treating a digest as evidence that code is safe. Prerequisites: Touba, Node.js 24 and npm.
Create the isolated project
mkdir -p supply-chain-demo
cd supply-chain-demo
mkdir -p handlers vendor
npm init -y
node --input-type=module -e 'import {execFileSync} from "node:child_process"; import {copyFileSync} from "node:fs"; const v=JSON.parse(execFileSync("touba",["version","--json"],{encoding:"utf8"})); copyFileSync(v.distribution.sdk_archive,"vendor/touba-sdk.tgz");'
npm install --offline --ignore-scripts --save-exact ./vendor/touba-sdk.tgz
module examples.supply_chain
tool ReadCatalogue
description "Read the local catalogue from the reviewed project file."
gives Text
requires filesystem_read
strategy RunCatalogue
gives Text
uses ReadCatalogue
do
ReadCatalogue
into catalogue
give catalogue
{"datasets":[{"id":"parks","title":"Public parks"}]}
import { readFile } from "node:fs/promises";
import { defineTool } from "@touba/sdk";
await defineTool({ toolRef: "tool:examples.supply_chain/ReadCatalogue",
inputSignature: [], outputType: "builtin:Text", requiredGrants: ["filesystem_read"],
run: async () => (await readFile("catalogue.json", "utf8")).trim(),
});
import { writeFileSync } from "node:fs";
writeFileSync("touba.manifest.json", JSON.stringify({ manifest_version: "touba-manifest-v1", bindings: {
"tool:examples.supply_chain/ReadCatalogue": {
handler: { kind: "process", command: process.execPath, args: ["handlers/read.mjs"], cwd: "." },
grants: ["filesystem_read"], secrets: [], effect_contract: { kind: "none" },
timeout_ms: 10000, max_payload_bytes: 65536,
},
} }, null, 2));
Generate, check and deliberately invalidate the lock
The generator hashes the actual files and leaves the original lock pending review. The script first proves that requiring a review rejects this lock. It then runs the handler and records the assertions in an automated report. A separate lock declares this narrow automated check with a tool reviewer. It does not claim a human security review, signed identity or production approval. Your deployment policy must require the actual reviews appropriate to your environment.
Every drift test restores its own isolated file. Neither the original lock nor its digests are silently rewritten to make a failed check pass.
import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { readFileSync, writeFileSync } from "node:fs";
import { spawnSync } from "node:child_process";
import { TOUBA_SDK_METADATA } from "@touba/sdk";
function cli(args, expected = 0) {
const child = spawnSync("touba", args, { encoding: "utf8" });
assert.equal(child.status, expected, child.stderr || child.stdout);
return JSON.parse(child.stdout);
}
const write = (path, value) => writeFileSync(path, JSON.stringify(value, null, 2) + "\n");
const digest = path => ({ kind: "sha256", value: createHash("sha256").update(readFileSync(path)).digest("hex") });
cli(["supply-chain", "generate", "--air", "program.air.json", "--manifest", "touba.manifest.json",
"--out", "touba.lock.json", "--file", "catalogue.json", "--file", "vendor/touba-sdk.tgz",
"--sdk-metadata-json", JSON.stringify(TOUBA_SDK_METADATA)]);
const baseline = JSON.parse(readFileSync("touba.lock.json", "utf8"));
assert.equal(baseline.review.status, "pending");
const validate = (lock, expected, flags = []) => cli(["supply-chain", "validate", "--lock", lock,
"--air", "program.air.json", "--manifest", "touba.manifest.json", "--output", "json", ...flags], expected);
const pending = validate("touba.lock.json", 1, ["--strict", "--require-review"]);
assert.ok(pending.diagnostics.some(item => item.code === "SupplyChainReviewNotApproved"));
const run = (lock, expected) => cli(["run", "program.air.json", "--entry", "strategy:examples.supply_chain/RunCatalogue",
"--manifest", "touba.manifest.json", "--supply-chain-lock", lock, "--require-supply-chain-lock", "--output", "json"], expected);
const direct = cli(["run", "program.air.json", "--entry", "strategy:examples.supply_chain/RunCatalogue",
"--manifest", "touba.manifest.json", "--output", "json"]);
assert.equal(direct.result, readFileSync("catalogue.json", "utf8").trim());
write("automated-check.json", { scope: "isolated tutorial: actual catalogue read, expected value, pending-review refusal",
checked_at: new Date().toISOString(), result: "passed", security_review: false });
const checked = structuredClone(baseline);
checked.review = { status: "approved", reviewed_at: new Date().toISOString(),
reviewers: [{ kind: "tool", id: "tutorial:automated-check", role: "isolated-functional-check" }],
evidence: [{ kind: "validation_report", path: "automated-check.json", digest: digest("automated-check.json") }] };
write("automated.checked.lock.json", checked);
assert.equal(validate("automated.checked.lock.json", 0, ["--strict", "--require-review"]).status, "ok");
assert.equal(run("automated.checked.lock.json", 0).result, direct.result);
function variant(name, mutate, code) {
const value = structuredClone(checked); mutate(value); const file = name + ".lock.json"; write(file, value);
const report = validate(file, 1, ["--strict", "--require-review"]);
assert.ok(report.diagnostics.some(item => item.code === code), JSON.stringify(report));
}
variant("protocol", lock => { lock.handlers[0].sdk.protocol = "unsupported-protocol"; }, "HandlerSdkUnsupported");
variant("package", lock => { delete lock.handlers[0].handler_package; }, "HandlerPackageUnknown");
variant("rejected", lock => { lock.review.status = "rejected"; }, "SupplyChainReviewRejected");
for (const [file, code] of [["handlers/read.mjs", "HandlerChanged"],
["package-lock.json", "HandlerDependencyArtifactChanged"], ["automated-check.json", "SupplyChainReviewEvidenceChanged"]]) {
const original = readFileSync(file);
try {
writeFileSync(file, Buffer.concat([original, Buffer.from("\n ")]));
const report = validate("automated.checked.lock.json", 1, ["--strict", "--require-review"]);
assert.ok(report.diagnostics.some(item => item.code === code), JSON.stringify(report));
const refused = run("automated.checked.lock.json", 1);
assert.equal(refused.status, "failure");
assert.ok(!refused.trace.events.some(event => event.kind === "tool_binding_authorized"));
} finally { writeFileSync(file, original); }
}
assert.equal(JSON.parse(readFileSync("touba.lock.json", "utf8")).review.status, "pending");
console.log("LOCK_DIGEST_PROTOCOL_PACKAGE_REVIEW_AND_DRIFT_CHECKS_OK");
node configure.mjs
touba compile source.tb --out program.air.json
node exercise.mjs
touba inspect --supply-chain touba.lock.json
The script prints LOCK_DIGEST_PROTOCOL_PACKAGE_REVIEW_AND_DRIFT_CHECKS_OK.
The runtime manifest still grants authority; the lock only constrains artifact
identity and declared review state. The generator follows static local
JavaScript and Python imports; --file adds other resources.
Third-party dependencies remain described by their lockfiles.
See the supply-chain guide for inventory details.