jcp-runtime: Gate a call on its argument propagated provenance #40

Closed
dcharlot wants to merge 1 commit from jcp-runtime-flow-gate into jcp-flow-lattice
Owner

Increment 3 of the FL-2 flow-lattice design (FLOW_LATTICE_PLAN.md) — the gate that makes a tainted call actually deny. Stacks on #37 (the lattice); base is jcp-flow-lattice. Parallel to #39 (no file overlap: #39 is the jcp-flow crate, this is jcp-schema additions + jcp-runtime).

What. A new check 6c in the reference monitor tests the argument's propagated label against the grant's flow ceiling and the resource's taint floor — so a value derived from an untrusted read can be refused from a capability even when its literal bytes match no rule, the question ContentContains cannot ask.

Placement is load-bearing. 6c sits after capability (the ceiling is inside the grant; judging flow before the signature verifies would seal a verdict over attacker-chosen policy) and before caveats and budget (a tainted call must cost zero joules — the tool-poisoning invariant, extended to derived taint).

The facts argument distinguishes three cases: absent facts under a ceiling fail closed; a ceiling-free grant with absent facts is untouched (every grant that exists today); clean facts pass any ceiling, byte-identical to the plain path. Refinement holds — the gate only ever denies more.

Three couplings make the wedge real:

  • TaintFloor mirrors trust roots — an operator refusal of given origins that no grant carries and no delegated ceiling can widen.
  • A tainted sub-budget — a tighter wallet for work that touched an untrusted source, pre-checked at authorize_flow (denies before the tool runs) and depleted by charge_flow, tracked apart from the main budget it never converts into.
  • The chain path mirrors 6c against the attenuation-fold of every link's ceiling, so a delegated call is at most as permissive as its strictest hop, and also fails closed without facts.

The tainted budget rides inside FlowCeiling (from the FlowBelow caveat), not a new Grant field, so the grant encoding is untouched — a grant without a flow caveat is byte-identical to one minted before the feature (pinned by a test). FlowFacts lives in jcp-schema as pure data so the runtime needn't depend on the jcp-flow scan machinery.

Eleven new tests: deny-with-zero-debit, the fail-closed law, ceiling-free no-op, byte-identical clean calls, floor refusal, the tainted sub-budget pre-check, and the chain form of each. Full JCP workspace: 215 pass, 0 warnings.

Next: the jcp/3 receipt seal, then the gateway relay that computes the facts and drives authorize_flow/charge_flow.

Release Notes:

  • Added flow-provenance gating (check 6c), a taint floor, and a tainted sub-budget to the JCP runtime
Increment 3 of the FL-2 flow-lattice design (FLOW_LATTICE_PLAN.md) — **the gate that makes a tainted call actually deny.** Stacks on #37 (the lattice); base is `jcp-flow-lattice`. Parallel to #39 (no file overlap: #39 is the jcp-flow crate, this is jcp-schema additions + jcp-runtime). **What.** A new **check 6c** in the reference monitor tests the argument's propagated label against the grant's flow ceiling and the resource's taint floor — so a value *derived from* an untrusted read can be refused from a capability even when its literal bytes match no rule, the question `ContentContains` cannot ask. **Placement is load-bearing.** 6c sits *after* capability (the ceiling is inside the grant; judging flow before the signature verifies would seal a verdict over attacker-chosen policy) and *before* caveats and budget (a tainted call must cost **zero joules** — the tool-poisoning invariant, extended to derived taint). **The `facts` argument distinguishes three cases:** absent facts under a ceiling **fail closed**; a ceiling-free grant with absent facts is untouched (every grant that exists today); clean facts pass any ceiling, **byte-identical** to the plain path. Refinement holds — the gate only ever denies more. **Three couplings make the wedge real:** - `TaintFloor` mirrors trust roots — an operator refusal of given origins that no grant carries and no delegated ceiling can widen. - A **tainted sub-budget** — a tighter wallet for work that touched an untrusted source, pre-checked at `authorize_flow` (denies before the tool runs) and depleted by `charge_flow`, tracked apart from the main budget it never converts into. - The **chain path** mirrors 6c against the attenuation-fold of every link's ceiling, so a delegated call is at most as permissive as its strictest hop, and also fails closed without facts. The tainted budget rides inside `FlowCeiling` (from the `FlowBelow` caveat), not a new `Grant` field, so the grant encoding is untouched — a grant without a flow caveat is byte-identical to one minted before the feature (pinned by a test). `FlowFacts` lives in `jcp-schema` as pure data so the runtime needn't depend on the jcp-flow scan machinery. **Eleven new tests:** deny-with-zero-debit, the fail-closed law, ceiling-free no-op, byte-identical clean calls, floor refusal, the tainted sub-budget pre-check, and the chain form of each. Full JCP workspace: 215 pass, 0 warnings. Next: the `jcp/3` receipt seal, then the gateway relay that computes the facts and drives `authorize_flow`/`charge_flow`. Release Notes: - Added flow-provenance gating (check 6c), a taint floor, and a tainted sub-budget to the JCP runtime
The lattice defines flow labels and jcp-flow computes them; this is the check
that acts on one. A new check 6c in the reference monitor tests the argument's
label against the grant's flow ceiling and the resource's taint floor, so a value
derived from an untrusted read can be refused from a capability even though its
literal bytes match no rule — the question ContentContains cannot ask.

6c sits after capability and before caveats and budget, each placement load-
bearing. After capability, because the ceiling lives inside the grant and judging
flow before the signature verifies would seal a verdict over attacker-chosen
policy. Before budget, because a tainted call must cost zero joules — the same
invariant the tool-poisoning denial already holds, now extended to derived taint.

The facts argument distinguishes three cases on purpose. Absent facts (an
un-migrated caller) under a ceiling fail closed; a ceiling-free grant with absent
facts is untouched, which is every grant that exists today; clean facts pass any
ceiling and are byte-identical to the plain path. So refinement holds — the gate
only ever denies more, never allows what JCP denies now.

Three couplings make the wedge real rather than decorative. A TaintFloor mirrors
trust roots: an operator refusal of given origins on a resource that no grant
carries and no delegated ceiling can widen. A tainted sub-budget gives work that
has touched an untrusted source a tighter wallet, pre-checked at authorize so it
denies before the tool runs and depleted by charge_flow on the allow path,
tracked apart from the main budget it never converts into. And the chain path
mirrors 6c against the attenuation-fold of every link's ceiling, so a delegated
call is at most as permissive as its strictest hop and also fails closed without
facts.

The tainted budget rides inside FlowCeiling (from the FlowBelow caveat) rather
than a new Grant field, so the grant encoding is untouched; a grant without a
flow caveat is byte-identical to one minted before the feature, its content id
and signature unchanged (pinned by a test). FlowFacts lives in jcp-schema as pure
data so the runtime need not depend on the jcp-flow scan machinery for it.

Eleven new tests: the deny-with-zero-debit core, the fail-closed law, the
ceiling-free no-op, byte-identical clean calls, the floor refusal, the tainted
sub-budget pre-check, and the chain form of each.

Increment 3 of the FL-2 plan; the jcp/3 receipt seal and the gateway relay that
computes the facts follow.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
dcharlot closed this pull request 2026-07-27 19:14:34 -04:00

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!40
No description provided.