{"protocol":"clank-doc/1","frameworkVersion":"0.13.0","slug":"blueprint-registry","title":"Signed blueprint registry","description":"Clank's blueprint registry is a dependency free protocol and CLI for reusing application blueprints without trusting mutable tags, registry hosted code, redirects, or install hooks. A publisher signs one normalized clank app/1 data contract","group":{"id":"agents","title":"Agents and generation"},"url":"https://docs.clank.run/docs/blueprint-registry","source":"docs/blueprint-registry.md","headings":["Protocol at a glance","Create narrowly scoped keys","Sign an exact blueprint release","Build a static signed catalog","Install an exact verified release","Programmatic API","Rotation, revocation, and honest boundaries"],"tableOfContents":[{"id":"protocol-at-a-glance","title":"Protocol at a glance","level":2},{"id":"create-narrowly-scoped-keys","title":"Create narrowly scoped keys","level":2},{"id":"sign-an-exact-blueprint-release","title":"Sign an exact blueprint release","level":2},{"id":"build-a-static-signed-catalog","title":"Build a static signed catalog","level":2},{"id":"install-an-exact-verified-release","title":"Install an exact verified release","level":2},{"id":"programmatic-api","title":"Programmatic API","level":2},{"id":"rotation-revocation-and-honest-boundaries","title":"Rotation, revocation, and honest boundaries","level":2}],"markdown":"# Signed blueprint registry\n\nClank's blueprint registry is a dependency-free protocol and CLI for reusing application\nblueprints without trusting mutable tags, registry-hosted code, redirects, or install hooks. A\npublisher signs one normalized `clank-app/1` data contract as an exact semantic version. A registry\nsigns a monotonic catalog that binds that release's digest, publisher key, and same-origin path.\nThe installer verifies both signatures and then runs the ordinary deterministic generator.\n\nThe registry can be any static HTTPS origin. It needs no custom server and never receives a private\nkey. A catalog is discovery, not trust: trust begins in a small local `clank-blueprint-trust/1`\npolicy distributed through a separate reviewed channel.\n\n## Protocol at a glance\n\n| Artifact | Protocol | Authority |\n| --- | --- | --- |\n| Publisher private key | `clank-blueprint-private-key/1` | Signs releases in explicit owner namespaces |\n| Trust policy | `clank-blueprint-trust/1` | Pins Ed25519 keys, roles, namespaces, registry origins, revocations, and minimum catalog sequences |\n| Release | `clank-blueprint-release/1` | Binds exact `owner/name@version`, normalized blueprint, SHA-256 digests, publisher key, and signature |\n| Catalog | `clank-blueprint-catalog/1` | Binds an HTTPS origin, monotonic sequence, exact release digests, publisher keys, and relative paths |\n\nAll signed values use canonical JSON and domain-separated Ed25519 signatures. Key IDs are the\nlowercase SHA-256 fingerprint of the public SPKI bytes. Release and catalog signatures are\ndeterministic for identical canonical input. Exact semantic versions may include a prerelease but\nnot build metadata; `latest`, ranges, and mutable channels are intentionally unsupported.\n\n## Create narrowly scoped keys\n\nCreate a publisher key that can sign only one owner namespace:\n\n```sh\nclank registry keygen publisher acme \\\n  --private ./keys/acme-publisher.private.json \\\n  --trust ./clank-blueprint.trust.json\n```\n\nCreate a separate key for one registry origin. The command appends its public trust record to the\nsame trust policy:\n\n```sh\nclank registry keygen registry https://blueprints.example/ \\\n  --private ./keys/registry.private.json \\\n  --trust ./clank-blueprint.trust.json\n```\n\nPrivate files are written mode `0600`; public trust files are `0644`. Existing private or output\nfiles are not replaced without `--force`, and symbolic-link destinations are refused. Keep private\nkeys outside the app, source control, artifacts, npm packages, logs, and the static registry. Use\nseparate publisher and registry keys so compromising one authority does not silently grant the\nother role.\n\nThe generated public key has an exact role and scope. A wildcard `*` is supported by the protocol\nfor controlled private deployments, but the CLI never creates one implicitly.\n\n## Sign an exact blueprint release\n\n```sh\nclank registry sign ./clank.app.ts acme/tasks@1.0.0 \\\n  --key ./keys/acme-publisher.private.json \\\n  --out ./publish/tasks-1.0.0.release.json\n\nclank registry verify ./publish/tasks-1.0.0.release.json \\\n  --trust ./clank-blueprint.trust.json\n```\n\nSigning first passes the blueprint through the same static parser and complete normalizer as\n`clank plan`; TypeScript modules are never executed. The release contains the complete normalized\nblueprint, its independent digest, signed release metadata, release digest, and signature.\nVerification rejects unknown envelope properties, malformed keys/signatures, noncanonical\nblueprints, digest mismatch, wrong namespaces, untrusted/revoked keys, revoked releases, future\ntimestamps, and optional caller-defined minimum creation times.\n\n`--json` returns `clank-registry-result/1` with the exact name, version, digest, publisher key, and\nwritten path. It never prints private key bytes.\n\n## Build a static signed catalog\n\nThe registry owner verifies every publisher release against the trust policy before cataloging it:\n\n```sh\nclank registry catalog https://blueprints.example/ 42 \\\n  ./publish/tasks-1.0.0.release.json \\\n  --key ./keys/registry.private.json \\\n  --trust ./clank-blueprint.trust.json \\\n  --out ./static/catalog.json\n```\n\nCatalog entries are sorted by exact name/version. With the default path prefix, place the exact\nrelease bytes at:\n\n```text\n./static/releases/acme/tasks/1.0.0.json\n```\n\nThen serve `./static` from `https://blueprints.example/` as immutable static JSON. A different safe\nrelative prefix can be selected with `--path-prefix`. The command does not upload, move, or mutate\nrelease files; this keeps hosting deployment explicit and reviewable. Increment `sequence` for\nevery catalog change, including a removal or revocation response. Never reuse a sequence for\ndifferent bytes.\n\nThe trust policy can remember:\n\n```json\n{\n  \"minimumCatalogSequences\": {\n    \"https://blueprints.example/\": 42\n  }\n}\n```\n\nClients then reject a validly signed older catalog. Store the highest accepted sequence in trusted\nconfiguration when rollback protection across runs matters.\n\n## Install an exact verified release\n\n```sh\nclank registry install \\\n  https://blueprints.example/catalog.json \\\n  acme/tasks@1.0.0 \\\n  ./my-tasks \\\n  --trust ./clank-blueprint.trust.json\n```\n\nInstall performs this fixed chain:\n\n1. fetch the catalog as bounded `application/json` over credential-free default-port HTTPS;\n2. refuse redirects and require the catalog's declared registry to match the fetched origin;\n3. verify its registry key role, exact origin scope, digest, signature, and minimum sequence;\n4. resolve only the requested exact name and version—never a tag or range;\n5. fetch the signed registry-relative release from the same origin;\n6. re-verify publisher role, namespace, release identity, digest, signature, revocation, and its\n   binding to the catalog entry; and\n7. generate the app with the ordinary reviewed-file overwrite rules and write `.clank/plan.json`.\n\nCatalog and release responses are time- and byte-bounded, decompressed bytes are counted, response\ncontent type is exact, caches are bypassed, cross-origin paths and traversal are impossible, and\neven a caller-supplied `VerifiedBlueprintCatalog` wrapper is cryptographically rechecked. The\ndefault response limit is 2 MiB and the hard maximum is 8 MiB.\n\n`--framework=local`, an exact version, or another explicit dependency spec has the same meaning as\nordinary `clank generate`. Generation does not install dependencies, execute the blueprint,\ncontact the registry again, or run build/install hooks.\n\n## Programmatic API\n\n```ts\nimport {\n  fetchBlueprintCatalog,\n  resolveBlueprintRelease,\n} from \"@clank.run/framework/blueprint-registry\";\n\nconst catalog = await fetchBlueprintCatalog(\n  \"https://blueprints.example/catalog.json\",\n  trustPolicy,\n);\n\nconst verified = await resolveBlueprintRelease(\n  catalog,\n  { name: \"acme/tasks\", version: \"1.0.0\" },\n  trustPolicy,\n);\n\n// verified.blueprint is normalized, frozen, data-only AppBlueprint.\n```\n\nPublishing APIs are `generateBlueprintSigningKey`, `signBlueprintRelease`, and\n`signBlueprintCatalog`. Verification APIs are `createBlueprintTrustPolicy`,\n`verifyBlueprintRelease`, `verifyBlueprintCatalog`, `fetchBlueprintCatalog`, and\n`resolveBlueprintRelease`. Machine-readable failures use `BlueprintRegistryError.code` for trust,\ndigest, signature, rollback, origin, identity, response, and lookup failures.\n\n## Rotation, revocation, and honest boundaries\n\n- Add a replacement public key to trust before publishing releases or catalogs signed by it.\n- Remove or add the old key ID to `revokedKeyIds` after the transition. Revoked keys fail closed\n  for all releases, including releases claiming an earlier timestamp.\n- Add a compromised release digest to `revokedReleaseDigests`, increment the catalog sequence, and\n  remove its entry. Clients must receive the updated trust policy to learn release revocations.\n- A signed blueprint proves publisher authorization and byte integrity, not product correctness.\n  Review the blueprint, generated plan warnings, migrations, roles, actions, services, and source.\n- Static hosting availability, TLS, DNS, private-key custody, trust-policy distribution, and\n  durable highest-sequence storage remain operator responsibilities.\n- The registry transports data-only application contracts. It is not a package manager and never\n  distributes framework executables, arbitrary generators, plugins, scripts, secrets, database\n  snapshots, compiled applications, or npm dependencies.\n"}