Shared integration library that chio plugins import to talk to the chio protocol runtime. Wraps @chio-protocol/sdk (MCP edge + trust plane) and the local chio CLI.
This is plugin-author infrastructure, not an end-user tool.
npm install @chio/bridge
# peer: @chio-protocol/sdk ^1.0.0 (bundled as dep here)Node >= 22 (ESM only).
import { ChioBridge } from "@chio/bridge";
// Daemon mode — hits chio's MCP edge (8931) and trust plane (8940)
const bridge = ChioBridge.fromDaemon({
mcpEdgeUrl: "http://127.0.0.1:8931", // default
trustUrl: "http://127.0.0.1:8940", // default
token: process.env.CHIO_SERVICE_TOKEN!,
});
// CLI mode — shells out to the local `chio` binary. No daemon required.
const bridge = ChioBridge.fromCli({ chioBinary: "chio" });Arc upstream renamed every runtime identifier to chio. Downstream consumers must update in lockstep:
| Before (Wave 4.x) | After (Wave 5.0) |
|---|---|
CHIO_ARC_BIN env var |
CHIO_BIN |
chioBinary was arcBinary on CliOptions |
chioBinary |
ArcClient, ArcSession, ArcReceipt re-exports |
ChioClient, ChioSession, ChioReceipt |
ArcCli class (rarely used directly) |
ChioCli |
@arc-protocol/sdk dep |
@chio-protocol/sdk |
did:arc:<hex> subject DIDs |
did:chio:<hex> |
default binary fallback "arc" on PATH |
"chio" on PATH |
The HushSpec hushspec: schema identity is unchanged (that's the policy schema version, not an arc identifier).
The bridge prefers chio over arc. ChioCli resolves the runtime binary in this order:
- Explicit
chioBinaryoption (highest precedence). CHIO_BINenv var (set bychio-test-harness/bin/env.sh).- If a legacy
CHIO_ARC_BIN/ARC_BINpath is set and a siblingchiobinary exists in the same directory (common forcargo build --release --bin chiolanding alongside the oldarcartifact), the bridge switches to that sibling automatically. chioon$PATH.- Legacy
CHIO_ARC_BIN/ARC_BIN— fallback for pre-rename deployments wherechiohas not been built yet. - Plain
arcon$PATH(final fallback).
Set CHIO_BIN explicitly in CI to pin the binary. Legacy CHIO_ARC_BIN is honored only when CHIO_BIN is not set.
bond(opts)— load policy, validate, issue passport.check(call)— mediate a tool call. Returns aVerdict; verdict may include a signedChioReceipt.receipts(opts)/receiptStream(opts)— query / long-poll the trust plane.verifyReceipt(r)— real ed25519 verification via@chio-protocol/sdk/invariants.issueCapability(input)/attenuate(id, delta)— trust plane REST. See "Attenuation semantics" below for the issue-then-revoke fallback.revokeAllForSubject(did)— kill every active passport in the lifecycle registry whose subject matchesdid. Returns{ revokedPassportIds, failed }.createPassport(opts)/verifyPassport(did)—did:chio:*via trust plane or CLI.loadPolicy(path)/lintPolicy(policyOrPath)— real HushSpec schema validation.discoverMcpServers()/wrapMcp(cmd, options)— MCP mesh.wrapMcpforwards--policy,--server-id,--auth-token, and--listento the realchio mcp serve-httpflag surface and returns the resolved URL + auth token + server id.policyis required (matching chio's own Usage signature).serverIddefaults to a deterministicchio-wrap-<sha256(cmd[0])[0..16]>so idempotent re-wraps of the same binary produce the same id.verifyPassport(input)—inputmay be a bare DID string (daemon mode: trust-plane lifecycle lookup),{ did }/{ passportId }for explicit forms, or{ file: "/path/to/passport.json" }for CLI-only verification viachio passport verify --input <file>. The old single-argverifyPassport(did)form continues to work against daemon mode; CLI-only callers must switch to the{ file }form becausechio passport verify --inputexpects a path, not a DID.
HushSpec 0.1.0 is the only supported version. The rule set is closed:
forbidden_paths | path_allowlist | egress | secret_patterns | patch_integrity
shell_commands | tool_access | computer_use | remote_desktop_channels | input_injection
velocity and human_in_loop are not first-class rule keys. Put them under
extensions.chio.* — the linter suggests this path.
import { DEFAULT_MCP_EDGE_URL, DEFAULT_TRUST_URL, HUSHSPEC_SUPPORTED_VERSION } from "@chio/bridge";The chio trust plane on this build has no /v1/capabilities/<id>/attenuate endpoint. (We grepped arc/crates/chio-cli/src/trust_control/ for attenuat: only the validate_attenuation core type machinery exists, never bound to an HTTP handler.) bridge.attenuate(capabilityId, delta) therefore implements attenuation as issue-narrower-then-revoke-old:
POST /v1/capabilities/issuewith the narrower scope/budget, yielding a freshIssuedCapabilityToken(new id, new subject key).POST /v1/revocationswith the old capability id.
Best-effort atomicity: if step 2 fails after step 1 succeeds, the bridge throws ChioBridgeError("attenuation_partial", ...) carrying the new token in cause so the operator can retry or hand-revoke.
AttenuationDelta.subjectPublicKey lets callers reuse an existing key on the narrower capability; when omitted, the bridge generates a fresh ed25519 keypair via node:crypto and surfaces both halves on the returned IssuedCapabilityToken (subjectPublicKey, subjectPrivateKeyHex).
bridge.issueCapability({ subject?, subjectPublicKey?, scope, ttl?, ttlSeconds?, runtimeAttestation? }) mirrors the trust plane DTO IssueCapabilityRequest { subjectPublicKey, scope, ttlSeconds } (camelCase, see arc/crates/chio-cli/src/trust_control/service_types.rs:691). Resolution order for subjectPublicKey:
- Explicit
subjectPublicKeyargument (preferred). - Hex suffix of
subjectwhen it matchesdid:chio:<64-hex>. - A fresh ed25519 keypair generated via
node:crypto— bothsubjectPublicKeyandsubjectPrivateKeyHexare returned on theIssuedCapabilityTokenso the caller can sign downstream presentations.
ChioClient, ChioSession, ReceiptQueryClient and types ChioReceipt, CapabilityToken are re-exported for plugin convenience — one import surface.
Workflow: .github/workflows/ci.yml. Runs lint/typecheck (non-blocking in Wave 5.1), unit tests, and a chio-backed smoke pass. Swap owner/... once the GitHub org is live.