fix(cli): align generated samples with local verification#829
Merged
Conversation
9ae9c85 to
5a76898
Compare
Generated valid CLI samples were legacy receipt-shaped claims signed directly, so they failed strict local verification. This aligns them with the current PEAC signed interaction record path. - Valid samples are now issue() input recipes (format: issue-options) and are issued via issue() at generation time, so every generated valid record passes verifyLocal() against the generated sandbox JWKS. - Sample IDs are now record-oriented: basic-record, full-record, mcp-tool-run, payment-event, event-time-record. The misleading long-expiry sample is removed (current records do not model expiry). - --now sets each valid sample's event time (occurred_at), not its issuance time (iat); iat remains the actual issuance time, so generated record bytes are not identical across runs. --kid is reflected in the generated JWS header and the sandbox JWKS. - Invalid samples remain raw legacy claims (intentionally invalid rejection fixtures), signed directly. - The sample loader uses a discriminated SampleDefinition union (issue-options for valid, legacy-claims for invalid/edge). - Sample docs use PEAC record / signed interaction record language. - Adds a test that every generated valid sample verifies locally, invalid samples are rejected, --now maps to occurred_at, --kid appears in the header, and a payload-tampered record fails. Updates public sample/demo semantics only; does not change normative fixtures, schema validators, signing semantics, registries, or conformance requirements.
5a76898 to
72245a3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Align the CLI-generated valid samples with the current PEAC signed interaction record path so that generated valid samples actually pass local verification. This updates public sample/demo semantics only.
Scope
@peac/clisample generation + thespecs/conformance/samples/demo namespace + sample docs. It does not change normative fixtures (specs/conformance/fixtures/), the record format, schema validators, signing semantics, registries, or conformance requirements. The sample loader reads onlyspecs/conformance/samples/, which is separate from the normativefixtures/andspecs/wire/namespaces; those are untouched.Why generated samples needed alignment
The generated
valid/*.jwsfiles were legacy receipt-shaped claims signed directly via the low-level sign path. Their signatures were valid but their claims did not pass strict local verification (verifyLocal), so "valid" samples were not actually locally verifiable.What "valid" means after this PR
Valid samples are now stored as
issue()input recipes (format: "issue-options") and are issued through the current issue path at generation time. Every generated valid record passesverifyLocal()against the generated single-key sandbox JWKS (bundles/sandbox-jwks.json). Record-oriented IDs:basic-record,full-record,mcp-tool-run,payment-event,event-time-record. The previouslong-expirysample is removed (current records are permanent evidence and do not model expiry);event-time-recordreplaces it to demonstrate event-time semantics.jtiis a fresh UUIDv7 assigned byissue()(no fixed literal).What
--nowmeans after this PR--now <unix-seconds>sets each valid sample's event time (occurred_at), not its issuance time.iatremains the actual issuance time, so generated valid record JWS bytes are not identical across runs.--nowis rejected at preflight (before any sample files are written) when valid samples are selected, because a futureoccurred_atfails local verification. A non-integer--nowis rejected;--now 0is honored (epoch occurred_at).--kidis reflected in the generated JWS header and the sandbox JWKS.Input validation (preflight, before filesystem side effects)
samples generatevalidates all inputs before creating directories or writing files: unsupported--format(onlyjws/json) is rejected, unknown--category(onlyvalid/invalid/edge) is rejected, and an empty selection fails.What remains intentionally invalid/edge
Invalid samples (
expired,future-iat,missing-iss) remain raw legacy claims signed directly, so they keep their intentionally invalid shapes as rejection fixtures. The sample loader uses a discriminatedSampleDefinitionunion (issue-optionsfor valid,legacy-claimsfor invalid/edge) and skips malformed samples with a surfaced message rather than degrading silently.Confirmations
verifyLocal()is not relaxed and no lenient mode was added.peac verify --public-keywork is out of scope for this PR and remains a follow-up.Validation
pnpm --filter '@peac/cli' buildandpnpm --filter '@peac/cli' test(259 tests) pass. New tests assert: every generated valid sample verifies locally and has a UUIDv7jti; invalid samples are rejected;--nowmaps tooccurred_at(notiat);--now 0sets the epoch; a future--nowis rejected before any valid files are written; non-integer--nowis rejected; unsupported--formatand unknown--categoryare rejected; a malformed valid sample is skipped;--kidappears in the header and JWKS; a payload-tampered record fails.pnpm conformance:test(676 tests) passes, confirming no conformance impact.pnpm format:check,scripts/ci/forbid-strings.sh, eslint,git diff --check, and the extension sentinel test all pass.Changed public sample semantics
Generated valid samples are now current PEAC signed interaction records that pass local verification (previously legacy receipt-shaped, signature-only). Sample IDs changed to record-oriented names;
long-expiryremoved. CLI help and sample docs now describe records and theoccurred_atvsiatdistinction.