Chio/Docs
LOGIN · JOIN

PlatformThe Guard Model

Kernel

Default Pipeline

The kernel's default 7-guard pipeline runs inexpensive checks first and denies a request when any guard denies it.


Registration order

The default guard pipeline: forbidden-path, shell-command, egress-allowlist, path-allowlist, mcp-tool, secret-leak, patch-integrity, evaluated in that ordercall1. ForbiddenPathGuard answers to the runtime name forbidden-pathforbidden-pathForbiddenPathGuard2. ShellCommandGuard answers to the runtime name shell-commandshell-commandShellCommandGuard3. EgressAllowlistGuard answers to the runtime name egress-allowlistegress-allowlistEgressAllowlistGuard4. PathAllowlistGuard answers to the runtime name path-allowlistpath-allowlistPathAllowlistGuard5. McpToolGuard answers to the runtime name mcp-toolmcp-toolMcpToolGuard6. SecretLeakGuard answers to the runtime name secret-leaksecret-leakSecretLeakGuard7. PatchIntegrityGuard answers to the runtime name patch-integritypatch-integrityPatchIntegrityGuardfirst Deny ends evaluation; an Err is a Deny7 guards, registered in this order by GuardPipeline::default_pipeline(); Default::default() is empty
The 7 guards GuardPipeline::default_pipeline() registers, in registration order. Post-invocation hooks, advisory guards, and operator-wired guards are not part of it.
sourcecrates/guards/chio-guards/src/pipeline.rs:39-49at fe56570

From crates/guards/chio-guards/src/pipeline.rs:

crates/guards/chio-guards/src/pipeline.rsrust
/// Create a default pipeline with all implemented guards using their
/// default configurations.
pub fn default_pipeline() -> Self {
    let mut pipeline = Self::new();
    pipeline.add(Box::new(crate::ForbiddenPathGuard::new()));
    pipeline.add(Box::new(crate::ShellCommandGuard::new()));
    pipeline.add(Box::new(crate::EgressAllowlistGuard::new()));
    pipeline.add(Box::new(crate::PathAllowlistGuard::new()));
    pipeline.add(Box::new(crate::McpToolGuard::new()));
    pipeline.add(Box::new(crate::SecretLeakGuard::new()));
    pipeline.add(Box::new(crate::PatchIntegrityGuard::new()));
    pipeline
}

Each guard answers to a runtime name, and that name is what a denial reason, a metric label, and a GuardEvidence row all carry. Both columns below are read out of the source; the third is the reason the guard sits at that position.

#GuardRuntime namePurpose
1ForbiddenPathGuardforbidden-pathGlob match against forbidden filesystem patterns. Cheap, so it runs first and its denials short-circuit the rest.
2ShellCommandGuardshell-commandRegex match against forbidden shell-command patterns extracted from the request arguments.
3EgressAllowlistGuardegress-allowlistDomain allowlist for outbound network requests. Wildcard host patterns; the default action is block.
4PathAllowlistGuardpath-allowlistAllowlist for filesystem read, write, and patch operations. An empty allowlist means no filesystem access.
5McpToolGuardmcp-toolPer-tool access control. Allow and block lists for tool names, plus a maximum argument size and a require-confirmation list.
6SecretLeakGuardsecret-leakPattern match over tool arguments for known secret formats: AWS keys, GitHub tokens, private keys, and generic patterns.
7PatchIntegrityGuardpatch-integrityValidates patches against maximum additions, maximum deletions, forbidden content patterns, and addition-to-deletion balance.

Two defaults, not one

default_pipeline() registers the stateless guards above. default_runtime_guard_profile() (below) is a separate default that adds internal-network, agent-velocity, and the response sanitizer. Guards outside both ( velocity, jailbreak, prompt-injection, data-flow, and the external adapters) are explicit opt-ins because they have ordering, dependency, or cost characteristics that operators should choose deliberately.

Ordering rationale

Conjunctive combination means ordering does not change correctness, but it does change cost. Cheap stateless checks are at the top so the common-case denial finishes work before any guard touches a regex compile, an argument-tree walk, or a patch parser.

  • Forbidden path is first because a glob match on a small list is among the cheapest checks the catalog has.
  • Tool access (mcp-tool) runs mid-pipeline. Requests that pass path checks but fail the tool allowlist are less common, so this order optimizes the expected denial distribution.
  • Patch integrity is last because patch parsing is the most expensive check in the default. By the time it runs, the cheap denials are already gone.

The runtime guard profile

default_pipeline() is not the only default. A second builder, default_runtime_guard_profile(), returns a RuntimeGuardProfile the standard runtime installs. It is a different guard set from default_pipeline(), and neither is a superset of the other.

crates/guards/chio-guards/src/lib.rsrust
pub fn default_runtime_guard_profile() -> RuntimeGuardProfile {
    let mut post_invocation_pipeline = PostInvocationPipeline::new();
    post_invocation_pipeline.add(Box::new(SanitizerHook::new()));

    RuntimeGuardProfile {
        pre_invocation_guards: vec![
            Box::new(InternalNetworkGuard::new()),
            Box::new(AgentVelocityGuard::new(AgentVelocityConfig::default())),
            Box::new(AdvisoryPipeline::new(PromotionPolicy::new())),
        ],
        post_invocation_pipeline,
    }
}

Three guards operators might expect to wire in by hand are already installed by this profile: InternalNetworkGuard (SSRF prevention), AgentVelocityGuard (cross-capability rate limiting), and ResponseSanitizationGuard through the post-invocation SanitizerHook. The AdvisoryPipeline is installed too, but empty: the session-journal advisory guards that would populate it stay operator-wired.

What the spec table says installs itself

spec/GUARDS.md carries an implementation-status table whose last column answers one question per guard: does the standard runtime builder install it, or must an operator wire it. The table below is that table, plus one resolved column. The spec names a crate per row; the fourth column is the crate whose source actually defines a type of that name, searched from the named crate outward, so a row that names the wrong crate shows it here rather than reading as agreement.

GuardCrate, per the specImplementationDefined inDefault control-plane profile
InternalNetworkGuardchio-guardsFullchio-guardsInstalled by default runtime profile
AgentVelocityGuardchio-guardsFullchio-guardsInstalled by default runtime profile
DataFlowGuardchio-guardsFullchio-guardsOperator-wired (requires `SessionJournal`)
BehavioralSequenceGuardchio-guardsFullchio-guardsOperator-wired (requires `SessionJournal`)
ResponseSanitizationGuardchio-guardsFullchio-guardsInstalled by default runtime profile through `SanitizerHook`
PostInvocationPipelinechio-guardsFullchio-kernelInstalled by default runtime profile
AdvisoryPipelinechio-guardsFullchio-guardsInstalled by default runtime profile (session-journal advisory guards remain operator-wired)
AnomalyAdvisoryGuardchio-guardsFullchio-guardsOperator-wired (requires `SessionJournal`)
DataTransferAdvisoryGuardchio-guardsFullchio-guardsOperator-wired (requires `SessionJournal`)
PortableFilesystemRootsGuardchio-guardsFullNo type of this nameOperator-wired (filesystem-scoped)
WasmGuardchio-wasm-guardsFullchio-wasm-guardsOperator-wired
WasmGuardRuntimechio-wasm-guardsFullchio-wasm-guardsOperator-wired
SessionJournalchio-http-sessionFullchio-http-sessionOperator-wired

Two rows are worth reading twice. PostInvocationPipeline is listed under chio-guards and is defined in crates/kernel/chio-kernel/src/post_invocation.rs, which is where the post-invocation phase lives. PortableFilesystemRootsGuard has no type of that name in any crate the search covers, so the row promises a guard the source does not carry. Treat the spec table as the control-plane contract for the guards that do resolve, and this page's registration order as the shipped behavior.


What neither default installs

The full chio-guards catalog ships more than the two defaults cover. The guards below stay operator-wired even with the runtime profile enabled. Each row is a Kernel page.

GuardWhy opt-in
JailbreakGuard (heuristic + statistical + ML)ML inference cost and false-positive risk vary by deployment; operators should review thresholds before enabling.
PromptInjectionGuardPattern detection on incoming text. Useful but noisy; operators tune the signal set.
BehavioralProfileGuardReads receipt feed history; needs a configured feed source and baseline windows before it has anything to compare against.
DataFlowGuard, BehavioralSequenceGuardSession-aware: depend on a configured session journal. Not every edge maintains one.
VelocityGuardPer-window, per-grant invocation cap; operators should pick a window and limit, not inherit a default that may not match their workload.
External adapters (Bedrock, Azure, Vertex, Safe Browsing, VirusTotal, Snyk)Network dependency, credentials, latency cost; only enable per External Guards.
chio-data-guards (SqlQueryGuard, VectorDbGuard, WarehouseCostGuard, QueryResultGuard)A separate crate; neither default references it. Depends on a configured data-store connection and a table/column allowlist policy. QueryResultGuard is a post-invocation hook over returned rows.
AnomalyAdvisoryGuard, DataTransferAdvisoryGuardThe runtime profile installs an empty AdvisoryPipeline shell; these session-journal advisory guards, and any promotion rules, stay operator-wired.
CUA guards (computer_use, browser_automation, remote_desktop, input_injection, embedding_anomaly)Only relevant for deployments that mediate computer use; off by default elsewhere.
code_execution, content_review, memory_governanceDomain-specific; operators wire them in only where the corresponding tool interface exists.
WASM custom guardsLoaded from operator-authored modules at start-up; the kernel does not ship with default WASM guards baked in.

Extending the default

Start from the default and append. Anything you add runs after the default guards, which is the right place for guards that are more expensive or that depend on session state.

rust
use chio_guards::{
    GuardPipeline, AgentVelocityGuard, AgentVelocityConfig,
    DataFlowGuard, DataFlowConfig,
};
use chio_guards::response_sanitization::ResponseSanitizationGuard;

let mut pipeline = GuardPipeline::default_pipeline();

// AgentVelocityGuard keeps its own in-memory rate buckets.
pipeline.add(Box::new(AgentVelocityGuard::new(
    AgentVelocityConfig::default(),
)));
// DataFlowGuard is session-aware and needs a journal handle.
pipeline.add(Box::new(DataFlowGuard::new(
    journal.clone(),
    DataFlowConfig::default(),
)));

// Custom guards last.
pipeline.add(Box::new(BusinessHoursGuard::new(9, 17)));

kernel.add_guard(Box::new(pipeline));

Authoring custom guards is covered in Custom Guards. For the response-side phase, register a PostInvocationPipeline separately on the kernel; see Sanitization.


Opting out

Build the pipeline by hand to omit one or more of the default guards. The kernel accepts a manually registered pipeline.

rust
use chio_guards::{
    GuardPipeline, ForbiddenPathGuard, PathAllowlistGuard,
    McpToolGuard, SecretLeakGuard, PatchIntegrityGuard,
};

// A pipeline without ShellCommandGuard or EgressAllowlistGuard,
// for a deployment that has no shell or network surface to begin with.
let mut pipeline = GuardPipeline::new();
pipeline.add(Box::new(ForbiddenPathGuard::new()));
pipeline.add(Box::new(PathAllowlistGuard::new()));
pipeline.add(Box::new(McpToolGuard::new()));
pipeline.add(Box::new(SecretLeakGuard::new()));
pipeline.add(Box::new(PatchIntegrityGuard::new()));

kernel.add_guard(Box::new(pipeline));

Removing a guard removes the protection

Each default guard exists because its absence opens a class of attack. Confirm the corresponding interface is closed by another control (network sandbox, immutable filesystem, no shell tool) before omitting the guard.

Catalog pages by category


Where to go next