diff --git a/README.md b/README.md new file mode 100644 index 0000000..321c04b --- /dev/null +++ b/README.md @@ -0,0 +1,89 @@ +# The Workshop — the Frontier Infra full-stack demo + +**A live board whose content is the stack's own receipts.** Every card on the board is real +work done by an AI agent workforce under the complete +[Frontier Infra](https://frontierinfra.org) stack — and every claim on it links a +cryptographic receipt you can verify yourself. Nothing here is asserted; everything is +proven. + +```bash +npm start # → http://localhost:4600 (the board) +npm test # ground truth: 7 tests +curl -s localhost:4600/.agent # agents read the board directly (AVL, validates L3) +``` + +## What this demonstrates + +One repo running **every tier of the stack at once**, with the receipts to show it worked: + +| Tier | Project | In this repo | Receipt | +|---|---|---|---| +| **Govern** | [Maintainer Gate Blueprint](https://github.com/frontier-infra/Maintainer-Gate-Blueprint) | `ops/` (10 runbooks, 4 rules), stamped by the installer; prod-DB PreToolUse gate wired; CI intake checks | gate smoke: destructive-prod command → exit 2 (blocked) | +| **Behave** | [ADL](https://github.com/frontier-infra/adl) | `CLAUDE.md` discipline layers + roles + `/goal` machinery, installed by ADL's installer | `receipts/meta-manifest.json` — the run's contract as an ADL manifest | +| **Enforce** | [Proctor](https://github.com/frontier-infra/proctor) | Proctor's own verifier (`goal-verify`) ran the meta-contract's checks against reality and signed the proof | [`receipts/meta-manifest.proof.json`](receipts/meta-manifest.proof.json) — `signed_off: true`, 3/3 checks | +| **Operate** | [machine-driver](https://github.com/frontier-infra/machine-driver) | The workforce: a deterministic, zero-token driver dispatched one fresh `claude -p` worker per task, verified each by `npm test`, budget-governed, propose-mode | `harness/driver-run.log` + `harness/driver-log.jsonl` (hash-chained loop audit) | +| **Prove** | [AAR](https://github.com/frontier-infra/agentcontrolplane) | One Ed25519-signed Agent Attestation Record per verified task, signed as `did:web:frontierinfra.org` — the DID document is [published on the live domain](https://frontierinfra.org/.well-known/did.json) | [`receipts/aar-job-001.json`](receipts/aar-job-001.json) · [`receipts/aar-job-002.json`](receipts/aar-job-002.json) — verify them yourself, no local files needed | +| **Declare** | [AVL](https://github.com/frontier-infra/avl) | The board serves `text/agent-view` companions (`/.agent`, `/agent.txt`, `/llms.txt`, content negotiation, Link header) | official validator: **conformance L3** | + +## The run that built this (2026-07-07) + +The two API endpoints on the board — `/api/uptime` and `/api/version` — were **not written +by a human, and not written by the maintainer either**. They were built by the workforce: + +1. **Contract** (Box 0): `harness/goal.json` — definition of done, acceptance tests + (immutable), constraints, budget (8 worker runs / 30 min), autonomy ceiling 0 + (propose-only; the dial is earned, and day one earns nothing). +2. **Dispatch**: machine-driver spawned a fresh `claude -p` worker per task. Each worker ran + under this repo's ADL discipline and the machine's Proctor supervision. +3. **Verify** (never self-graded): the driver ran `npm test` as ground truth. Worker says + done ≠ done; exit code 0 = done. Both tasks: `ok verified`. +4. **Prove**: one signed AAR per task, `verdict: verified`, `ground_truth: confirmed`, with a + hash commitment to the verify output. Signature checks against the DID document published + at the org's own domain — no vendor, no central server: + ```bash + node ../agentcontrolplane/tools/aar.mjs verify harness/aar/job-001.json + # [✓] L0: Ed25519 signature valid ... → conformance: L2 + ``` +5. **Meta-proof** (behave → prove): Proctor's own verifier ran the demo's meta-contract — + "all tests green AND both AARs verify" — and its proof was bridged to a signed AAR by + `harness/proof-to-aar.mjs`: [`receipts/aar-workshop-run-001.json`](receipts/aar-workshop-run-001.json). +6. **Land**: the workforce diff (23 insertions, exactly the contracted files) was reviewed by + the operator and landed through the repo's own gates — branch → PR → CI → merge. Nothing + reaches `main` on an agent's word alone. + +**A defect the stack caught in its own first run:** the driver's AAR signing step failed on a +relative key path — and instead of a silent skip, it **alerted loudly** (that's the +fail-closed design), left the unsigned records intact, and the fix was a one-line config +change. The receipts you see were then signed and verified. Demos that never break prove +nothing; this one broke honestly and recovered with the audit trail to show it. + +## Reproduce the workforce run + +```bash +# siblings expected: ../machine-driver, ../agentcontrolplane (github.com/frontier-infra) +node ../agentcontrolplane/tools/aar.mjs keygen --did did:web:YOURDOMAIN \ + --out-priv secrets/demo.jwk.json --out-did receipts/did.json +# edit harness/goal.json: your tasks, your budget, absolute aar_priv path +python3 ../machine-driver/driver.py harness/goal.json # the workforce runs +node harness/collect.mjs # receipts → the board +npm start +``` + +## Layout + +``` +app/server.mjs the board (zero-dep node http) + AVL agent views +data/jobs.json board content — generated from receipts, never hand-written +harness/goal.json the workforce contract + tasks (machine-driver config) +harness/collect.mjs driver receipts → board data (re-verifies every AAR) +harness/proof-to-aar.mjs ADL/Proctor proof.json → signed AAR (behave feeds prove) +receipts/ served at /receipts/* — AARs, proofs, DID document +ops/ Maintainer Gate Blueprint (stamped) +CLAUDE.md + .claude/ ADL discipline layers, roles, /goal + gates +secrets/ signing key (gitignored — rotate by republishing did.json) +``` + +--- + +Part of [Frontier Infra](https://frontierinfra.org) — open, producer-owned standards for the +agent web. MIT. diff --git a/app/server.mjs b/app/server.mjs index d5d138d..f8e7e40 100644 --- a/app/server.mjs +++ b/app/server.mjs @@ -159,6 +159,10 @@ export function createApp() { if (path === "/api/jobs") return send(200, "application/json", JSON.stringify(jobs(), null, 2)); if (path === "/api/health") return send(200, "application/json", JSON.stringify({ ok: true, uptimeSeconds: Math.floor((Date.now() - STARTED) / 1000) })); + if (path === "/api/version") + return send(200, "application/json", JSON.stringify({ version: JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8")).version })); + if (path === "/api/uptime") + return send(200, "application/json", JSON.stringify({ up: true, uptimeSeconds: Math.floor((Date.now() - STARTED) / 1000) })); if (path.startsWith("/receipts/")) { const name = normalize(path.slice("/receipts/".length)); if (name.includes("..") || name.includes("/")) return send(400, "text/plain", "bad path"); diff --git a/data/jobs.json b/data/jobs.json index fe51488..c612f3d 100644 --- a/data/jobs.json +++ b/data/jobs.json @@ -1 +1,22 @@ -[] +[ + { + "id": "job-001", + "title": "GET /api/uptime", + "goal": "In app/server.mjs add GET /api/uptime returning JSON {up:true, uptimeSeconds:}, and add a node:test case in test/app.test.mjs asserting 200 and up===true. Touch only app/server.mjs and test/app.test.mjs. Run npm test before finishing.", + "status": "done", + "verdict": "verified", + "ground_truth": "confirmed", + "conformance": "AAR L2 · sig valid", + "aar": "aar-job-001.json" + }, + { + "id": "job-002", + "title": "GET /api/version", + "goal": "In app/server.mjs add GET /api/version returning JSON {version} where version is read from package.json, and add a node:test case in test/app.test.mjs asserting it equals the package.json version. Touch only app/server.mjs and test/app.test.mjs. Run npm test before finishing.", + "status": "done", + "verdict": "verified", + "ground_truth": "confirmed", + "conformance": "AAR L2 · sig valid", + "aar": "aar-job-002.json" + } +] diff --git a/harness/aar/job-001.json b/harness/aar/job-001.json new file mode 100644 index 0000000..84e8854 --- /dev/null +++ b/harness/aar/job-001.json @@ -0,0 +1,31 @@ +{ + "aar": "0.02", + "subject": "did:web:frontierinfra.org:stack-demo-worker", + "principal": "did:web:frontierinfra.org", + "task": { + "id": "job-001", + "claim": "In app/server.mjs add GET /api/uptime returning JSON {up:true, uptimeSeconds:}, and add a node:test case in test/app.test.mjs asserting 200 and up===true. Touch only app/server.mjs and test/app.test.mjs. Run npm test before finishing." + }, + "verdict": "verified", + "ground_truth": "confirmed", + "reason": "verify_cmd exited 0 against repo HEAD", + "checks": [ + { + "source": "/Users/sem/code/frontier-infra/stack-demo", + "query": "npm test", + "observed_at": "2026-07-07T17:16:46+00:00", + "response_sha256": "9d116f0be8943b0ad86a11f0bf8c493de59141835acf32b1a97781918af2590b", + "excerpt": " up:true\n ---\n duration_ms: 1.943083\n type: 'test'\n ...\n# Subtest: receipts path traversal is rejected\nok 6 - receipts path traversal is rejected\n ---\n duration_ms: 1.31275\n type: 'test'\n ...\n1..6\n# tests 6\n# suites 0\n# pass 6\n# fail 0\n# cancelled 0\n# skipped 0\n# todo 0\n# duration_ms 88.279\n" + } + ], + "verifier": { + "id": "did:web:frontierinfra.org:machine-driver-verify", + "independence": "same_principal" + }, + "issued": "2026-07-07T17:16:46+00:00", + "sig": { + "alg": "Ed25519", + "by": "did:web:frontierinfra.org", + "value": "PhmP2277LEW9dvqOO_xBIZuwaWWTLbGjjY66MpWKWKmMEnvBmCOXqkLqhvCtNL77QXNOnmmDcE_eOjuNPzutBw" + } +} diff --git a/harness/aar/job-002.json b/harness/aar/job-002.json new file mode 100644 index 0000000..92fac9d --- /dev/null +++ b/harness/aar/job-002.json @@ -0,0 +1,31 @@ +{ + "aar": "0.02", + "subject": "did:web:frontierinfra.org:stack-demo-worker", + "principal": "did:web:frontierinfra.org", + "task": { + "id": "job-002", + "claim": "In app/server.mjs add GET /api/version returning JSON {version} where version is read from package.json, and add a node:test case in test/app.test.mjs asserting it equals the package.json version. Touch only app/server.mjs and test/app.test.mjs. Run npm test before finishing." + }, + "verdict": "verified", + "ground_truth": "confirmed", + "reason": "verify_cmd exited 0 against repo HEAD", + "checks": [ + { + "source": "/Users/sem/code/frontier-infra/stack-demo", + "query": "npm test", + "observed_at": "2026-07-07T17:18:00+00:00", + "response_sha256": "69e6bbc834393bb9451e2138edab9bdecc9dc1df24685bfe70260c57d8515872", + "excerpt": "rsion\n ---\n duration_ms: 1.702875\n type: 'test'\n ...\n# Subtest: receipts path traversal is rejected\nok 7 - receipts path traversal is rejected\n ---\n duration_ms: 1.17025\n type: 'test'\n ...\n1..7\n# tests 7\n# suites 0\n# pass 7\n# fail 0\n# cancelled 0\n# skipped 0\n# todo 0\n# duration_ms 97.956625\n" + } + ], + "verifier": { + "id": "did:web:frontierinfra.org:machine-driver-verify", + "independence": "same_principal" + }, + "issued": "2026-07-07T17:18:00+00:00", + "sig": { + "alg": "Ed25519", + "by": "did:web:frontierinfra.org", + "value": "BAhDPuHpl8_k3dPY5OeKKFz_wD5tdQyuMQWVtPBXA-DYUlSZolZpz1933ppbKhf_LcjNqBCsjoROsBHbyTQPDw" + } +} diff --git a/harness/collect.mjs b/harness/collect.mjs new file mode 100644 index 0000000..0f12687 --- /dev/null +++ b/harness/collect.mjs @@ -0,0 +1,50 @@ +// Collect the workforce receipts into the board's data: read harness/goal.json +// task states + the driver's signed AARs, verify each AAR with the org's own +// verifier, copy them into receipts/, and write data/jobs.json. +import { readFileSync, writeFileSync, readdirSync, copyFileSync, existsSync, mkdirSync } from "node:fs"; +import { execFileSync } from "node:child_process"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const ROOT = join(dirname(fileURLToPath(import.meta.url)), ".."); +const AAR_TOOL = "/Users/sem/code/frontier-infra/agentcontrolplane/tools/aar.mjs"; +const goal = JSON.parse(readFileSync(join(ROOT, "harness", "goal.json"), "utf8")); +const aarDir = join(ROOT, "harness", "aar"); +mkdirSync(join(ROOT, "receipts"), { recursive: true }); + +const TITLES = { "job-001": "GET /api/uptime", "job-002": "GET /api/version" }; + +const jobs = goal.tasks.map((t) => { + const job = { + id: t.id, + title: TITLES[t.id] ?? t.id, + goal: t.goal, + status: t.status, + }; + const aarFile = join(aarDir, `${t.id}.json`); + if (existsSync(aarFile)) { + const rec = JSON.parse(readFileSync(aarFile, "utf8")); + job.verdict = rec.verdict; + job.ground_truth = rec.ground_truth; + let verify = ""; + try { + verify = execFileSync("node", [AAR_TOOL, "verify", aarFile], { encoding: "utf8" }); + } catch (e) { + verify = String(e.stdout || e.message); + } + const level = verify.match(/conformance:\s*(L\d)/)?.[1]; + if (/Ed25519 signature valid/.test(verify) && level) { + job.conformance = `AAR ${level} · sig valid`; + } else { + job.conformance = "SIGNATURE INVALID"; + } + const dest = `aar-${t.id}.json`; + copyFileSync(aarFile, join(ROOT, "receipts", dest)); + job.aar = dest; + } + return job; +}); + +writeFileSync(join(ROOT, "data", "jobs.json"), JSON.stringify(jobs, null, 2) + "\n"); +console.log(`collected ${jobs.length} jobs → data/jobs.json`); +for (const j of jobs) console.log(` ${j.id}: ${j.status} · ${j.ground_truth ?? "-"} · ${j.conformance ?? "no AAR"}`); diff --git a/harness/driver-log.jsonl b/harness/driver-log.jsonl new file mode 100644 index 0000000..77b8622 --- /dev/null +++ b/harness/driver-log.jsonl @@ -0,0 +1,6 @@ +{"ts": "2026-07-07T17:15:54+00:00", "prev_hash": "", "event": "driver_up", "goal": "Extend The Workshop's API with two small, test-verified endpoints, built entirely by the agent workforce.", "mode": "propose", "ratified_by": "operator (Jason) via 'Go' on the full-stack demo; council ratification not run \u2014 demo stays in propose mode", "proposed_by": "claude-fable (planner, this session)", "hash": "704c08f8a67f9d39"} +{"ts": "2026-07-07T17:15:54+00:00", "prev_hash": "704c08f8a67f9d39", "event": "dispatch", "task": "job-001", "attempt": 1, "idempotency_key": "bde115300679707b", "hash": "4c32217dcd19672f"} +{"ts": "2026-07-07T17:16:46+00:00", "prev_hash": "4c32217dcd19672f", "event": "verified", "task": "job-001", "verify_exit": 0, "verifier_id": "npm test", "idempotency_key": "bde115300679707b", "autonomy_used": "propose", "hash": "8aa02ac3a8e3d1e4"} +{"ts": "2026-07-07T17:16:48+00:00", "prev_hash": "8aa02ac3a8e3d1e4", "event": "dispatch", "task": "job-002", "attempt": 1, "idempotency_key": "c3cb29ea77fb5d80", "hash": "ba4965aaba8d8b4b"} +{"ts": "2026-07-07T17:18:00+00:00", "prev_hash": "ba4965aaba8d8b4b", "event": "verified", "task": "job-002", "verify_exit": 0, "verifier_id": "npm test", "idempotency_key": "c3cb29ea77fb5d80", "autonomy_used": "propose", "hash": "d1c8eb53e58dbe19"} +{"ts": "2026-07-07T17:18:02+00:00", "prev_hash": "d1c8eb53e58dbe19", "event": "goal_complete", "tasks": 2, "blocked": 0, "hash": "1102cd82403c7f90"} diff --git a/harness/driver-run.log b/harness/driver-run.log new file mode 100644 index 0000000..ea14917 --- /dev/null +++ b/harness/driver-run.log @@ -0,0 +1,10 @@ +[2026-07-07T17:15:54+00:00] driver up — goal: "Extend The Workshop's API with two small, test-verified endpoints, built entirely by the agent workforce." mode=propose (operator asked propose) +[2026-07-07T17:15:54+00:00] -> job-001 (attempt 1): In app/server.mjs add GET /api/uptime returning JSON {up:true, uptimeSeconds:}, and add a node:test case in test/app.test.mjs asserting 200 and up===true. Touch only app/server.mjs and test/app.test.mjs. Run npm test before finishing. +[2026-07-07T17:16:46+00:00] ok verified. +[2026-07-07T17:16:46+00:00] ALERT: AAR sign FAILED for task job-001 (rc 1): error: ENOENT: no such file or directory, open 'secrets/stack-demo.jwk.json' +[2026-07-07T17:16:46+00:00] (propose mode — operator asked propose; diff left for your review) +[2026-07-07T17:16:48+00:00] -> job-002 (attempt 1): In app/server.mjs add GET /api/version returning JSON {version} where version is read from package.json, and add a node:test case in test/app.test.mjs asserting it equals the package.json version. Touch only app/server.mjs and test/app.test.mjs. Run npm test before finishing. +[2026-07-07T17:18:00+00:00] ok verified. +[2026-07-07T17:18:00+00:00] ALERT: AAR sign FAILED for task job-002 (rc 1): error: ENOENT: no such file or directory, open 'secrets/stack-demo.jwk.json' +[2026-07-07T17:18:00+00:00] (propose mode — operator asked propose; diff left for your review) +[2026-07-07T17:18:02+00:00] GOAL COMPLETE — 2 tasks, 0 blocked/surfaced. diff --git a/harness/goal.json b/harness/goal.json new file mode 100644 index 0000000..24502ae --- /dev/null +++ b/harness/goal.json @@ -0,0 +1,57 @@ +{ + "goal": "Extend The Workshop's API with two small, test-verified endpoints, built entirely by the agent workforce.", + "contract": { + "definition_of_done": "GET /api/uptime returns 200 JSON {up:true, uptimeSeconds:=0>}; GET /api/version returns 200 JSON {version} matching package.json. Each endpoint has a node --test case. No other route changes.", + "acceptance_tests": [ + "uptime endpoint answers with up===true", + "version endpoint matches package.json version" + ], + "immutable": [ + "acceptance_tests" + ], + "constraints": [ + "touch only app/server.mjs and test/", + "no new dependencies" + ], + "autonomy_ceiling": 0, + "proposed_by": "claude-fable (planner, this session)", + "ratified_by": "operator (Jason) via 'Go' on the full-stack demo; council ratification not run \u2014 demo stays in propose mode" + }, + "repo": "/Users/sem/code/frontier-infra/stack-demo", + "mode": "propose", + "worker_cmd": "claude -p \"{task}\" --permission-mode acceptEdits", + "verify_cmd": "npm test", + "aar_tool": "/Users/sem/code/frontier-infra/agentcontrolplane/tools/aar.mjs", + "aar_priv": "/Users/sem/code/frontier-infra/stack-demo/secrets/stack-demo.jwk.json", + "subject": "did:web:frontierinfra.org:stack-demo-worker", + "principal": "did:web:frontierinfra.org", + "verifier": "did:web:frontierinfra.org:machine-driver-verify", + "max_attempts": 3, + "tick_seconds": 2, + "budget": { + "max_worker_runs": 8, + "max_wall_seconds": 1800 + }, + "tasks": [ + { + "id": "job-001", + "goal": "In app/server.mjs add GET /api/uptime returning JSON {up:true, uptimeSeconds:}, and add a node:test case in test/app.test.mjs asserting 200 and up===true. Touch only app/server.mjs and test/app.test.mjs. Run npm test before finishing.", + "status": "done", + "attempts": 1, + "started": "2026-07-07T17:15:54+00:00", + "verify_tail": "e agent view\n ---\n duration_ms: 1.997917\n type: 'test'\n ...\n# Subtest: health and jobs endpoints answer JSON\nok 4 - health and jobs endpoints answer JSON\n ---\n duration_ms: 3.379708\n type: 'test'\n ...\n# Subtest: uptime endpoint answers 200 with up:true\nok 5 - uptime endpoint answers 200 with up:true\n ---\n duration_ms: 1.943083\n type: 'test'\n ...\n# Subtest: receipts path traversal is rejected\nok 6 - receipts path traversal is rejected\n ---\n duration_ms: 1.31275\n type: 'test'\n ...\n1..6\n# tests 6\n# suites 0\n# pass 6\n# fail 0\n# cancelled 0\n# skipped 0\n# todo 0\n# duration_ms 88.279\n", + "done": "2026-07-07T17:16:46+00:00" + }, + { + "id": "job-002", + "goal": "In app/server.mjs add GET /api/version returning JSON {version} where version is read from package.json, and add a node:test case in test/app.test.mjs asserting it equals the package.json version. Touch only app/server.mjs and test/app.test.mjs. Run npm test before finishing.", + "status": "done", + "attempts": 1, + "started": "2026-07-07T17:16:48+00:00", + "verify_tail": "on_ms: 3.544416\n type: 'test'\n ...\n# Subtest: uptime endpoint answers 200 with up:true\nok 5 - uptime endpoint answers 200 with up:true\n ---\n duration_ms: 2.240208\n type: 'test'\n ...\n# Subtest: version endpoint returns the package.json version\nok 6 - version endpoint returns the package.json version\n ---\n duration_ms: 1.702875\n type: 'test'\n ...\n# Subtest: receipts path traversal is rejected\nok 7 - receipts path traversal is rejected\n ---\n duration_ms: 1.17025\n type: 'test'\n ...\n1..7\n# tests 7\n# suites 0\n# pass 7\n# fail 0\n# cancelled 0\n# skipped 0\n# todo 0\n# duration_ms 97.956625\n", + "done": "2026-07-07T17:18:00+00:00" + } + ], + "runs": 2, + "last_success_at": "2026-07-07T17:18:00+00:00" +} \ No newline at end of file diff --git a/harness/proof-to-aar.mjs b/harness/proof-to-aar.mjs new file mode 100644 index 0000000..7169d4d --- /dev/null +++ b/harness/proof-to-aar.mjs @@ -0,0 +1,44 @@ +// Bridge: an ADL/Proctor Warden proof.json → a signed Agent Attestation Record. +// The "behave feeds prove" seam: goal-verify checks reality and signs off; +// this converts that proof into the org's portable, Ed25519-signed AAR format. +// +// Usage: node harness/proof-to-aar.mjs +import { readFileSync, writeFileSync } from "node:fs"; +import { execFileSync } from "node:child_process"; +import { createHash } from "node:crypto"; + +const AAR_TOOL = "/Users/sem/code/frontier-infra/agentcontrolplane/tools/aar.mjs"; +const PRIV = new URL("../secrets/stack-demo.jwk.json", import.meta.url).pathname; + +const [proofPath, outPath] = process.argv.slice(2); +if (!proofPath || !outPath) { + console.error("usage: node harness/proof-to-aar.mjs "); + process.exit(2); +} + +const proof = JSON.parse(readFileSync(proofPath, "utf8")); +const allPassed = proof.checks.every((c) => String(c.passed) === "true" || c.passed === true); +const signedOff = String(proof.signed_off) === "true" || proof.signed_off === true; + +const record = { + aar: "0.02", + subject: "did:web:frontierinfra.org:stack-demo-warden", + principal: "did:web:frontierinfra.org", + task: { id: proof.id, claim: proof.goal ?? `contract ${proof.id} complete` }, + verdict: signedOff && allPassed ? "verified" : "rejected", + ground_truth: allPassed ? "confirmed" : "contradicted", + reason: `goal-verify (${proof.verifier}) ran ${proof.checks.length} check(s) against reality; manifest sha256 bound`, + checks: proof.checks.map((c) => ({ + source: proofPath, + query: c.name, + observed_at: proof.verified_at, + response_sha256: createHash("sha256").update(String(c.output ?? "")).digest("hex"), + excerpt: String(c.output ?? "").slice(0, 120), + })), + verifier: { id: `did:web:frontierinfra.org:${proof.verifier ?? "goal-verify"}`, independence: "same_principal" }, + issued: new Date().toISOString(), +}; + +writeFileSync(outPath, JSON.stringify(record, null, 2) + "\n"); +execFileSync("node", [AAR_TOOL, "sign", outPath, "--priv", PRIV], { stdio: "inherit" }); +execFileSync("node", [AAR_TOOL, "verify", outPath], { stdio: "inherit" }); diff --git a/receipts/aar-job-001.json b/receipts/aar-job-001.json new file mode 100644 index 0000000..84e8854 --- /dev/null +++ b/receipts/aar-job-001.json @@ -0,0 +1,31 @@ +{ + "aar": "0.02", + "subject": "did:web:frontierinfra.org:stack-demo-worker", + "principal": "did:web:frontierinfra.org", + "task": { + "id": "job-001", + "claim": "In app/server.mjs add GET /api/uptime returning JSON {up:true, uptimeSeconds:}, and add a node:test case in test/app.test.mjs asserting 200 and up===true. Touch only app/server.mjs and test/app.test.mjs. Run npm test before finishing." + }, + "verdict": "verified", + "ground_truth": "confirmed", + "reason": "verify_cmd exited 0 against repo HEAD", + "checks": [ + { + "source": "/Users/sem/code/frontier-infra/stack-demo", + "query": "npm test", + "observed_at": "2026-07-07T17:16:46+00:00", + "response_sha256": "9d116f0be8943b0ad86a11f0bf8c493de59141835acf32b1a97781918af2590b", + "excerpt": " up:true\n ---\n duration_ms: 1.943083\n type: 'test'\n ...\n# Subtest: receipts path traversal is rejected\nok 6 - receipts path traversal is rejected\n ---\n duration_ms: 1.31275\n type: 'test'\n ...\n1..6\n# tests 6\n# suites 0\n# pass 6\n# fail 0\n# cancelled 0\n# skipped 0\n# todo 0\n# duration_ms 88.279\n" + } + ], + "verifier": { + "id": "did:web:frontierinfra.org:machine-driver-verify", + "independence": "same_principal" + }, + "issued": "2026-07-07T17:16:46+00:00", + "sig": { + "alg": "Ed25519", + "by": "did:web:frontierinfra.org", + "value": "PhmP2277LEW9dvqOO_xBIZuwaWWTLbGjjY66MpWKWKmMEnvBmCOXqkLqhvCtNL77QXNOnmmDcE_eOjuNPzutBw" + } +} diff --git a/receipts/aar-job-002.json b/receipts/aar-job-002.json new file mode 100644 index 0000000..92fac9d --- /dev/null +++ b/receipts/aar-job-002.json @@ -0,0 +1,31 @@ +{ + "aar": "0.02", + "subject": "did:web:frontierinfra.org:stack-demo-worker", + "principal": "did:web:frontierinfra.org", + "task": { + "id": "job-002", + "claim": "In app/server.mjs add GET /api/version returning JSON {version} where version is read from package.json, and add a node:test case in test/app.test.mjs asserting it equals the package.json version. Touch only app/server.mjs and test/app.test.mjs. Run npm test before finishing." + }, + "verdict": "verified", + "ground_truth": "confirmed", + "reason": "verify_cmd exited 0 against repo HEAD", + "checks": [ + { + "source": "/Users/sem/code/frontier-infra/stack-demo", + "query": "npm test", + "observed_at": "2026-07-07T17:18:00+00:00", + "response_sha256": "69e6bbc834393bb9451e2138edab9bdecc9dc1df24685bfe70260c57d8515872", + "excerpt": "rsion\n ---\n duration_ms: 1.702875\n type: 'test'\n ...\n# Subtest: receipts path traversal is rejected\nok 7 - receipts path traversal is rejected\n ---\n duration_ms: 1.17025\n type: 'test'\n ...\n1..7\n# tests 7\n# suites 0\n# pass 7\n# fail 0\n# cancelled 0\n# skipped 0\n# todo 0\n# duration_ms 97.956625\n" + } + ], + "verifier": { + "id": "did:web:frontierinfra.org:machine-driver-verify", + "independence": "same_principal" + }, + "issued": "2026-07-07T17:18:00+00:00", + "sig": { + "alg": "Ed25519", + "by": "did:web:frontierinfra.org", + "value": "BAhDPuHpl8_k3dPY5OeKKFz_wD5tdQyuMQWVtPBXA-DYUlSZolZpz1933ppbKhf_LcjNqBCsjoROsBHbyTQPDw" + } +} diff --git a/receipts/aar-workshop-run-001.json b/receipts/aar-workshop-run-001.json new file mode 100644 index 0000000..5e24b55 --- /dev/null +++ b/receipts/aar-workshop-run-001.json @@ -0,0 +1,45 @@ +{ + "aar": "0.02", + "subject": "did:web:frontierinfra.org:stack-demo-warden", + "principal": "did:web:frontierinfra.org", + "task": { + "id": "workshop-run-001", + "claim": "contract workshop-run-001 complete" + }, + "verdict": "verified", + "ground_truth": "confirmed", + "reason": "goal-verify (goal-verify@0.1.0) ran 3 check(s) against reality; manifest sha256 bound", + "checks": [ + { + "source": "receipts/meta-manifest.proof.json", + "query": "`npm test` passes (all endpoints incl. workforce-built uptime+version)", + "observed_at": "2026-07-07T17:20:08.922951+00:00", + "response_sha256": "bee35adb6782e2b9cb1f5733cf875596a69876c0ea6eab424d1470c4671b91bb", + "excerpt": "exit=0 :: > stack-demo@0.1.0 test\n> node --test\n\nTAP version 13\n# Subtest: board serves HTML with AVL discovery link\nok " + }, + { + "source": "receipts/meta-manifest.proof.json", + "query": "job-001 AAR verifies (signature + evidence)", + "observed_at": "2026-07-07T17:20:08.922951+00:00", + "response_sha256": "5df2369b7e487b465190b652f81b42078e3a6c2c42bac0b76ff95152474a0493", + "excerpt": "exit=0 :: harness/aar/job-001.json\n [✓] L0: Ed25519 signature valid\n [✓] L1: ground_truth=confirmed\n [✓] L1: evidence" + }, + { + "source": "receipts/meta-manifest.proof.json", + "query": "job-002 AAR verifies (signature + evidence)", + "observed_at": "2026-07-07T17:20:08.922951+00:00", + "response_sha256": "72949a616769225276b942351790953f5ee535755303b017cefcbc5c2920cabf", + "excerpt": "exit=0 :: harness/aar/job-002.json\n [✓] L0: Ed25519 signature valid\n [✓] L1: ground_truth=confirmed\n [✓] L1: evidence" + } + ], + "verifier": { + "id": "did:web:frontierinfra.org:goal-verify@0.1.0", + "independence": "same_principal" + }, + "issued": "2026-07-07T17:20:24.743Z", + "sig": { + "alg": "Ed25519", + "by": "did:web:frontierinfra.org", + "value": "h7Aq21MXefasu6NGIlm2VJMkq4BTLFRZPEKNY6daH_t2gSLi-ed3ooeWR4Ri2AB5M5DK_v4XVMMpJSmeCCYeAw" + } +} diff --git a/receipts/did.json b/receipts/did.json new file mode 100644 index 0000000..2b7ad7a --- /dev/null +++ b/receipts/did.json @@ -0,0 +1,22 @@ +{ + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3id.org/security/suites/jws-2020/v1" + ], + "id": "did:web:frontierinfra.org", + "verificationMethod": [ + { + "id": "did:web:frontierinfra.org#key-1", + "type": "JsonWebKey2020", + "controller": "did:web:frontierinfra.org", + "publicKeyJwk": { + "crv": "Ed25519", + "x": "wpYC7Peynz00gSSNi6O2XE7QP6uNXy9k0cTpODUdezM", + "kty": "OKP" + } + } + ], + "assertionMethod": [ + "did:web:frontierinfra.org#key-1" + ] +} diff --git a/receipts/meta-manifest.json b/receipts/meta-manifest.json new file mode 100644 index 0000000..f231811 --- /dev/null +++ b/receipts/meta-manifest.json @@ -0,0 +1,12 @@ +{ + "id": "workshop-run-001", + "created": "2026-07-07T17:20:00+00:00", + "source": "meta-contract: the full-stack demo run itself, verified by Proctor's own verifier", + "goal": "The Workshop's first workforce run is complete and proven: all tests green, both task AARs Ed25519-verified at L2.", + "done_when": [ + { "type": "test", "name": "`npm test` passes (all endpoints incl. workforce-built uptime+version)", "command": "npm test", "expect_pass": true }, + { "type": "test", "name": "job-001 AAR verifies (signature + evidence)", "command": "node /Users/sem/code/frontier-infra/agentcontrolplane/tools/aar.mjs verify harness/aar/job-001.json --did-json receipts/did.json", "expect_pass": true }, + { "type": "test", "name": "job-002 AAR verifies (signature + evidence)", "command": "node /Users/sem/code/frontier-infra/agentcontrolplane/tools/aar.mjs verify harness/aar/job-002.json --did-json receipts/did.json", "expect_pass": true } + ], + "cwd": "/Users/sem/code/frontier-infra/stack-demo" +} diff --git a/receipts/meta-manifest.proof.json b/receipts/meta-manifest.proof.json new file mode 100644 index 0000000..39c4ef9 --- /dev/null +++ b/receipts/meta-manifest.proof.json @@ -0,0 +1,28 @@ +{ + "id": "workshop-run-001", + "verified_at": "2026-07-07T17:20:08.922951+00:00", + "verifier": "goal-verify@0.1.0", + "manifest_sha256": "1a631fdd230f2294311da9286747d603af1bbfb959807beccb8f53017ce355a6", + "checks": [ + { + "name": "`npm test` passes (all endpoints incl. workforce-built uptime+version)", + "type": "test", + "passed": true, + "output": "exit=0 :: > stack-demo@0.1.0 test\n> node --test\n\nTAP version 13\n# Subtest: board serves HTML with AVL discovery link\nok 1 - board serves HTML with AVL discovery link\n ---\n duration_ms: 12.280459\n type: 'test'\n ...\n# Subtest: agent view is valid text/agent-view with required blocks\nok 2 - agent view is val" + }, + { + "name": "job-001 AAR verifies (signature + evidence)", + "type": "test", + "passed": true, + "output": "exit=0 :: harness/aar/job-001.json\n [\u2713] L0: Ed25519 signature valid\n [\u2713] L1: ground_truth=confirmed\n [\u2713] L1: evidence committed (1 check)\n [\u2713] L2: independent verifier (id != subject)\n [\u2713] \u2139: independence: same_principal (organizational attestation \u2014 disclose; not audit-grade)\n \u2192 conformance: L2" + }, + { + "name": "job-002 AAR verifies (signature + evidence)", + "type": "test", + "passed": true, + "output": "exit=0 :: harness/aar/job-002.json\n [\u2713] L0: Ed25519 signature valid\n [\u2713] L1: ground_truth=confirmed\n [\u2713] L1: evidence committed (1 check)\n [\u2713] L2: independent verifier (id != subject)\n [\u2713] \u2139: independence: same_principal (organizational attestation \u2014 disclose; not audit-grade)\n \u2192 conformance: L2" + } + ], + "signed_off": true, + "kickback_reason": null +} \ No newline at end of file diff --git a/test/app.test.mjs b/test/app.test.mjs index c678122..2104b92 100644 --- a/test/app.test.mjs +++ b/test/app.test.mjs @@ -1,5 +1,6 @@ import { test } from "node:test"; import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; import { createApp } from "../app/server.mjs"; const withServer = async (fn) => { @@ -43,6 +44,24 @@ test("health and jobs endpoints answer JSON", () => assert.ok(Array.isArray(jobs)); })); +test("uptime endpoint answers 200 with up:true", () => + withServer(async (base) => { + const res = await fetch(base + "/api/uptime"); + assert.equal(res.status, 200); + const body = await res.json(); + assert.equal(body.up, true); + assert.ok(Number.isInteger(body.uptimeSeconds)); + })); + +test("version endpoint returns the package.json version", () => + withServer(async (base) => { + const res = await fetch(base + "/api/version"); + assert.equal(res.status, 200); + const body = await res.json(); + const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")); + assert.equal(body.version, pkg.version); + })); + test("receipts path traversal is rejected", () => withServer(async (base) => { const res = await fetch(base + "/receipts/..%2Fpackage.json");