diff --git a/data/audit/catalog.json b/data/audit/catalog.json index 7bbc0d6..a0de0bb 100644 --- a/data/audit/catalog.json +++ b/data/audit/catalog.json @@ -1339,6 +1339,11 @@ "$type": "body", "$description": "extracted prose — index.html" }, + "index.html#7cf0ba906692": { + "$value": "The hand-graded claims above are also published as a signed graph — claims.jsonld — each carrying its grade, its gap, and the evidence link, secured by the same build signature", + "$type": "body", + "$description": "extracted prose — index.html" + }, "index.html#7ff944b86a94": { "$value": "Bounded Systems scopes that down", "$type": "body", diff --git a/index.html b/index.html index bf9746b..0f00190 100644 --- a/index.html +++ b/index.html @@ -176,7 +176,7 @@

Every claim here is graded against the running code.

graded against the latest commit

-

Beyond the hand-graded claims, the build runs a battery of fail-closed gates: lone blesses each rendered page's DOM for semantic HTML and accessibility, the JSON-LD is checked against SHACL shapes, an SPDX SBOM is generated and completeness-checked, a signed whole-site manifest and RFC 9530 content digests cover the served bytes, and the nix build is reproducible. Each verdict folds into one honest conformance projection — lone's web-build model, which emits the strong WCAG 2.2 AA / OWASP ASVS claim only when every gating criterion passes, so manual and unsupplied criteria stay not assessed rather than overclaimed.

+

Beyond the hand-graded claims, the build runs a battery of fail-closed gates: lone blesses each rendered page's DOM for semantic HTML and accessibility, the JSON-LD is checked against SHACL shapes, an SPDX SBOM is generated and completeness-checked, a signed whole-site manifest and RFC 9530 content digests cover the served bytes, and the nix build is reproducible. Each verdict folds into one honest conformance projection — lone's web-build model, which emits the strong WCAG 2.2 AA / OWASP ASVS claim only when every gating criterion passes, so manual and unsupplied criteria stay not assessed rather than overclaimed. The hand-graded claims above are also published as a signed graph — claims.jsonld — each carrying its grade, its gap, and the evidence link, secured by the same build signature.

diff --git a/integrity/claims/claims.jsonld b/integrity/claims/claims.jsonld new file mode 100644 index 0000000..08baa15 --- /dev/null +++ b/integrity/claims/claims.jsonld @@ -0,0 +1,93 @@ +{ + "//": "The bounded.tools honesty-section claims, as a nanopublication-shaped, Sigstore-securable graph. Mirrors the visible grade chips (same claims, same grades) so the page's honesty is also machine-checkable data. Grades/gaps/evidence are verified against the linked repos (see content/grounding.json for the per-term evidence). @@DATE@@ is substituted at build by scripts/gen-claims.mjs. Validated by validate-claims.mjs (brand-checks CI).", + "@context": { + "schema": "https://schema.org/", + "prov": "http://www.w3.org/ns/prov#", + "np": "http://www.nanopub.org/nschema#", + "bt": "https://bounded.tools/ns/claims#", + "claim": "bt:claim", + "grade": "bt:grade", + "gap": "bt:gap", + "evidence": { "@id": "bt:evidence", "@type": "@id" }, + "subject": { "@id": "schema:subjectOf", "@type": "@id" }, + "wasDerivedFrom": { "@id": "prov:wasDerivedFrom", "@type": "@id" }, + "generatedAtTime": { "@id": "prov:generatedAtTime", "@type": "schema:DateTime" }, + "wasAttributedTo": { "@id": "prov:wasAttributedTo", "@type": "@id" }, + "securedBy": { "@id": "bt:securedBy", "@type": "@id" } + }, + "@id": "https://bounded.tools/claims#", + "@graph": [ + { + "@id": "https://bounded.tools/claims#assertion", + "@graph": [ + { + "@id": "https://bounded.tools/claims#c1", + "@type": "bt:Claim", + "claim": "Docs generate from source and fail CI on drift.", + "grade": "enforced", + "evidence": "https://github.com/bounded-systems/site" + }, + { + "@id": "https://bounded.tools/claims#c2", + "@type": "bt:Claim", + "claim": "guest-room's behaviour specs execute against the engine.", + "grade": "enforced", + "evidence": "https://github.com/bounded-systems/guest-room" + }, + { + "@id": "https://bounded.tools/claims#c3", + "@type": "bt:Claim", + "claim": "A git-write carries a verifiable in-toto / SLSA provenance derivation — signed per-actor, content-addressed in a derivation ledger, checked fail-closed at the merge gate.", + "grade": "partial", + "gap": "Emission and enforcement are opt-in (a signer plus require-signed) until Sigstore lands; without them the push is bare. Verified mechanisms: ocap-provenance/slsa.ts, prx verify.ts/merge-guard.ts, anchored-chain.", + "evidence": "https://github.com/bounded-systems/ocap-provenance" + }, + { + "@id": "https://bounded.tools/claims#c4", + "@type": "bt:Claim", + "claim": "The agent never holds the credential — a broker daemon does.", + "grade": "partial", + "gap": "On macOS the door is TCP loopback — weaker than unix-socket possession; isolation is the container plus daemon discipline, not a hardened sandbox. Verified: keeperd/daemon.ts holds the key; claude-box is credential-free.", + "evidence": "https://github.com/bounded-systems/prx" + }, + { + "@id": "https://bounded.tools/claims#c5", + "@type": "bt:Claim", + "claim": "prx and claude-box converge onto one guest-room door runtime.", + "grade": "aspirational", + "gap": "Convergence is in progress — prx wires the seams in-process today; the out-of-process door runtime is the direction, not yet at scale.", + "evidence": "https://github.com/bounded-systems/prx" + }, + { + "@id": "https://bounded.tools/claims#c6", + "@type": "bt:Claim", + "claim": "Contracts stay honest between components as they evolve.", + "grade": "aspirational", + "gap": "Inter-contract enforcement is the open problem this whole project is aimed at — a bet, stated as direction, not a solved result.", + "evidence": "https://github.com/bounded-systems/prx" + } + ] + }, + { + "@id": "https://bounded.tools/claims#provenance", + "@graph": [ + { + "@id": "https://bounded.tools/claims#assertion", + "subject": "https://bounded.tools", + "wasDerivedFrom": "https://github.com/bounded-systems/site", + "generatedAtTime": "@@DATE@@" + } + ] + }, + { + "@id": "https://bounded.tools/claims#pubinfo", + "@graph": [ + { + "@id": "https://bounded.tools/claims#", + "wasAttributedTo": "https://github.com/bounded-systems/site", + "securedBy": "https://bounded.tools/provenance.json" + } + ] + } + ] +} diff --git a/integrity/structure-audit/structure.json b/integrity/structure-audit/structure.json index cb65f7f..863638d 100644 --- a/integrity/structure-audit/structure.json +++ b/integrity/structure-audit/structure.json @@ -99,6 +99,7 @@ "readerOk": null, "internalLinks": [ "/blog/", + "/claims.jsonld", "/conformance", "/conformance", "/provenance.json", diff --git a/package.json b/package.json index 340d038..43f51f0 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,9 @@ "description": "The bounded.tools website. Static HTML built on the @bounded-systems/brand design system.", "type": "module", "scripts": { - "build": "node build.mjs && node scripts/gen-blog.mjs && node scripts/gen-conformance.mjs && node scripts/gen-sitemap.mjs && node scripts/gen-sbom.mjs && node vendor/conformance-kit/gates/sbom/check-sbom.mjs && node scripts/gen-stamp.mjs && node scripts/emit-catalog.mjs", + "build": "node build.mjs && node scripts/gen-blog.mjs && node scripts/gen-conformance.mjs && node scripts/gen-sitemap.mjs && node scripts/gen-sbom.mjs && node vendor/conformance-kit/gates/sbom/check-sbom.mjs && node scripts/gen-stamp.mjs && node scripts/gen-claims.mjs && node scripts/emit-catalog.mjs", "conformance": "node scripts/gen-conformance.mjs", - "check": "node scripts/verify-vendor.mjs && node brand/tokens/build-tokens.mjs --check && node scripts/gen-seams.mjs --check && node scripts/gen-blog.mjs --check && node scripts/emit-catalog.mjs --check && node scripts/check-reader.mjs && node scripts/check-seo.mjs", + "check": "node scripts/verify-vendor.mjs && node brand/tokens/build-tokens.mjs --check && node scripts/gen-seams.mjs --check && node scripts/gen-blog.mjs --check && node scripts/gen-claims.mjs --check && node scripts/emit-catalog.mjs --check && node scripts/check-reader.mjs && node scripts/check-seo.mjs", "seams": "node scripts/gen-seams.mjs", "blog": "node scripts/gen-blog.mjs", "copy": "node scripts/emit-catalog.mjs", diff --git a/scripts/gen-claims.mjs b/scripts/gen-claims.mjs new file mode 100644 index 0000000..36af05e --- /dev/null +++ b/scripts/gen-claims.mjs @@ -0,0 +1,29 @@ +#!/usr/bin/env node +// Emit dist/claims.jsonld — the served, build-dated copy of the honesty-section +// claims graph (integrity/claims/claims.jsonld). The served file is covered by +// the site's content-digest manifest (its `securedBy`), so the graph the page +// links to is the one the build signature attests. @@DATE@@ → build date (date +// granularity, matching gen-stamp, to keep the build reproducible day-to-day). +// +// node scripts/gen-claims.mjs # write dist/claims.jsonld +// node scripts/gen-claims.mjs --check # validate the source graph only +import { readFile, writeFile, mkdir } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { execSync } from "node:child_process"; + +const root = dirname(dirname(fileURLToPath(import.meta.url))); +const SRC = join(root, "integrity", "claims", "claims.jsonld"); +const CHECK = process.argv.includes("--check"); + +const src = await readFile(SRC, "utf8"); +// Validate structure via the same checker CI runs (throws/exits non-zero on bad graph). +execSync(`node ${join(root, "integrity", "claims", "validate-claims.mjs")} ${SRC}`, { stdio: "inherit" }); + +if (CHECK) process.exit(0); + +const date = new Date().toISOString().slice(0, 10); +const out = src.replace(/@@DATE@@/g, date); +await mkdir(join(root, "dist"), { recursive: true }); +await writeFile(join(root, "dist", "claims.jsonld"), out); +console.log(`✓ claims: 6 graded claims → dist/claims.jsonld (${date})`);