jcp: WithinWindow, a sliding-window caveat over calls, payloads and measured joules #388
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/jcp-window-caveat"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Sweep 2026-10-19 item #7. A grant's budget bounded its whole life and not its rate: a day's budget could go in a minute. jcp-runtime budgets were cumulative only.
The caveat (jcp-schema)
Caveat::WithinWindow(Window { secs, limit })withWindowLimit::{MaxUj, MaxCalls, MaxDistinctPayloads}, plusDenyReason::WindowExceeded(window-exceeded). As withFlowBelow, a window is decided by a runtime gate against state, not from the frame;holds()refuses only a zero-second window.Enforcement (jcp-runtime, SPEC §4.12)
MaxCostUjdoes.charge,charge_chained) is placed at the grant's latest admission, which is never earlier than its own call's, so its joules stay in a window at least as long as they should. A debit with no admission at all stays in every window. No caller signature changed.restore()rebuilds spend from a timeless ledger, so afterwards every window counts as full untilresume_windows_at(t)names when the history is complete and a whole window has passed since. An unknown history is not an empty one.authorize_chained_for(…, need_uj)puts a caller's ceiling into the budget test, so a call refused for its ceiling is refused before it is admitted.Elsewhere
authorize_chained_for, replacing the after-the-factBudgetTooLowcheck. A serve refused for its ceiling therefore fills no window. The errors and their figures are unchanged.cloned_ref_to_slice_refs) are fixed in jcp-runtime's tests.Verification (build host)
-D warningsis clean on jcp-schema, -runtime, -bridge, -wire, -conformance and mesh-core.--all-targets.Not in this PR
From the same sweep item: carrying flow state across sandbox checkpoints. That is a decision about the sandbox capability
Enforcer's conformance class (stateless on purpose, C2), not wiring. Compiling a policy subset into these caveats is also left out.🤖 Generated with Claude Code
A grant's budget bounded its whole life and not its rate: a day's budget could go in a minute. jcp-runtime budgets were cumulative only. jcp-schema: - Caveat::WithinWindow(Window { secs, limit }), where limit is WindowLimit::{MaxUj, MaxCalls, MaxDistinctPayloads}. - DenyReason::WindowExceeded ("window-exceeded"). - holds() refuses only a zero-second window. Like FlowBelow, a window is decided by a runtime gate against state, not from the frame. jcp-runtime (SPEC §4.12): - Per grant carrying a window, a history of admissions (time, payload) and debits (time, µJ), pruned to the grant's longest window. - A call counts once admitted: allowed in a one-shot decide, or authorised in the two-phase path. A denied call counts for nothing and is debited nothing. Every path gates and records: decide, decide_priced, decide_flow, authorize, authorize_flow, the chained paths and every charge. Every link of a chain is counted, so a parent's window bounds its delegates together. - A joule window refuses a one-shot call whose cost would overfill it. In the two-phase path it admits while any of the window is left and binds at the debit: an overrun is denied window-exceeded and stays debited, as MaxCostUj does. - A debit that arrives without its time (charge, charge_chained) is placed at the grant's latest admission, never earlier than its own call's, so its joules stay in a window at least as long as they should. A debit with no admission at all stays in every window. No caller signature changed. - restore(), a timeless spend ledger, leaves every window counted as full until resume_windows_at(t) names when history is complete and a whole window has passed since. An unknown history is not an empty one. - authorize_chained_for(…, need_uj) takes a caller's ceiling into the budget test, so a call refused for its ceiling is refused before it is admitted. mesh-core: authorise with the request's joule ceiling via authorize_chained_for, replacing the after-the-fact BudgetTooLow check. A serve refused for its ceiling therefore fills no window. The errors and their figures are unchanged. jcp-bridge: a window caveat is unprojectable into an OAuth token. Durability: jcp-wire and mesh-core replay each decision through the gate at its recorded time, so windows rebuild across a restart (tests in both). The gateway resumes from receipts, which carry no time, so its windows stay full after a resume (its clock is also fixed at 0, a separate gap). SPEC: - §4.12: semantics, durability, sharding (n points admit n × the limit unless they share history). - The §4.9 wire form. - Conformance class C27, with a C27 check in jcp-conformance. Also: two pre-existing clippy errors (cloned_ref_to_slice_refs) in jcp-runtime's tests, so the crates touched are clippy-clean. Tests: 10 in jcp-runtime, 1 in jcp-wire, 1 in mesh-core, plus the bridge case and C27. Mutation testing: 30 mutants, all killed. They cover every limit's boundary, the slide, debit placement (latest, not first; MAX, not 0, without an admission), the restore gap, pruning, the ceiling-before- admission test, and each path's gate and recording. Verified on the build host: - jcp-rs: 575 tests, 119 behind features, every conformance class. - mesh-rs: 95 tests. - Clippy -D warnings clean on jcp-schema, -runtime, -bridge, -wire, -conformance and mesh-core. - sandbox-rs, joule-code-rs, jouledesk-rs, joulehook-rs and joule-ui-rs compile with --all-targets. - Repo checks pass. Co-Authored-By: Claude Opus 5.5 <[email protected]>cc52ca08c7b1510772d1