A JCP grant authorizes a call by its audience — the right primitive for "may
this principal do this at all". The serving ecosystem's "service policy" shape
adds two things a static audience can't express: decisions keyed on the call's
CONTENTS (block a message carrying a secret marker to an external tool) and a
require-approval CONSENT gate (hold a write for a human). This crate adds
exactly those, nothing that duplicates the grant.
- Policy = ordered Rules, each matching on action / content (Match::ContentContains)
/ resource, with effect Deny or RequireApproval. Policy::evaluate → Allow /
Deny / RequireApproval (Deny wins, then approval, else allow). Deterministic.
- A RequireApproval is resolved by an approver's Ed25519 signature over the
call's context digest (binds the content HASH, not the content) — consent as
a verifiable artifact, bound to one exact call, not a UI click. A stale or
forged approval over a different call does not pass.
- PolicyEngine composes the policy verdict with the grant decision (call it
once the grant has allowed) and seals the outcome — allowed / denied /
held / approved — as a hash-chained policy/1 JCR-1 receipt. The content is
never sealed, only its hash.
Reuses jcp-schema + jcp-receipt jcr1 — no new crypto; receipts verify with the
family verifiers, a flipped byte fails. 8 tests green, clippy clean; runnable
example shows clean-allow, content-deny, write-held, approved-proceeds, and a
stale approval refused — all 5 sealed and verifying.
Co-Authored-By: Claude Opus 4.8 <[email protected]>