From 1fe33775a3194b51f60e08a7327cf832780480d6 Mon Sep 17 00:00:00 2001 From: samjanny Date: Wed, 3 Jun 2026 15:53:19 +0200 Subject: [PATCH] verify: check content against the manifest runtime key; complete the example A content document is signed by a runtime key, but only the manifest declares which runtime key is authorized (its canary.runtime_pubkey), so verifying a content document standalone could only report a signature rejection. Add --expected-runtime-pubkey to verify: pass the manifest's runtime key and the content (or transaction) signature is checked against it. Without it the prior behavior holds, now with a note pointing at the flag. Extend examples/blog into an end-to-end site: add manifest.unsigned.json and the signed manifest.json, and rewrite the README to walk the whole flow - derive the three role keys, convert and sign the content, sign the manifest, then verify the manifest from its onion (signature, canary, origin binding) and verify the content against the manifest's runtime key. Both verify steps accept. The README explains why the content needs the manifest. All four example .json files regenerate byte for byte from the documented commands, which were run verbatim to confirm. --- README.md | 4 +- examples/blog/README.md | 102 ++++++++++++++++----------- examples/blog/manifest.json | 1 + examples/blog/manifest.unsigned.json | 24 +++++++ src/cli.rs | 8 +++ src/commands/verify.rs | 47 ++++++++---- 6 files changed, 132 insertions(+), 54 deletions(-) create mode 100644 examples/blog/manifest.json create mode 100644 examples/blog/manifest.unsigned.json diff --git a/README.md b/README.md index f12f84c..bc636ac 100644 --- a/README.md +++ b/README.md @@ -51,14 +51,16 @@ A manifest is driven through the full chain: signature (Stages 2-6), canary (Sta - `--now` sets the verified-time reference for the canary and origin-expiry checks (defaults to the corpus clock). - `--fetched-onion` is the onion address the manifest was fetched from; with it, Stage 9 origin binding runs. Omit to skip Stage 9. - `--content-index` is the served `/content_index.json`; when the manifest declares `content_root`, Stage 9b verifies it (and its absence with a declared `content_root` surfaces the fetch failure). +- `--expected-runtime-pubkey` is the manifest's `canary.runtime_pubkey`; for a content or transaction document, it is the key the signature is checked against. Without it, a content/transaction has no authorized key and reports a signature rejection. Skipped stages are reported, so an accept is never mistaken for a full-pipeline pass. ```sh entangled-tool verify --input manifest.json --fetched-onion dkptfye...onion --content-index content_index.json +entangled-tool verify --input post.json --expected-runtime-pubkey jzFtzi...F7o ``` -Content and transaction documents are verified through signature only here; their binding checks need the fetch path / submit body, which a future revision will accept. +A content document is signed by a runtime key, and only the manifest declares which runtime key is authorized. So a content or transaction document is verified in the context of its manifest: pass `--expected-runtime-pubkey` to check its signature against the authorized key. See `examples/blog` for the full manifest-plus-content flow. ### `build --input (--key-seed-file | --key-seed-hex <64 hex>) [--now