Chio/Docs
LOGIN · JOIN

Policy Rollout

Release a policy change across Chio nodes, check its decisions before traffic, and confirm the receipts carry the new identity.

Deployment chio@5f63e6e

On this page

At the end of a rollout, every edge that serves the affected operation runs the new policy, its receipts carry the new runtime hash, and a pinned release record names every input it loaded.

Each edge loads its Chio YAML or HushSpec policy from local files when it starts. Peers replicate authority, revocations, and other cluster state; policy stays on each node. Your deployment platform delivers the files and replaces the processes, and Replication & Convergence covers the state that does travel.

Start

Two hashes name a policy, and the guards can change under both

Chio records two identities for a loaded policy. The source hash follows the root file’s bytes; the runtime hash follows the normalized inputs the kernel fingerprints, and receipts carry it as policy_hash.

Policy identityChoose a view.
hsrcsource hashhrtruntime hashgcompiled guardsa selected rule in the root filean omitted rule in the root filea selected rule in a parentan omitted rule in a parentan auxiliary assetthe binary’s default guardshsrchrtgselected, rootomitted, rootselected, parentomitted, parentassetbinary defaults

Every edit reaches the compiled guards

Each row is one edit to a HushSpec release. All six change what the kernel enforces; only some of them move either hash. A filled cell means the edit changes that column. Policy identities.

Six edits to a HushSpec release against the two policy hashes and the guards the kernel compiles. The edits are representative; the default kernel configuration, extensions other than reputation, and the approval rule’s effect on default capabilities are left out.

Read a matching hash as evidence about the inputs it covers. Parent files, rules outside the fingerprint, and the binary’s own default guards can change enforcement with both hashes fixed, so a release pins them separately and tests the controls it changes.

1. Pin every input in a release record

Write a release manifest for the change. It is your deployment record, and it holds five things:

  1. Binary or image. An immutable build identity, the enabled features, and the configuration that launches each edge.
  2. Policy document. The exact root file, its format, and the path the process receives.
  3. Inherited policies and assets. Every HushSpec parent and referenced asset with its own file digest, in a stable filesystem layout where the layout affects identity.
  4. Policy identities. The source and runtime hashes the release binary produces from those inputs.
  5. Behavior checks. Allowed, refused, approval-sensitive, and output-sensitive cases with their expected results.

Keep the complete previous release beside it for rollback. A pinned root file alone leaves out the parents, the assets, and the default guards compiled into the binary.

2. Check the candidate’s decisions before traffic

Run the release binary against the server, tool, and arguments your workflow uses, with its own admission and receipt databases. Here the candidate is your pricing policy with the refund-ceiling guard added, checked against a quote for sku-118 that it must keep admitting.

Terminalbash
mkdir -p ./rollout-check
chio --session-db ./rollout-check/admission.db \
  --receipt-db ./rollout-check/receipts.db --json \
  check --policy ./candidate.yaml \
  --server pricing --tool quote \
  --params '{"sku":"sku-118","percent":10}'
Output
{
  "verdict": "ALLOW",
  "tool": "quote",
  "server": "pricing",
  "params": { "sku": "sku-118", "percent": 10 },
  "reason": null,
  "receipt_id": "rcpt-01a8…9c04",
  "policy_hash": "9a41…d3b6",
  "policy_source_hash": "e5f0…11c8",
  "check_mode": "preflight",
  "output_fixture": false
}

You should see the verdict, the reason, and both identities. The check evaluates against an internal tool server, so the tool itself never runs. Record policy_hash and policy_source_hash with each test case.

VerdictExitNext action
ALLOW0Also run the refusal cases
DENY2Compare the reason with the expected refusal
PENDING_APPROVAL3Exercise the approval and resume workflow

Keep the JSON from a refusal before you treat exit 2 as a failed test. Preflight refuses a policy with a compiled post-invocation pipeline; check those with --mode full --output-fixture ./tool-result.json and a representative result. Include one case the new policy changes on purpose and one it leaves alone, and follow approval cases through Human-in-the-Loop.

3. Replay captured traffic against the candidate

Replay runs a compatible traffic capture through a fresh kernel under the candidate and compares each recorded decision. Supply the tenant key you trust independently.

Terminalbash
chio replay traffic \
  --from ./capture.ndjson \
  --against path:./candidate.yaml \
  --tenant-pubkey ./tenant.pub --json

The comparison covers the verdict, the attributed guard, and the reason text, and the report is clean when its drift and error counts are both zero. A deliberate policy change produces expected drift; review those cases one by one.

Replay refuses policies with compiled post-invocation hooks or default grants for a specific server. A capture holds a response digest, without response bytes or the original tool-server id, so output-sensitive cases use explicit fixtures or an integration environment. Replay receipts take identifiers from their own namespace, and the run is an offline comparison before live traffic.

4. Replace the processes that enforce the operation

Deliver the complete release to each target and start a new process with the intended policy path. A running edge keeps the policy it loaded at startup, so the new rules take effect only in the new process. Use the platform’s rollout and rollback mechanisms.

During a rolling change, old and new processes enforce different rules. When a tighter rule must cover every request at once, drain the old execution paths before admitting affected traffic, and keep in-flight state as the operation’s contract requires.

List every edge that can execute the affected operation and test each one; leader order covers only the writes routed through the leader. Coordinate any authority or admission change with Leader & Failover and Authority & Rotation.

5. Confirm the receipts carry the new policy hash

Process health leaves the policy question open, so send the allowed and refused probes through every execution path. On the standard loaded-policy path, build_kernel assigns the runtime hash to the kernel’s policy_hash; a receipt’s content-addressed id includes that field and its signature binds the body. Group a receipt-store snapshot by hash, with :rollout_started_at bound to the start time in Unix seconds:

rollout-hashes.sqlsql
SELECT policy_hash,
       COUNT(*) AS receipts,
       MIN(timestamp) AS first_seen,
       MAX(timestamp) AS last_seen
FROM chio_tool_receipts
WHERE timestamp >= :rollout_started_at
GROUP BY policy_hash
ORDER BY last_seen DESC;
Output
policy_hash   receipts   first_seen   last_seen
9a41…d3b6          212   1791712944   1791716380
3c7b…8010           38   1791712801   1791713420

You should see the new hash take over from the old one. The query groups the records in one store, so read it beside your list of execution paths: a quiet process writes no fresh receipt, and one store can hold replicated records from several paths. On durable admission paths, terminal validation also compares each receipt’s policy hash with the operation’s admission binding.

Read more in Evidence Export, which preserves and verifies the decision records.

An inherited edit can change enforcement with both hashes fixed

Take an unchanged root file that extends a parent. Tighten only the parent’s enabled velocity rule. The compiler builds a different velocity guard, and the root’s source digest and the runtime fingerprint stay as they were, because the parent sits outside one and velocity outside the other.

Make the same edit in the root file and the source hash changes. Kernel construction also registers the binary’s default runtime guard profile before the policy-compiled pipeline and appends its post-invocation hooks, from outside both hashes. Hash the parent files independently, pin the build, and test the changed control.

Roll back by restarting on the previous release

Restart the affected edges on the previous release’s complete inputs through the same platform rollout, then repeat the probes and the hash query. The release record from step 1 is what makes that restart exact.

Receipts still show the old hash after replacement

An edge that loaded the old files still serves the operation. Find it from your execution-path list, confirm the policy path it started with, and replace it. Embedders that manage their own lifecycle replace their kernels by their own procedure; in-process WASM guard replacement swaps one module and distributes no fleet policy.

An exported policy file disagrees with past receipts

An evidence export can attach a policy root file with metadata holding its source and runtime hashes. The loader reads that file at export time, and the export verifier checks the attachment without recompiling it into a policy identity. Compare the receipts with the preserved release files, which record what the past process loaded.

API Protect receipts carry a different identity

The API Protect route evaluator derives its policy identity from OpenAPI specification bytes, and without a spec file it discovers the spec from the upstream server at startup. Pass a pinned local --spec file for repeatable releases, and compare identities within the same receipt-producing path.

Policy identity reference

Source and runtime hashes by format

  • Source hash, Chio YAML. SHA-256 of the root file bytes.
  • Source hash, HushSpec. Starts with the root-file digest. An enabled threat-intel pattern database adds its resolved path and content digest through an auxiliary-asset fingerprint.
  • Runtime hash, Chio YAML. A serialized fingerprint of the format, kernel configuration, guards configuration, and compiled default capabilities.
  • Runtime hash, HushSpec. A serialized fingerprint of the format, default kernel configuration, compiled default capabilities, selected resolved rule blocks, reputation extension, and auxiliary assets.

The source hash covers the root file, without a recursive digest of HushSpec parents. The runtime hash covers its listed inputs, without a digest of the complete compiled pipeline or the binary. A path inside the auxiliary-asset identity makes identical asset bytes produce different identities on different filesystem layouts.

Root file and format detection

The loader detects HushSpec from the top-level format marker and otherwise parses Chio YAML. Any change to the root bytes changes the source digest, including an edit to a rule the runtime fingerprint omits.

Inherited policy resolution

The HushSpec resolver follows filesystem parents and merges them before compilation. Its default limits allow a chain of 32 documents and 4 MiB per document, with cycle detection. List the parents in the release manifest even when the root file is unchanged.

Runtime fingerprint coverage

  • Included rule blocks. forbidden_paths, path_allowlist, egress, secret_patterns, patch_integrity, shell_commands, and tool_access.
  • Rule blocks outside the fingerprint. velocity, computer_use, remote_desktop_channels, input_injection, browser_automation, and code_execution, which still compile into guards.
  • Human approval rule. Serialized only through its effect on the default capabilities when compiled with enabled tool_access; coverage follows the resulting scope.
  • Extensions. The reputation extension and detected auxiliary assets; the rest of the extensions tree stays outside.

Source files

  • crates/platform/chio-control-plane/src/policy/loader.rs and util.rs: source bytes, auxiliary assets, and runtime fingerprint inputs.
  • crates/guards/chio-policy/src/resolve.rs and compiler/rules.rs: inherited inputs and rule compilation.
  • crates/products/chio-cli/src/cli/runtime.rs and replay/execute.rs: check modes, identity output, and replay limits.

Next: define the rules you release

Policy Rollout · Chio Docs