Chio/Docs
LOGIN · JOIN

Process attestation

Export a signed observation of a stopped host's run or one call, then verify it offline under pins you keep yourself.

Guide chio@5f63e6e

On this page

One signed file from a stopped process host shows a reviewer that a run, or one call inside it, happened under the authority it claims. The host signs an observation of its own retained state with its kernel key, and the reviewer checks that observation offline against pins held apart from the host.

The pins are the kernel public key, the runtime ID, and, for native tools, each server's launch-policy signer. The attest-run, attest-call, and attest-outcomes commands read the host's state and run only on Linux; verify-run, verify-call, and verify-outcomes read only the artifact and the pins, so they run on any Unix host, macOS included.

Start

1. Choose the artifact that covers the work under review

Pick the scope from the question before collecting anything. The reference run below fans out to reader-1 and reader-2, which read one file each through one confined launch of a repository reader.

Attestation scopeChoose an artifact.
dispatchsignsa₁a₂BKdλfρ₁ρ₂Jreader-1reader-2process hostfamily budgettoollaunch recordresourcejoina₁a₂BKdλfρ₁ρ₂Jreader-1reader-2hostbudgetlaunchresourcejoin

A completed run binds the graph, its calls, and its launch

attest-run writes chio.process.completed-fanout.v3. Every worker in the plan finished with an allowed result. The artifact binds each call's request, response, continuation custody, nonce custody, and log inclusion, plus the issued graph, family usage, the join and terminal receipts, and the native launch. Supervision.

Checks
signer_pin, runtime_pin, issued_capabilities, worker_responses, task_authority, actual_join_parents, terminal_result, runner_completion, aggregate_usage, continuation_custody, confinement_receipt_chain, execution_nonces, receipt_log_inclusion
Unchecked
scenario_matrix
The reference run: two workers, one confined launch of the repository reader, two tool receipts, and the join. The resource the reader touched sits outside every artifact. Brokered calls and the adversarial scenario matrix are left out.

Use attest-run for a completed fan-out whose workers all returned allow, attest-call for one allowed, denied, or uncertain call, and attest-outcomes for every worker, interrupted ones included. A completed-run view of an incident with a worker that never returned would drop the uncertainty you need to investigate.

An attestation makes retained work inspectable and leaves judgment of the result to the application: a correctly attributed worker can still write an incorrect report.

2. Collect the pins from your provisioning records

Set STATE to the existing host state directory, and take every pin from your retained provisioning records before you open any artifact. None of them comes from the artifact under test.

Terminalbash
STATE=/absolute/path/to/the/existing/host

TRUSTED_KERNEL_PUBKEY is the public-key file authority.db.kernel.pub in the initialized state directory, and TRUSTED_RUNTIME_ID comes from the same initialization. Each native server's TRUSTED_SERVER_ID and TRUSTED_LAUNCH_SIGNER come from the operator-owned host configuration, passed as SERVER=PUBLIC_KEY once per server; a host with only built-in mailbox tools needs none.

3. Export from the stopped Linux host

Stop the serving process, then export the run with the runner plan the workers ran under, or one call with its request and context frozen before invocation and its response exactly as received.

Terminalbash
chio process attest-run \
  --state "$STATE" \
  --plan "$STATE/reference-run-plan.json" \
  --out run.json

chio process attest-call \
  --state "$STATE" \
  --request request.json \
  --context context.json \
  --response response.json \
  --out call.json

You should see attest-run print the artifact path, its receipt ID, the runtime ID, and the worker count. Export takes the state directory's lease, so it refuses while a host is serving, and it requires the runner's completion journal with every worker in the plan completed and no container or socket cleanup outstanding.

Export reads each worker's retained request, response, and continuation custody, the captured invocation count from the family budget store, and, for a native tool, the launch and terminal receipts its call references. It signs a join receipt over the workers' tool receipts and a terminal graph receipt over the digest of their results, both only inside the artifact. It dispatches no tool call, creates no admission authority, and leaves the host's retained authority unchanged.

With execution_nonces: true in the host configuration, export first checkpoints the receipt log, adds each call's original execution-nonce custody and its receipt's inclusion proof, and writes chio.process.completed-fanout.v3; otherwise it writes chio.process.completed-fanout.v2. It verifies its own artifact before writing it to a new private file. An export failure never calls for replaying the effect: reconcile the original operation in recovery first.

4. Verify the run under your pins

Give the verifier the artifact and the pins from step 2. The recorded invocation below ran on Linux 6.17.0-1020-oracle x86_64 with a chio built for x86_64-unknown-linux-gnu, against the reference run.

Terminal · recorded runbash
chio process verify-run --artifact run.json --trusted-kernel-pubkey kernel.pub --runtime-id d8320379-e3f5-4eed-a80b-d526f2609aae --trusted-launch-policy-signer reference-reader=94dcfb118ae7b3321c8430d1bbaf1514c7df38addda1726422a0de61c0e28acb
Outputtext
{
  "schema": "chio.process.run-verification.v1",
  "runtime_id": "d8320379-e3f5-4eed-a80b-d526f2609aae",
  "verified_workers": [
    "reader-1",
    "reader-2"
  ],
  "captured_invocations": 2,
  "verified_native_launches": 1,
  "checks": [
    "signer_pin",
    "runtime_pin",
    "issued_capabilities",
    "worker_responses",
    "task_authority",
    "actual_join_parents",
    "terminal_result",
    "runner_completion",
    "aggregate_usage",
    "continuation_custody",
    "confinement_receipt_chain",
    "execution_nonces",
    "receipt_log_inclusion"
  ],
  "artifact_schema": "chio.process.completed-fanout.v3",
  "unchecked": [
    "scenario_matrix"
  ]
}

You should see two verified workers, two captured invocations, and one verified native launch: both calls went through one confined launch of the reader, and the host ran with execution_nonces: true, so the artifact is version 3. The verifier prints a report only when every check passes, and any failed check is a refusal instead. Standard error was empty and the command exited 0.

The same command with your own pins
Terminalbash
chio process verify-run \
  --artifact run.json \
  --trusted-kernel-pubkey "$TRUSTED_KERNEL_PUBKEY" \
  --runtime-id "$TRUSTED_RUNTIME_ID" \
  --trusted-launch-policy-signer "$TRUSTED_SERVER_ID=$TRUSTED_LAUNCH_SIGNER"

Omit the launch-signer flag for a mailbox-only host. Verify security evidence covers retaining and checking those pins.

5. Verify one call against its frozen request

Retain request.json and context.json from the call you sent, and give the verifier those files. It requires the artifact's request and context to equal yours, and never takes their values from the observation.

Terminal · recorded callbash
chio process verify-call --artifact call.json --request request.json --context context.json --trusted-kernel-pubkey kernel.pub --runtime-id d8320379-e3f5-4eed-a80b-d526f2609aae
Outputtext
{
  "schema": "chio.process.call-verification.v1",
  "runtime_id": "d8320379-e3f5-4eed-a80b-d526f2609aae",
  "process_id": "reader-1",
  "request_id": "process:8e8d938e5acfe9f2e4ebe25fbcbda6da1c2eb1ba7ffccece07d026ac80b589c3",
  "observed_operation_state": "completed",
  "custody_binding": "original_call_commitment_and_signed_store_readback",
  "artifact_schema": "chio.process.call-observation.v2",
  "checks": [
    "signer_pin",
    "runtime_pin",
    "original_request",
    "issued_capability",
    "worker_response",
    "retained_operation",
    "continuation_custody",
    "execution_nonces",
    "receipt_log_inclusion"
  ],
  "unchecked": [
    "task_authority",
    "aggregate_usage",
    "confinement",
    "physical_effects",
    "graph_completion",
    "scenario_matrix",
    "witnessed_parent_authority"
  ]
}

You should see reader-1 bound to its original retained operation, captured on the same host for the first worker of the same run. The unchecked list is longer than the run's: one call says nothing about task authority, family usage, physical effects, graph completion, or the scenario matrix, and a call made without the credential broker carries no evidence of its confinement or of witnessed parent authority.

custody_binding names where the custody claim came from. A denied call with no retained operation reports no_custody_claim, and that observation leaves open whether an effect occurred. To observe every worker at once, run attest-outcomes and verify-outcomes with the same state, plan, and pins as the run pair; for a version 3 artifact the report lists each worker's observed operation state and each referenced native launch, and names physical_effects, scenario_matrix, original_interrupted_launch, and target_death_without_terminal as unchecked.

Export and verify a call with your own pins
Terminalbash
chio process attest-call --state "$STATE" --request request.json \
  --context context.json --response response.json --out call.json
Terminalbash
chio process verify-call --artifact call.json \
  --request request.json --context context.json \
  --trusted-kernel-pubkey "$TRUSTED_KERNEL_PUBKEY" \
  --runtime-id "$TRUSTED_RUNTIME_ID"

attest-call signs the call's public operation binding and continuation custody while the host is stopped, adds nonce custody and receipt-log inclusion when the host has execution_nonces: true, and never dispatches, retries, or releases anything.

A passing verification establishes the artifact's bindings, under your pins

The run report's unchecked field names one entry, scenario_matrix. The verifier checks one completed run; the adversarial scenarios for authority, revocation, files, network, a host crash, and a shared budget are separate runs, the full matrix in secure a swarm. A version 2 artifact adds execution_nonces and receipt_log_inclusion to unchecked, because its host kept no nonce custody or log proof.

Log inclusion shows membership in the host's own log under a checkpoint signed by its own kernel key, and publication of that checkpoint or a later one extending it needs history evidence. The worker responses are what each tool returned through Chio, compared with nothing outside the artifact, such as the files the readers read.

Every process verifier also writes m5_acceptance_complete as the constant false, which the displayed selections leave out: no single attestation stands for the full acceptance scenario.

Exports refuse a serving host, and verifiers refuse any binding your pins contradict

Each refusal prints the standard four-line error to standard error (error[CODE]: and the message, then help, code, and docs lines) and exits 1. Most messages start with process host: ; the two signer-pin mismatches carry no prefix.

MessageConditionCommand
"worker run attestation requires Linux", "call custody attestation requires Linux", "worker outcome attestation requires Linux"
Host is not Linux
attest-*
"host state is already in use; stop the serving host before administrative changes"
A host holds the state directory
attest-*, prepare-broker-call
"workers or retained cleanup custody are incomplete"
A worker in the plan unfinished, or cleanup outstanding
attest-run
"every joined task must have an allowed result"
A worker ended with another verdict, such as a denial
attest-run
"run schema or pinned runtime differs"
Runtime pin or completed fan-out schema differs
verify-run
"receipt verification failed: receipt signer is not in the trusted kernel key pin set"
Another kernel key signed the artifact
verify-*
"missing external native policy signer pin"
A native server has no signer pin
verify-run
"cage launch policy is not signed by the configured trust root"
The pinned signer did not sign the launch policy
verify-run
"call differs from independently retained request or context"
Request or context differs from yours
verify-call
"execution nonce differs from original operation custody"
Nonce evidence differs from its operation
verify-*
"receipt log inclusion differs from original call", "checkpoint signature verification failed"
Proof or checkpoint fails under the pinned key
verify-*
"legacy call carries unsupported nonce or log claims", "legacy run carries unsupported nonce or log claims"
A v1 call or v2 run claims bindings it cannot carry
verify-*
"host has no native broker route"
Host configured without a native broker
prepare-broker-call
"broker capability differs from the live process authority"
Parent, subject, or validity differs
prepare-broker-call

A verifier refusal writes no partial report. Before any retry, compare the artifact with the original request, context, and provisioning records; an uncertain operation continues in recovery, because verification leaves its external effect unresolved.

Artifacts and verification checks

Each artifact is a signed observation receipt from chio-process-host. The verifier reports are chio.process.run-verification.v1, chio.process.call-verification.v1, and chio.process.worker-outcomes-verification.v2. Security receipts covers the launch and broker records inside an artifact, and replay protection covers the execution nonce.

chio.process.completed-fanout.v3

Every completed call's request, response, and continuation custody, with its original nonce custody and receipt-log inclusion, plus the issued graph, the runner journal, family usage, the join and terminal receipts, and each native launch.

chio.process.completed-fanout.v2

The same run without nonce custody or log inclusion, which its verifier reports as unchecked.

chio.process.call-observation.v1

One call without nonce or log claims, from a host without execution_nonces.

chio.process.call-observation.v2

One call's request, context, response, retained operation, and continuation custody, with receipt-log inclusion and, when the operation retained a nonce, its nonce custody.

chio.process.call-observation.v3

A brokered call, adding the broker's signed response, the original composite quota capture, and the call's confinement.

chio.process.worker-outcomes.v3

Every declared worker's outcome across the graphs sharing one family, family usage, each call's nonce custody and log inclusion, and the native launches the calls reference.

Brokered calls

A brokered tool reaches its provider through the credential broker. chio process prepare-broker-call signs the provider request with the process's retained proof key while the host is stopped, so the worker holds neither the proof key nor the provider credential. It takes the state directory, the process, the operation key, a broker capability file, and a provider request file, writes a chio.broker-execute.v1 request to a new private file, prints nothing, and sets the invocation ID the host derives for that process and operation key.

It refuses a host with no native broker route, a broker capability from another issuer or audience than the host configures or outside its validity interval, and a capability whose parent or subject differs from the process's live capability, or any capability while the process's own capability is invalid. The worker passes the file's contents as its tool call's arguments.

After the call, attest-call exports chio.process.call-observation.v3. verify-call then also needs --trusted-broker-host-config, the host configuration retained at provisioning, and for a governed parent --trusted-keylog-verifier, a key-log verifier you keep synchronized. Its report adds broker_signature, broker_response_binding, original_composite_capture, and confinement to its checks, plus witnessed_parent_authority for a governed parent.

signer_pin, runtime_pin

The artifact and the host's original provisioning receipt verify under the pinned kernel key and name the pinned runtime ID.

issued_capabilities

The graph's tasks are exactly the issued root and worker capabilities, each with its scope and validity, and every worker is a direct child sharing the root's delegation-family budget.

worker_responses

Each worker's signed response verifies against its request and its runtime, process, and capability context.

task_authority

Each tool receipt is bound to its task's issued capability, route, continuation token, delegation witness, revocation epoch, and budget pool.

actual_join_parents, terminal_result

The join names exactly the workers' tool receipts, and the join and terminal receipts carry the digest of the recorded results.

runner_completion

The runner journal shows each worker completed at least one attempt with exactly the input its plan assigned.

aggregate_usage

The captured invocation count covers every worker and stays within the issued family limit, with none still reserved.

continuation_custody

Each call's retained continuation claim is the one its tool receipt names, and it retained the task's issued single-use token after dispatch.

confinement_receipt_chain

Each native call's launch evidence verifies under the pinned policy signer, matches the host's configured command and manifest, and contains the call within the launch's tool surface and lifetime.

execution_nonces

Each call's original nonce custody matches its operation, capability, request, and issuance commitment, and no nonce repeats across workers.

A mismatch here refuses with "execution nonce differs from original operation custody", and covers the signed nonce, operation, request, capability, action parameters, policy, nonce requirement, timing, and dispatch commit.

receipt_log_inclusion

Each call's receipt has a Merkle inclusion proof under a checkpoint signed by the pinned kernel key.

A checkpoint whose own signature fails refuses with "checkpoint signature verification failed"; a proof that differs from the original call refuses with "receipt log inclusion differs from original call".

Next: check what the artifact leaves open