Runx receipts must explain the authority boundary without becoming a secret
side channel. The compact policy projection lives under authority_proof and
validates against runx.authority-proof.v1. The adapter-observed execution
boundary is also copied into receipt.authority.enforcement before sealing, so
it is part of the signed receipt body rather than unsigned read metadata.
Allowed public fields:
run_id,skill_name, andsource_type- requested connected-auth scopes and whether the skill declared mutating work
- scope admission status, granted scopes, grant id, and decision summary
- provider, connection id, grant reference, and
material_refhash - typed execution-boundary observation and approval result
- redaction policy status
Banned fields:
- raw access tokens, refresh tokens, API keys, passwords, client secrets, and provider credential bodies
- full private stdout or stderr bodies in public projections
- ambient environment dumps or unbounded local command logs
- unchecked provider output bodies in comments, public evidence, or ledgers
Credential material is represented by hashed opaque handles such as
material_ref_hash. Receipt writers still hash stdout and stderr, and metadata is
passed through the receipt redactor before signing. Hosted workers and local
runners use the same authority_proof schema name; consuming repos add policy
for source channels, assignees, and target repositories outside the core proof.
Local secret handoff is owned by the declared skill-credential contract and the
Rust CredentialDelivery boundary. Hosted provider execution continues to use
the credential-broker contract and opaque handles. In both paths, secret values
may cross only the trusted adapter/supervisor delivery channel, not authority
proofs, receipts, invocation metadata, adapter observations, or public provider
evidence. See Credential Resolution.
The Rust AuthorityProof wire structs and
ExecutionBoundaryObservation live in runx-contracts, which owns their
portable JSON shape and generated schemas. runx-core alone owns the policy
projection that constructs an authority proof from admission decisions. The
runtime adapter alone owns the observed execution-boundary value and binds it
into the receipt at seal time. Neither a skill nor an agent may self-attest that
observation.
The local runner applies authority in this order:
- Manifest validation identifies the selected runner and its declared credential requirement.
- The credential resolver selects one profile, project binding, hosted handle, or declared workspace source and constructs a redacted delivery.
- Structural policy admission resolves the exact authority grant.
- The adapter delivers only the resolved environment and credential material, executes through the lane's canonical boundary, and redacts captured output.
- The signed receipt records the observed boundary, public observations, and output hashes without raw material.
provider_permission graph policy may declare required scopes, an expected
grant id, and the authority verb. It must not declare granted_scopes; granted
scopes come only from operator-carried runtime grant evidence.
Legacy provider-permission and MCP host steps fail closed unless the operator supplies:
RUNX_PROVIDER_PERMISSION_GRANT_IDRUNX_PROVIDER_PERMISSION_GRANTED_SCOPES, encoded as a JSON array of exact capability strings (for example["repo.read","issues.write"])
Native provider.read and provider.mutate steps use the same evidence model
without requiring that setup in the common Connect path. They authenticate the
operator, read active grant metadata from Connect, and select the unique grant
whose provider and authoritative scopes cover the declared step. The selection
is cached for the run. No provider token or credential body crosses into the
skill. A host-injected native grant is a complete evidence tuple: the two
variables above plus RUNX_PROVIDER_PERMISSION_PRINCIPAL_REF. Partial tuples
never report ready.
The native boundary can also require exact expected_result identity fields
and project only declared result_fields before the result enters a receipt.
This prevents a correctly scoped call from being mistaken for the wrong
resource and keeps undeclared secret-adjacent material out of skill output.
When more than one active grant matches, resolution fails as ambiguous. Set
RUNX_PROVIDER_PERMISSION_GRANT_ID to select one; Runx then reads that grant's
current scopes from Connect. A host may inject the complete three-variable
tuple to carry already-resolved native grant evidence without a discovery call.
When a provider-permission effect is admitted, the sealed step receipt records
the operator grant as a typed runx:grant:* reference under
receipt.authority.grant_refs. The grant id is evidence of the authority that
admitted the effect; it is not a credential body and does not carry provider
token material.
Spend-class payment authority must carry an aggregate cap (max_per_run_units
or max_per_period_units) in addition to any per-call cap. Both aggregate caps
are enforced by the runtime spend ledger.
Per-run: each run's reserved spend is bounded by the smaller of the two declared caps, because a run never spans more than one period.
Per-period: when the authority also declares a period of daily, weekly,
or monthly, every spend is additionally reserved against a durable
calendar-window ledger in the effect state file (RUNX_EFFECT_STATE_PATH or
<receipt dir>/effect-state.json), so the cap holds across runs inside one
UTC window. An unrecognized period value fails closed at admission instead
of becoming an unenforced annotation. A period cap declared without a period
is enforced only as the run-level clamp, and the period ledger only exists
when an effect state path is configured — operators who want cross-run spend
bounds must configure a stable state path.
Period ledgers are bounded during the same locked state transaction that records the reservation. For each family/authority/currency/period tuple, the runtime retains at least the active reservation window and the immediately previous window, keeps any newer windows already present, and prunes older period-ledger rows. Idempotency entries, finality records, finality events, consumed capabilities, and run-spend ledgers are not pruned by this retention pass. Out-of-order reservations remain safe because retention is computed relative to the reservation being recorded, not relative to the newest window currently present in the file.
When a payment effect is admitted, the sealed step receipt records authority
evidence under receipt.authority.grant_refs: the admitted payment authority
reference and the spend-capability reference. Replay receipts preserve those
same authority references so a replayed sealed effect remains verifiable
against the same admitted authority boundary.
Payment supervisor proofs bind the original settlement evidence through
evidence_digest. Rebinding a stored proof to a re-sealed receipt first
re-verifies that the stored evidence still hashes to the sealed digest, so
evidence altered after issuance is rejected instead of silently re-blessed.
runx verify [receipt-id] [--receipt-dir dir] [--receipt <path|->] [--json]
re-checks sealed receipts from disk with no runtime or network dependency:
canonical body digests, content-addressed ids, linked-tree parent/child
integrity, scope adherence for privileged effects, and — when
RUNX_RECEIPT_VERIFY_KID and
RUNX_RECEIPT_VERIFY_ED25519_PUBLIC_KEY_BASE64 are set — production Ed25519
signatures against the operator-trusted key. When those explicit verifier
variables are absent, a complete RUNX_RECEIPT_SIGN_* identity supplies its
own public verifier; explicit verifier configuration always takes precedence.
This lets the signing operator verify its own local store without duplicating
key configuration while independent verifiers need only the public key. Store
mode groups receipts into
trees by lineage; a chain that points at a receipt missing from the store is
reported as incomplete and fails verification. Single-receipt mode emits one
runx.verify_verdict.v1 JSON verdict suitable for hosted notaries and other
embedding surfaces. Because a single document cannot prove tree membership,
lineage is reported as unverified without failing an otherwise valid
receipt. The command exits non-zero on invalid receipts, so it can gate
automation.
fixtures/receipt-verify/ is the conformance corpus for machine consumers.
Every embedding surface that claims to verify a runx receipt must replay those
fixtures through the pinned runx binary and match the expected verdicts
instead of carrying a second verifier implementation in another language.
Scope adherence is intentionally pure and offline. Any act carrying typed
EffectEvidence without corresponding receipt.authority.grant_refs produces
EffectGrantEvidenceMissing, fails verification, and exits non-zero. This is
the boundary between a signed activity log and a governance proof: the receipt
must show both the privileged effect and the operator-granted authority that
admitted it.
runx doctor authority [--json] gives operators a redacted authority readiness
view before exercising privileged effects. It reports:
- receipt signer readiness, naming
RUNX_RECEIPT_SIGN_KID,RUNX_RECEIPT_SIGN_ED25519_SEED_BASE64, andRUNX_RECEIPT_SIGN_ISSUER_TYPE - receipt verification readiness and whether it resolves from the explicit
RUNX_RECEIPT_VERIFY_*pair or the complete signing identity - the resolved effect-state path when configured
- the consequence when
RUNX_EFFECT_STATE_PATHis unset: cross-run spend caps, payment idempotency, and effect replay recovery are not durable without a configured state path - provider-permission grant readiness, reporting either authenticated Connect
discovery or the complete host-injected
RUNX_PROVIDER_PERMISSION_GRANT_ID, JSON-arrayRUNX_PROVIDER_PERMISSION_GRANTED_SCOPES, andRUNX_PROVIDER_PERMISSION_PRINCIPAL_REFpath
The diagnostic may show key ids and resolved filesystem paths. It must not show signing seeds, public key material, provider scope values, grant ids, or credential bodies.