Chio/Docs

Portable Kernel

Chio's trust kernel is not a server. It is a pure, portable, verdict-producing library that compiles to three shapes: a full server-side sidecar for data centers, a mobile binding for iOS and Android, and a WebAssembly module for the browser. All three share the same compiled decision logic, the same capability rules, the same receipt format, the same signature scheme. The only things that change between targets are entropy source, clock source, and how emitted receipts find their way into storage.

Why this matters

Agents do not only live in data centers anymore. They run on a phone while the user is offline, in a browser tab at the edge of a CDN, inside a native desktop app. A trust kernel that only works behind a sidecar on the cloud side of the network cannot protect those agents. The portable kernel is the answer: identical verdicts everywhere, with platform-appropriate glue.

One Core, Three Targets

The core is chio-kernel-core: a no_std + alloc Rust crate with no async runtime, no HTTP client, no database. It depends only on cryptography (ed25519-dalek, sha2) and serialization (serde). All verdict logic — capability verification, scope matching, DPoP binding, guard evaluation, receipt canonicalisation and signing, Merkle checkpoint construction — lives here and nowhere else.

Three thin adapter crates wrap the core for each deployment shape:

  • chio-kernel · the production sidecar. Adds tokio, an HTTP server, a SQLite-backed receipt store, a price oracle, and a guard plugin interface. This is what you run in Kubernetes.
  • chio-kernel-mobile · UniFFI wrapper that exposes the core to Swift and Kotlin through a JSON-in, JSON-out FFI. Ships as an .xcframework for iOS and a .so plus Kotlin module for Android.
  • chio-kernel-browser · wasm-bindgen wrapper that compiles the core to WebAssembly for direct use from JavaScript and TypeScript. Ships as an npm package with typed bindings.
rendering…
The kernel is a single crate compiled against three adapter shells. Verdicts are identical across all three. Only the platform primitives (entropy, clock, receipt sink) are injected by the adapter.

Feature Parity

Verdict and receipt semantics are identical across all three targets. The differences are in the surrounding machinery, and they are deliberate.

FeatureSidecarMobileBrowser
Capability verificationYesYesYes
Scope matching and DPoP bindingYesYesYes
Signed receipts (Ed25519)YesYesYes
Guard pipeline (sync)YesYesYes
Portable passport verificationYesYesRoadmap
Merkle checkpoint constructionYesYesYes
Custom guards at runtimeYes (dyn Guard)Compile-time onlyCompile-time only
Async runtimeYes (tokio)NoNo
Persistent receipt storeSQLiteRing buffer + platform drainRing buffer + platform drain
HTTP fetch (price oracle, siem)YesHost suppliesHost supplies
Entropy sourceOS getrandomSecRandom / /dev/urandomWeb Crypto
Clock sourceSystemTimeCFAbsoluteTime / SystemClockInjectable (default Date.now)

Native (Sidecar)

This is the default deployment: run chio-kernel as a sidecar next to your agent, your Prefect worker, your Temporal activity worker, or your Next.js server. It holds the policy, signs receipts, persists them to SQLite or whatever durable store you plug in, and exposes an HTTP interface for callers. The Installation and Quick Start guides are written against this shape.


Mobile (iOS and Android)

The mobile binding is a thin FFI over the core. Four functions, all JSON-in and JSON-out: evaluate, signReceipt, verifyCapability, and verifyPassport. iOS consumers link an .xcframework; Android consumers link a shared library and a generated Kotlin module.

Swift

swift
import chio_kernel_mobile

// Evaluate a tool call locally — no network, no server round trip.
let request: [String: Any] = [
  "capability": cachedCapabilityJson,
  "trusted_issuers": [issuerHex],
  "request": [
    "request_id": UUID().uuidString,
    "tool_name": "read_medical_record",
    "server_id": "clinical-srv",
    "agent_id": agentPubHex,
    "arguments": ["patient_id": patientId],
  ],
  "now_secs": 0,  // 0 = use the on-device clock
]
let verdictJson = try evaluate(
  requestJson: JSONSerialization.string(from: request)
)

// On allow, sign a receipt and queue it for later upload.
if verdict.verdict == "allow" {
  let receiptJson = try signReceipt(
    bodyJson: canonicalReceiptBody,
    signingSeedHex: deviceSigningSeedHex
  )
  receiptQueue.enqueue(receiptJson)
}

Kotlin

kotlin
import uniffi.chio_kernel_mobile.*

val verdictJson = evaluate(
    requestJson = buildString {
        append("""{"capability": """)
        append(cachedCapabilityJson)
        append(""", "trusted_issuers": [""")
        append(""$issuerHex"")
        append("""], "request": {"request_id": "$requestId", """)
        append(""""tool_name": "read_medical_record", """)
        append(""""server_id": "clinical-srv", """)
        append(""""agent_id": "$agentPubHex", """)
        append(""""arguments": $argsJson}, "now_secs": 0}""")
    }
)

val receipt = signReceipt(
    bodyJson = canonicalReceiptBody,
    signingSeedHex = deviceSigningSeedHex
)

Offline-first is the point

Mobile agents spend most of their time partly or fully disconnected. The portable kernel is designed for that: cache a capability to the Keychain or the Android KeyStore, evaluate locally while offline, queue signed receipts, and drain the queue on reconnect. The upstream kernel then verifies every queued receipt's signature against the same canonical form, so offline decisions remain fully auditable.

Browser (WebAssembly)

The browser binding compiles the core to WebAssembly and exposes the same four functions as small, typed JavaScript exports. Entropy comes from the Web Crypto API; clock from Date.now (with an optional override for deterministic tests). The compiled artifact is 552 KB uncompressed and drops to roughly 45 KB after wasm-opt, with <5 ms evaluation latency in Chrome.

Install

bash
npm install chio-kernel-browser

Use

typescript
import init, {
  evaluate,
  sign_receipt,
  verify_capability,
  mint_signing_seed_hex,
} from "chio-kernel-browser";

// Load the wasm module once at app start.
await init();

const verdictJson = evaluate(JSON.stringify({
  request: {
    request_id: crypto.randomUUID(),
    tool_name: "read_medical_record",
    server_id: "clinical-srv",
    agent_id: agentPubHex,
    arguments: { patient_id: patientId },
  },
  capability: cachedCapability,
  trusted_issuers_hex: [issuerHex],
  // clock_override_unix_secs: 1_717_200_000,  // optional, for tests
}));

const verdict = JSON.parse(verdictJson) as EvaluationVerdict;
if (verdict.verdict !== "allow") {
  throw new PermissionError(verdict.reason);
}

const seedHex = mint_signing_seed_hex();
const receiptJson = sign_receipt(
  JSON.stringify(receiptBody),
  seedHex,
);

Integration in Next.js

The module loads fine in both the Node and Edge runtimes. A common shape is a single server-side gating helper, shared by the app routes that need it:

typescript
// lib/chio-gate.ts
import init, { evaluate } from "chio-kernel-browser";

let ready: Promise<unknown> | null = null;
function ensure() {
  return (ready ??= init());
}

export async function gate(request: EvaluateRequest) {
  await ensure();
  const verdict = JSON.parse(evaluate(JSON.stringify(request)));
  if (verdict.verdict !== "allow") {
    throw Object.assign(new Error(verdict.reason), { code: "chio_deny" });
  }
  return verdict;
}

Design Decisions

  • JSON at the FFI boundary. Rather than project the entire Chio type graph into UDL or TypeScript, the mobile and browser bindings speak JSON. The FFI surface stays small (four functions), the type graph stays versionable, and language binding impedance is avoided. This is the same tradeoff gRPC and protobuf make against native struct marshalling.
  • Fail-closed entropy. If a platform's RNG cannot produce high-quality randomness at signing time, the kernel refuses with a weak_entropy error rather than producing a signature over predictable bytes. Silent degradation is the worst possible outcome for a trust kernel; we would rather fail loudly.
  • Ring-buffer receipts, platform drains. The core buffers emitted receipts in memory. Platform adapters call drain_receipts on a cadence they choose and persist to whatever storage they have (Keychain-backed SQLite on iOS, Room on Android, IndexedDB in the browser). The core stays deterministic and ignorant of storage.
  • No custom guards at runtime on mobile or browser. Guards are compiled into the adapter library at build time. Runtime guard registration requires a plugin loader that mobile and WASM cannot practically support. Most mobile and browser deployments need only the built-in guards; custom guards still run server-side on the sidecar for more complex policies.
  • Same receipt format, everywhere. A receipt signed offline on a phone three days ago verifies against the same canonicalisation rules and the same signature scheme as one produced this second by a server in a data center. There is no "mobile receipt format" or "browser receipt format".

Status and Roadmap

CrateStatusDistribution
chio-kernel-coreProduction · CI-gated for no_std and wasm32Internal crate, not separately versioned
chio-kernel (sidecar)ProductionContainer image, Homebrew, release binaries
chio-kernel-mobileAlpha · iOS device and simulator builds verified; Android builds verified on configured NDK.xcframework and .aar produced; CocoaPods and Maven Central not yet published
chio-kernel-browserAlpha · headless Chrome qualification passing; 552 KB / <5 ms evaluatewasm-pack output ready; not yet published to npm

Alpha contract

Mobile and browser bindings are pre-1.0. The JSON wire shape is stable across patch releases but may tighten between minor versions as the Chio spec evolves. Pin by minor, not patch, until the first GA release.

Next Steps

  • Architecture · how the kernel fits into the broader Chio deployment topology
  • Capabilities · the token format the portable kernel verifies on every call
  • Receipts · the canonical form the kernel signs, identical across all three targets
  • Trust Model · what it means for a verdict to be identical across server, mobile, and browser
Portable Kernel · Chio Docs