open-standards/jcp/jcp-rs/crates/jcp-cli
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Transaction Science 22e4af3158 jcp: caveats — the open attenuation axis (SPEC §4.9, C24)
Attenuation's axes all narrow by set-subtraction: remove actions, lower a
ceiling, shorten an expiry. They cannot express a condition the issuer did not
anticipate. `jcp-policy` can, but it is service-side — evaluated against a
PolicyContext the verifier must be told about, not carried in the capability.

A caveat is a condition embedded INSIDE the signed grant. Any enforcement point
holding it checks offline: no call back to the issuer, no shared policy config.
That is the distinction — policy is something a verifier must be told; a caveat
is something the token carries.

A design constraint found in the code rather than assumed: the predicate set is
bounded by what `decide` actually holds. jcp-policy's `Match::ContentContains`
CANNOT become a caveat, because the runtime sees a frame's `payload_hash` and
never its bytes. So a condition on content is expressible only as `PayloadIs`,
binding an exact payload rather than inspecting one. SPEC §4.9 states that
constraint normatively so implementations don't invent undecidable predicates.

Two variants express authority the fixed axes cannot:
- `PayloadIs` makes a grant ONE-SHOT — it authorizes one specific call, so a
  leaked grant cannot be replayed against different arguments. `capability`
  narrows to an action; it can never narrow to a call.
- `MaxCostUj` bounds a SINGLE invocation, where `budget_uj` is cumulative — so a
  delegate cannot burn the whole remaining budget in one call.
Plus ActionIn (may narrow below the capability), CallerIs, NotBefore, NotAfter.

Enforcement is in decide/decide_priced with the real cost. The two-phase
`authorize` path skips only the cost-dependent caveat — the cost is unknowable
there — and enforces every other; that binds at decide time and is documented
rather than silently ignored.

Attenuation is monotone: `attenuate` INHERITS the parent's caveats (dropping
them would widen a delegate's authority — the same amplification the dual-ceiling
work had to guard), `attenuate_caveated` appends, and `caveats_narrow` refuses a
child that lost one. Adding the field with no default made the compiler flag
every Grant construction site, which is how three bad auto-patches into function
parameter lists surfaced as build errors instead of silent breakage.

Empty caveat set ⇒ omitted from the canonical encoding, so a grant without
caveats is byte-identical to one minted before they existed — content id and
signature unchanged, asserted by test and by C24.

42 suites green, clippy clean, all conformance checks pass including C24.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-19 11:03:04 -04:00
..
src jcp: caveats — the open attenuation axis (SPEC §4.9, C24) 2026-07-19 11:03:04 -04:00
Cargo.toml jcp: energy backends, revocation + chain enforcement, jcp/1 wire binding 2026-06-10 02:00:58 -04:00