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
crates/guards/chio-guards/src/pipeline.rs:39-49at fe56570From crates/guards/chio-guards/src/pipeline.rs:
/// 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.
| # | Guard | Runtime name | Purpose |
|---|---|---|---|
| 1 | ForbiddenPathGuard | forbidden-path | Glob match against forbidden filesystem patterns. Cheap, so it runs first and its denials short-circuit the rest. |
| 2 | ShellCommandGuard | shell-command | Regex match against forbidden shell-command patterns extracted from the request arguments. |
| 3 | EgressAllowlistGuard | egress-allowlist | Domain allowlist for outbound network requests. Wildcard host patterns; the default action is block. |
| 4 | PathAllowlistGuard | path-allowlist | Allowlist for filesystem read, write, and patch operations. An empty allowlist means no filesystem access. |
| 5 | McpToolGuard | mcp-tool | Per-tool access control. Allow and block lists for tool names, plus a maximum argument size and a require-confirmation list. |
| 6 | SecretLeakGuard | secret-leak | Pattern match over tool arguments for known secret formats: AWS keys, GitHub tokens, private keys, and generic patterns. |
| 7 | PatchIntegrityGuard | patch-integrity | Validates 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.
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.
| Guard | Crate, per the spec | Implementation | Defined in | Default control-plane profile |
|---|---|---|---|---|
InternalNetworkGuard | chio-guards | Full | chio-guards | Installed by default runtime profile |
AgentVelocityGuard | chio-guards | Full | chio-guards | Installed by default runtime profile |
DataFlowGuard | chio-guards | Full | chio-guards | Operator-wired (requires `SessionJournal`) |
BehavioralSequenceGuard | chio-guards | Full | chio-guards | Operator-wired (requires `SessionJournal`) |
ResponseSanitizationGuard | chio-guards | Full | chio-guards | Installed by default runtime profile through `SanitizerHook` |
PostInvocationPipeline | chio-guards | Full | chio-kernel | Installed by default runtime profile |
AdvisoryPipeline | chio-guards | Full | chio-guards | Installed by default runtime profile (session-journal advisory guards remain operator-wired) |
AnomalyAdvisoryGuard | chio-guards | Full | chio-guards | Operator-wired (requires `SessionJournal`) |
DataTransferAdvisoryGuard | chio-guards | Full | chio-guards | Operator-wired (requires `SessionJournal`) |
PortableFilesystemRootsGuard | chio-guards | Full | No type of this name | Operator-wired (filesystem-scoped) |
WasmGuard | chio-wasm-guards | Full | chio-wasm-guards | Operator-wired |
WasmGuardRuntime | chio-wasm-guards | Full | chio-wasm-guards | Operator-wired |
SessionJournal | chio-http-session | Full | chio-http-session | Operator-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.
| Guard | Why opt-in |
|---|---|
JailbreakGuard (heuristic + statistical + ML) | ML inference cost and false-positive risk vary by deployment; operators should review thresholds before enabling. |
PromptInjectionGuard | Pattern detection on incoming text. Useful but noisy; operators tune the signal set. |
BehavioralProfileGuard | Reads receipt feed history; needs a configured feed source and baseline windows before it has anything to compare against. |
DataFlowGuard, BehavioralSequenceGuard | Session-aware: depend on a configured session journal. Not every edge maintains one. |
VelocityGuard | Per-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, DataTransferAdvisoryGuard | The 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_governance | Domain-specific; operators wire them in only where the corresponding tool interface exists. |
| WASM custom guards | Loaded 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.
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.
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
Catalog pages by category
- Filesystem ·
ForbiddenPathGuard,PathAllowlistGuard - Network ·
EgressAllowlistGuard,InternalNetworkGuard - Shell & Code ·
ShellCommandGuard,PatchIntegrityGuard, code-execution guards - Rate Limit ·
VelocityGuard,AgentVelocityGuard - Session-aware Guards ·
DataFlowGuard,BehavioralSequenceGuard - Jailbreak Detection ·
JailbreakGuard,PromptInjectionGuard - Sanitization ·
SanitizerHook,ResponseSanitizationGuard - Computer Use · CUA-mediation guards
- Data Layer ·
SqlQueryGuardandchio-data-guards - External Adapters · cloud guardrails and threat-intel
- Advisory Signals ·
AdvisoryPipelineandPromotionPolicy - WASM Guards · custom guards loaded from
.arcguardcomponents
Where to go next
- The Guard Trait · the contract every guard implements
- Pipelines & Composition · the surrounding combination rules
- Custom Guards · author your own
- Write a Policy · configure the default guards through HushSpec