Chio/Docs
LOGIN · JOIN

BuildProtocols

Cross-Provider Policy

Govern model-selected calls with one policy. Run two providers, tighten a write allowance, and inspect the resulting effects and signed receipts.

On this page

Run two models against the same application

A model can propose three writes. Chio decides how many reach your application. Both OpenAI Chat Completions and Anthropic Messages use the same notes host, tool schemas and role policy. At a limit of two, two notes are saved and the third call is refused. Tighten the limit to one and the host dispatches only one write.

Download the complete application. Extract it anywhere, install Rust and uv, and configure your funded provider accounts as described in the included README. Then run:

From agent-notesbash
python3 compare.py --openai-model "$OPENAI_MODEL" --anthropic-model "$ANTHROPIC_MODEL"

The command makes four real model runs. It builds and starts the included Chio host, requests three unique notes per run, and verifies the saved results. Your original policy file and existing notes remain intact. Complete model responses, policy snapshots, MCP exchanges and signed receipts stay in the project.

See the policy change the result

These captures used OpenAI and Anthropic models through a funded OpenRouter account. All 16 tool calls have distinct signed receipts. Select a provider and allowance, then inspect any call. The saved note list was checked independently against each write result.

Recorded executionsSeptember 10, 2026
Model API
Chio write limit

2 saved1 refused4 signed receipts verified

These controls select actual captured runs through a funded provider endpoint. They do not make a model request. The model is openai/gpt-4.1-mini.

createNote Completed

{
  "body": {
    "text": "openai-2-c0c36d57 alpha"
  }
}
Request
mcp-edge-req-7f6384cca6834111afb65a2e68c00f21
Receipt
03a3f98130bfde5578d26e5a129db8e31b4b1be77ee221332ade54693238f177
Operator-selected signer
0fde22e00547adb7a8efda9ea2b0289192dc38a86e65019c4d08f683c5816efa

Signature, content hashes and trusted signer verified. Request and tool arguments matched independently.

Complete signed receipt
{
  "id": "03a3f98130bfde5578d26e5a129db8e31b4b1be77ee221332ade54693238f177",
  "timestamp": 1789081255,
  "capability_id": "cap-01a08d8d-746a-7270-ac83-c932783a9c1f",
  "tool_server": "notes",
  "tool_name": "createNote",
  "action": {
    "parameters": {
      "body": {
        "text": "openai-2-c0c36d57 alpha"
      }
    },
    "parameter_hash": "6032f7a85f5ba975da92cb2e5add3cfe1fb35743a3e7f860fdd9d0112d55226e"
  },
  "decision": {
    "verdict": "allow"
  },
  "receipt_kind": "mediated_decision",
  "boundary_class": "prevent",
  "tool_origin": "caller_executed",
  "redaction_mode": "none",
  "content_hash": "9ece9592bf1b1c962b608d66ccc28b07842c5ca0ebc5852ed617dd9d1a8d5a02",
  "policy_hash": "6b65b1da0151c482eddbbd24233e860312fe0510910a49143402f569b6f9b5d8",
  "metadata": {
    "admission_operation": {
      "compensation_status": "not_compensated",
      "coordinator_lease_epoch": 1,
      "coordinator_lease_id": "01a08d8d-7393-71ea-a4ee-9f00ddf63c48",
      "operation_id": "3d1e55a6fa3dd3c289631980d8f45e09b73457eeceb05ab8af22a56efd7ba116",
      "projected_dispatch_state": "terminal",
      "projected_operation_version": 8,
      "projected_state": "completed",
      "request_binding_hash": "3dee093d47c5efaa714d4fc4f315f1251979ddaa601ad0ff7b95ce7d19c98954",
      "request_id": "mcp-edge-req-7f6384cca6834111afb65a2e68c00f21",
      "request_namespace_digest": "9612780366ad05794d723e9324957370a6b14f67d933da155c49e321b233e90d",
      "retained_dispatch_commit": {
        "committed_version": 6,
        "coordinator_lease_epoch": 1,
        "coordinator_lease_id": "01a08d8d-7393-71ea-a4ee-9f00ddf63c48",
        "provider_attempt": {
          "attempt_id": "attempt:3d1e55a6fa3dd3c289631980d8f45e09b73457eeceb05ab8af22a56efd7ba116",
          "operation_id": "3d1e55a6fa3dd3c289631980d8f45e09b73457eeceb05ab8af22a56efd7ba116",
          "transport_id": "kernel-tool-server:notes",
          "transport_key_epoch": 1
        },
        "store_fence": {
          "lease_id": "01a08d8d-7393-71ea-a4ee-9f00ddf63c48",
          "owner_epoch": 1,
          "store_uuid": "01a08d8d-71a7-7108-bd50-f86722738054"
        }
      },
      "schema": "chio.admission-receipt.v1",
      "store_fence": {
        "lease_id": "01a08d8d-7393-71ea-a4ee-9f00ddf63c48",
        "owner_epoch": 1,
        "store_uuid": "01a08d8d-71a7-7108-bd50-f86722738054"
      },
      "tool_outcome_id": "c6c4f396c3c63dca1806972e4613fe511655c365e93e797f753170dcca70cd83",
      "tool_outcome_version": 2,
      "trusted_time_unix_ms": 1789081255924
    },
    "attribution": {
      "delegation_depth": 0,
      "grant_index": 2,
      "issuer_key": "0fde22e00547adb7a8efda9ea2b0289192dc38a86e65019c4d08f683c5816efa",
      "subject_key": "1be559c30de81b4a9b22fb0b8a63ffd1637cf7b7e79dd913da0aea2118c5212d"
    },
    "chio_receipt_signing_nonce": "rcpt-01a08d8d-8032-717d-97d5-0e430c9a841a",
    "receipt_context": {
      "request_id": "mcp-edge-req-7f6384cca6834111afb65a2e68c00f21"
    }
  },
  "trust_level": "mediated",
  "kernel_key": "0fde22e00547adb7a8efda9ea2b0289192dc38a86e65019c4d08f683c5816efa",
  "signature": "dc01d221d31ec6d11744597111bec93b31c518270c9cba7dd9a1960e9f43cc85a358f3d1037988eaea83b750d76091b83aa50349f5369463cbfd472cbd29730a"
}

Download all four runs and their original receipts

Make the rule yours

For your own run, edit the writer in notes-api/authority.json, then start either provider client. The host issues the new session capability from this configuration and binds the complete policy snapshot into its policy hash.

notes-api/authority.json · writer limited to one writejson
{
  "writer": {
    "allow_read": true,
    "allow_write": true,
    "max_writes": 1
  },
  "auditor": {
    "allow_read": true,
    "allow_write": false,
    "max_writes": 0
  }
}
Use your changed policybash
python3 run.py openai --model "$OPENAI_MODEL" "Attempt to save alpha, beta and gamma once each, then list the actual saved notes."

The provider loop forwards full tool arguments to its existing MCP session. It does not decide admission, replenish the grant after a refusal, or treat the model's final text as proof of a write. The shared boundary is small:

openai_notes.py · inside the model's tool-call looppython
result = session.call(call.function.name, arguments)
messages.append({
    "role": "tool",
    "tool_call_id": call.id,
    "content": json.dumps(result),
})

Receipts from different executions have different identities, timestamps and signatures. Compare their authorized operation, policy, verdict and actual effect. Byte equality would be the wrong acceptance test. Each original receipt remains available for independent verification.

Continue with the complete provider clients or a writer and auditor with separate framework authority.

Provider format conformance

The older eight-provider capture comparison is useful for translation regression checks. Its full source, commands and original anchors remain below. It checks recorded formats; the executable policy application above performs new kernel decisions and application effects.

Eight-provider conformance reference and complete records

One operation, eight provider formats

Chio separates a model provider’s tool-call syntax from the authority to execute an operation. That separation lets an agentic operating system govern tools across model runtimes. This project examines the translation layer: eight saved exchanges request get_weather for San Francisco in Celsius and carry an allow verdict.

Run it to compare those recorded fields and catch a fixture-contract mismatch. It is an offline verifier: the command makes no model request, performs no weather lookup, and signs no new kernel receipt. For an application you can connect to a model, start with the provider and LangChain project or the complete Rust host.

Run the comparison

Fetch the project and run it. You need Rust; rust-toolchain.toml selects the compiler and Cargo fetches the conformance crate with its recorded fixtures. You can also download the project.

Terminalbash
chio new cross-provider-policy --example cross-provider-policy
cd cross-provider-policy
cargo run --locked --quiet -- --dry-run

The command prints eight comparison records, then this captured summary. A successful exit means their selected fields match the configured fixture contract and each other.

Captured final linetext
cross-provider verdict equality: 8 receipts validated for policy cross-provider-policy-demo

Download the complete captured stdout or all eight comparison records as JSON. The required --dry-run flag makes the command’s offline role explicit.

Follow the weather request

Read the example’s contract

The file below is the project's complete configuration. Its fixture_contract names the expected operation, exact arguments and recorded verdict. The example’s own parser reads this subset; it does not load the full HushSpec guard engine. Keep this verifier configuration separate from an application’s runtime policy.

policy.yamlyaml
hushspec: "0.1.0"
name: cross-provider-policy-demo
description: Dry-run policy proving equivalent tool verdicts across native provider adapters.

rules:
  tool_access:
    enabled: true
    default: block
    allow:
      - get_weather
  fixture_contract:
    scenario_id: weather_lookup_allow
    required_tool: get_weather
    required_arguments:
      location: "San Francisco, CA"
      unit: celsius
    expected_verdict: allow

Distinguish the two input paths

Provider capturesWork performed
OpenAI, Anthropic, Bedrock
Replay the provider adapter: lift recorded requests, compare normalized invocations, and check lowered tool responses against the capture. The verdict comes from the saved record.
Gemini, Mistral, Groq, Ollama, Cohere
Read the invocation and verdict directly from the saved NDJSON record. This example does not run their deep adapter replay paths.

Inspect a complete comparison record

This is the first record from the executed command. Its receipt_id identifies a fixture record. The object has no kernel signature, signer key or content hash; use it for comparison, rather than as a signed execution receipt.

Captured OpenAI comparison record · completejson
{
  "receipt_id": "rcpt_openai_basic_single_tool_call_allow",
  "body": {
    "policy_id": "cross-provider-policy-demo",
    "scenario_id": "weather_lookup_allow",
    "tool_name": "get_weather",
    "arguments": {
      "location": "San Francisco, CA",
      "unit": "celsius"
    },
    "verdict": {
      "verdict": "allow",
      "reason": null,
      "redactions": []
    },
    "provenance": {
      "provider": "open_ai",
      "request_id": "call_weather_1",
      "api_version": "responses.2026-04-25",
      "principal": {
        "kind": "open_ai_org",
        "org_id": "org_chio_demo"
      },
      "received_at": "2026-04-25T00:00:01.150Z"
    }
  }
}

Make a mismatch visible

Copy the contract so the original stays available:

Terminal · cross-provider-policybash
cp policy.yaml comparison.yaml

In comparison.yaml, change only rules.fixture_contract.expected_verdict:

Inside rules.fixture_contractyaml
expected_verdict: deny
Check the changed expectationbash
cargo run --locked --quiet -- \
  --dry-run --policy comparison.yaml
Captured error · exit 1text
fixture rcpt_openai_basic_single_tool_call_allow verdict Allow did not match policy verdict Deny

The captured request still says allow. Changing the expected verdict makes the verifier reject that mismatch; it does not reevaluate the request and turn it into a denied invocation. Restore expected_verdict: allow to pass again.

Compare the fields that should agree

The comparison removes provider provenance and the outer receipt identifier, then serializes the remaining body as canonical JSON. Every selected record reduces to these fields:

Compared body · formatted for readingjson
{
  "policy_id": "cross-provider-policy-demo",
  "scenario_id": "weather_lookup_allow",
  "tool_name": "get_weather",
  "arguments": {
    "location": "San Francisco, CA",
    "unit": "celsius"
  },
  "verdict": {
    "verdict": "allow",
    "reason": null,
    "redactions": []
  }
}

Connect provider syntax to the recorded invocation

The OpenAI fixture contains four NDJSON records. This is the complete provider response payload from its second record; arguments arrive as an encoded JSON string.

OpenAI fixture · upstream_response payloadjson
{
  "id": "resp_openai_basic_single_tool_call",
  "object": "response",
  "output": [
    {
      "type": "message",
      "content": [
        {
          "type": "output_text",
          "text": "Checking the forecast."
        }
      ]
    },
    {
      "type": "function_call",
      "call_id": "call_weather_1",
      "name": "get_weather",
      "arguments": "{\"location\":\"San Francisco, CA\",\"unit\":\"celsius\"}"
    }
  ]
}

The third record stores the normalized invocation and the captured verdict. Adapter replay checks that the provider request lifts into this invocation. It does not independently authorize the weather lookup.

OpenAI fixture · complete kernel_verdict recordjson
{
  "ts": "2026-04-25T00:00:01.150Z",
  "schema": "chio-provider-conformance.capture.v1",
  "fixture_id": "openai_basic_single_tool_call",
  "family": "tool_use_basic",
  "api_snapshot": "2026-04-25",
  "direction": "kernel_verdict",
  "provider": "openai",
  "invocation_id": "call_weather_1",
  "verdict": "allow",
  "receipt_id": "rcpt_openai_basic_single_tool_call_allow",
  "payload": {
    "invocation": {
      "provider": "open_ai",
      "tool_name": "get_weather",
      "arguments": {
        "location": "San Francisco, CA",
        "unit": "celsius"
      },
      "provenance": {
        "provider": "open_ai",
        "request_id": "call_weather_1",
        "api_version": "responses.2026-04-25",
        "principal": {
          "kind": "open_ai_org",
          "org_id": "org_chio_demo"
        },
        "received_at": "2026-04-25T00:00:01.150Z"
      }
    }
  }
}

Interpret a passing result

ObservationConclusion
Tool, arguments and verdict agree
The eight selected captures contain the same normalized request and recorded decision.
Policy and scenario identifiers agree
The comparison attached the same configured labels to each output record. These labels are not independently recovered from signed policy evidence.
Provider provenance differs
The records retain their provider, request, principal, API version and capture time; those fields are excluded from equality.
Comparison exits successfully
The configured checks passed. This does not establish live provider compatibility, arbitrary HushSpec enforcement, tool execution or signed-receipt authenticity.

See where the paths converge

Two fixture paths converge on one record comparison
Two fixture paths converge on one record comparisonThree provider captures pass adapter replay checks. Five other captures are read directly. All eight recorded invocations and verdicts are checked against the fixture contract, then compared after removing provenance. The command executes no tool and signs no new receipt.Three provider capturesOpenAI · Anthropic · BedrockProvider-shaped exchangesFive provider capturesGemini · Mistral · GroqOllama · CohereAdapter replay checksLift recorded tool requestsCheck result loweringReadEight comparison recordsTool · arguments · recorded verdictCheck the fixture contractOmit provenanceCompare canonical bytesSame selected fields across recordsOffline comparison: no tool execution or new kernel signature.Two fixture paths converge on one record comparisonThree provider captures pass adapter replay checks. Five other captures are read directly. All eight recorded invocations and verdicts are checked against the fixture contract, then compared after removing provenance. The command executes no tool and signs no new receipt.Three provider capturesOpenAI · AnthropicBedrockAdapter replay checksLift requestsCheck result loweringFive other capturesGemini · Mistral · GroqOllama · CohereRead recorded verdictsEight recordsTool · arguments · verdictCheck the fixture contractOmitprovenanceCompare bytesCanonical JSONSame selected fieldsNo tool execution ornew kernel signature.
Eight captures converge on a comparison of selected recorded fields. Adapter replay adds translation checks for three providers; the command performs no fresh kernel evaluation.

Extend the check you actually need

Add captured cases to examine different argument shapes or provider translations. A denial case needs a captured denial and the matching expected contract. To test a new runtime guard, send the request through a configured kernel in your application and inspect its actual decision.

Adding an egress rule to this file does not test egress enforcement. A control run with an extra blocking egress rule still passes because the example’s parser ignores that section. Use Write a Policy for an executed policy edit and receipt verification for signed evidence.

Keep the comparison output

Save the complete output · cross-provider-policybash
cargo run --locked --quiet -- \
  --dry-run > comparison.out
cat comparison.out

Each record includes provider provenance, followed by a provenance.provider line. The complete output is useful when a change needs inspection; its fixture identifiers should not be passed to a kernel receipt verifier.

Inspect the verifier implementation

The source checks each record against the expected tool, arguments and verdict, then compares canonical bytes. These details support maintenance of the verifier after the complete run above works.

Contract checks and canonical comparison source
enforce_policy · complete functionrust
fn enforce_policy(policy: &DemoPolicy, receipt: &DemoReceipt) -> Result<(), DemoError> {
    let contract = &policy.rules.fixture_contract;
    if receipt.body.tool_name != contract.required_tool {
        return Err(DemoError::ToolMismatch {
            fixture_id: receipt.receipt_id.clone(),
            expected: contract.required_tool.clone(),
            actual: receipt.body.tool_name.clone(),
        });
    }
    if receipt.body.arguments != contract.required_arguments {
        return Err(DemoError::ArgumentsMismatch {
            fixture_id: receipt.receipt_id.clone(),
        });
    }
    if receipt.body.verdict.verdict != contract.expected_verdict {
        return Err(DemoError::VerdictMismatch {
            fixture_id: receipt.receipt_id.clone(),
            expected: contract.expected_verdict,
            actual: receipt.body.verdict.verdict,
        });
    }
    Ok(())
}
body_without_provenance · complete functionrust
fn body_without_provenance(body: &ReceiptBody) -> ReceiptBodyWithoutProvenance {
    ReceiptBodyWithoutProvenance {
        policy_id: body.policy_id.clone(),
        scenario_id: body.scenario_id.clone(),
        tool_name: body.tool_name.clone(),
        arguments: body.arguments.clone(),
        verdict: body.verdict.clone(),
    }
}
assert_receipt_equivalence · complete functionrust
fn assert_receipt_equivalence(receipts: &[DemoReceipt]) -> Result<(), DemoError> {
    let Some(first) = receipts.first() else {
        return Ok(());
    };

    let first_body = body_without_provenance(&first.body);
    let first_body_bytes = canonical_json_bytes_for("first receipt body", &first_body)?;
    let first_verdict_bytes =
        canonical_json_bytes_for("first normalized verdict", &first.body.verdict)?;

    for receipt in receipts.iter().skip(1) {
        let body = body_without_provenance(&receipt.body);
        let body_bytes = canonical_json_bytes_for("normalized receipt body", &body)?;
        assert_canonical_bytes_eq(
            "receipt body excluding provenance",
            &first_body_bytes,
            &body_bytes,
        )?;

        let verdict_bytes = canonical_json_bytes_for("normalized verdict", &receipt.body.verdict)?;
        assert_canonical_bytes_eq("normalized verdict", &first_verdict_bytes, &verdict_bytes)?;
    }

    Ok(())
}
Check the required flag and the output shape

Without --dry-run the command refuses to start, so a mistyped invocation can never reach a provider:

Terminal · cross-provider-policybash
cargo run --locked --quiet
exit 1
--dry-run is required; this example never calls live provider APIs

The complete run prints one record and one provenance line per provider, then the summary:

Terminal · cross-provider-policybash
cargo run --locked --quiet -- --dry-run > out.txt
wc -l < out.txt
grep -c '^provenance.provider' out.txt
tail -1 out.txt
Output
227
8
cross-provider verdict equality: 8 receipts validated for policy cross-provider-policy-demo

The first record and its provenance line, the first 29 lines of out.txt:

out.txt · lines 1 to 29json
{
  "receipt_id": "rcpt_openai_basic_single_tool_call_allow",
  "body": {
    "policy_id": "cross-provider-policy-demo",
    "scenario_id": "weather_lookup_allow",
    "tool_name": "get_weather",
    "arguments": {
      "location": "San Francisco, CA",
      "unit": "celsius"
    },
    "verdict": {
      "verdict": "allow",
      "reason": null,
      "redactions": []
    },
    "provenance": {
      "provider": "open_ai",
      "request_id": "call_weather_1",
      "api_version": "responses.2026-04-25",
      "principal": {
        "kind": "open_ai_org",
        "org_id": "org_chio_demo"
      },
      "received_at": "2026-04-25T00:00:01.150Z"
    }
  }
}
provenance.provider: "open_ai"
{

Continue with your governed application

Use the notes application with a provider SDK to put real tool dispatch behind Chio. For Rust, the OpenAI integration includes kernel boot, registration and capability issuance. Keep the provider loop and the governed operation connected when changing models.