Chio/Docs
LOGIN · JOIN

Policy Schema Reference

Look up a HushSpec field and its default, how documents inherit and merge, and which guard or scope constraint each block compiles to.

Reference chio@5f63e6e

On this page

The chio-policy crate compiles one HushSpec policy, a YAML document, into the kernel's guard pipeline, a post-invocation pipeline, and the default capability scope.

Each rule block governs one kind of action: which tools an agent can use, where it can connect, what it can change, and when a person must approve. Each field has a default, and each block compiles to a named guard, a scope constraint, or both. Write a Policy builds a first policy and runs it.

Synopsis

A document names its schema version and, optionally, a base to extend, rule blocks, extensions, and governance metadata. The version line alone is a valid document: it parses, validation reports no error, and the validator warns that no rules section is present.

policy.yamlyaml
hushspec: "0.1.0"
FieldTypeRequiredHolds
hushspec
String
yes
Schema version. The accepted value is 0.1.0.
name
Option<String>
no
Policy name. A merged document takes the base's name when the child has none.
description
Option<String>
no
Free text. A merged document takes the base's description when the child has none.
extends
Option<String>
no
Path of the base document, resolved as described under Inheritance.
merge_strategy
Option<MergeStrategy>
no
replace, merge, or deep_merge. Default deep_merge.
rules
Option<Rules>
no
The rule blocks. Each block is optional.
extensions
Option<Extensions>
no
posture, origins, detection, reputation, runtime_assurance, and chio.
metadata
Option<GovernanceMetadata>
no
Governance metadata. Every field is optional.

merge_strategy sets how the child composes with its base. The schema version is the HUSHSPEC_VERSION constant, 0.1.0; is_supported accepts only the entries of HUSHSPEC_SUPPORTED_VERSIONS, which lists that version alone.

Parsing and validation

HushSpec::parse reads YAML through serde. Before the parser runs, it rejects a document that does not start with a mapping, a document with an unterminated double-quoted scalar, and a document with a scalar whitespace run the YAML backend cannot join. A parser panic becomes an error. Every struct in the schema, from HushSpec down to each rule block and extension block, rejects an unknown key.

validate returns errors and warnings. The error kinds are UnsupportedVersion, DuplicatePatternName, InvalidRegex, and Custom, whose message names the field. A document with no rules key warns that no rules section is present, and a rules block with no block configured warns that no rules are configured. compile_policy validates first and answers a document that has errors with CompileError::Invalid, whose message joins them.

Enumerations

Every enum serializes its variants in snake case. A value marked default is the one serde supplies when the key is omitted. The other enums have no default; the owning table says whether their key is required.

TypeValuesUsed by
MergeStrategy
replace, merge, deep_merge (default)
merge_strategy
DefaultAction
allow, block
egress.default, tool_access.default
Severity
critical, error, warn
secret_patterns.patterns[].severity
ComputerUseMode
observe, guardrail (default), fail_closed
computer_use.mode
HumanInLoopTimeoutAction
deny (default), defer
human_in_loop.on_timeout
RuntimeAssuranceTier
none, basic, attested, verified
tool_access.require_runtime_assurance_tier, tool_access.prefer_runtime_assurance_tier, runtime_assurance tiers and verifiers
WorkloadIdentityScheme
spiffe
WorkloadIdentityMatch.scheme
WorkloadCredentialKind
uri, x509_svid, jwt_svid
WorkloadIdentityMatch.credential_kinds
TransitionTrigger
user_approval, user_denial, critical_violation, any_violation, timeout, budget_exhausted, pattern_match
posture.transitions[].on
OriginDefaultBehavior
deny (default), minimal_profile
origins.default_behavior
DetectionLevel
safe, suspicious, high, critical
detection.prompt_injection.warn_at_or_above, detection.prompt_injection.block_at_or_above
Classification
public, internal, confidential, restricted
metadata.classification
LifecycleState
draft, review, approved, deployed, deprecated, archived
metadata.lifecycle_state

Rule blocks

rules holds 14 optional blocks, listed below in the order of RULE_BLOCK_NAMES. An omitted block adds no guard. A block with enabled: false adds no guard either, because the compiler checks the flag before it builds anything.

Types are the Rust types on the rule structs: Vec<String> is a YAML sequence, Option<T> a key that may be omitted, and an enum takes the values under Enumerations. Every rule field has a serde default, so a document may omit any of them. The default column shows that default: [] for a sequence, unset for an optional key, and the declared value otherwise.

forbidden_paths

Globs for file paths the agent may not touch, and globs that exempt a matched path. With patterns empty the compiled guard uses its built-in pattern set; otherwise the compiler passes patterns and exceptions to the guard, which rejects an invalid glob at compile time.

FieldTypeRequiredDefault
enabled
bool
no
true
patterns
Vec<String>
no
[]
exceptions
Vec<String>
no
[]
policy.yamlyaml
rules:
  forbidden_paths:
    patterns:
      - "**/.env"
      - "**/.env.*"
      - "**/.ssh/**"
    exceptions:
      - "**/.env.example"

path_allowlist

Restricts file access to the listed globs by operation: read for reads, write for writes, and patch for patches. The block is off unless the document sets enabled: true.

FieldTypeRequiredDefault
enabled
bool
no
false
read
Vec<String>
no
[]
write
Vec<String>
no
[]
patch
Vec<String>
no
[]
policy.yamlyaml
rules:
  path_allowlist:
    enabled: true
    read:
      - ./workspace/**
      - ./docs/**
    write:
      - ./workspace/output/**
    patch:
      - ./workspace/src/**

egress

Domain lists for outbound network access. The compiled guard receives allow and block; with both empty it uses its built-in allowlist. The policy evaluator applies default to a domain in neither list. Enabling this block also adds the internal-network guard, which denies RFC 1918 and cloud-metadata addresses.

FieldTypeRequiredDefault
enabled
bool
no
true
allow
Vec<String>
no
[]
block
Vec<String>
no
[]
default
DefaultAction
no
block
policy.yamlyaml
rules:
  egress:
    allow:
      - api.github.com
      - "*.npmjs.org"
    block: []
    default: block

secret_patterns

Regexes that identify secrets. The compiled guard checks outbound file writes for a match, and a post-invocation sanitizer redacts matches from a tool result before the agent reads it. The compiler carries each pattern's name and pattern to the guard and drops severity and description. Validation rejects a duplicate name and a pattern that does not compile.

FieldTypeRequiredDefault
enabled
bool
no
true
patterns
Vec<SecretPattern>
no
[]
skip_paths
Vec<String>
no
[]

Each entry of patterns is a SecretPattern:

FieldTypeRequiredHolds
name
String
yes
Identifier, unique within the list.
pattern
String
yes
A regular expression.
severity
Severity
yes
critical, error, or warn.
description
Option<String>
no
Free text.
policy.yamlyaml
rules:
  secret_patterns:
    patterns:
      - name: aws_access_key
        pattern: "AKIA[0-9A-Z]{16}"
        severity: critical
      - name: github_token
        pattern: "gh[ps]_[A-Za-z0-9]{36}"
        severity: critical
    skip_paths:
      - "**/tests/**"

patch_integrity

Bounds on a patch: at most max_additions added lines and max_deletions deleted lines, regexes the patch content may not contain, and, with require_balance, a cap on the ratio between additions and deletions. Every field reaches the compiled guard.

FieldTypeRequiredDefault
enabled
bool
no
true
max_additions
usize
no
1000
max_deletions
usize
no
500
forbidden_patterns
Vec<String>
no
[]
require_balance
bool
no
false
max_imbalance_ratio
f64
no
10.0
policy.yamlyaml
rules:
  patch_integrity:
    max_additions: 500
    max_deletions: 200
    require_balance: true
    max_imbalance_ratio: 5.0
    forbidden_patterns:
      - "(?i)eval\\s*\\("
      - "(?i)exec\\s*\\("

shell_commands

Regexes that deny a shell command. With no patterns the compiled guard uses its default forbidden set; with patterns the compiler passes them and turns on the guard's forbidden-path enforcement.

FieldTypeRequiredDefault
enabled
bool
no
true
forbidden_patterns
Vec<String>
no
[]
policy.yamlyaml
rules:
  shell_commands:
    forbidden_patterns:
      - "(?i)rm\\s+-rf\\s+/"
      - "curl.*\\|.*bash"
      - "wget.*\\|.*bash"

tool_access

Tool allow and block lists with a default action for a tool in neither. The compiled guard receives allow, block, default, and max_args_size. require_confirmation, require_runtime_assurance_tier, and max_args_size also become constraints on the default capability scope, described under Compilation. The compiler does not read prefer_runtime_assurance_tier, carries neither workload-identity field into a guard or a constraint, and compiles a document that sets either workload-identity field to an empty default scope. Validation requires a max_args_size of at least 1 and checks each workload-identity match: a trust_domain given must not be empty, and each path prefix must start with /.

FieldTypeRequiredDefault
enabled
bool
no
true
allow
Vec<String>
no
[]
block
Vec<String>
no
[]
require_confirmation
Vec<String>
no
[]
default
DefaultAction
no
allow
max_args_size
Option<usize>
no
unset
require_runtime_assurance_tier
Option<RuntimeAssuranceTier>
no
unset
prefer_runtime_assurance_tier
Option<RuntimeAssuranceTier>
no
unset
require_workload_identity
Option<WorkloadIdentityMatch>
no
unset
prefer_workload_identity
Option<WorkloadIdentityMatch>
no
unset

require_workload_identity and prefer_workload_identity take a WorkloadIdentityMatch: an object with optional scheme (spiffe), optional trust_domain, a path_prefixes list, and a credential_kinds list of uri, x509_svid, or jwt_svid. See Workload Identity for the normalized identity and Bind Workload Identity for the operational steps.

policy.yamlyaml
rules:
  tool_access:
    default: block
    allow:
      - read_file
      - list_directory
      - search
    block:
      - shell_exec
    require_confirmation:
      - write_file
    max_args_size: 65536
    require_runtime_assurance_tier: attested

computer_use

Gates computer-use actions by action type. mode selects the enforcement mode: observe allows every action, guardrail allows a listed action and allows an unlisted one with a warning, and fail_closed denies an unlisted action. allowed_actions is the action-type allowlist. The block is off unless the document sets enabled: true.

FieldTypeRequiredDefault
enabled
bool
no
false
mode
ComputerUseMode
no
guardrail
allowed_actions
Vec<String>
no
[]

remote_desktop_channels

Toggles for the side channels of a remote-desktop session. Each toggle sets the matching channel flag on the compiled guard. Session sharing, printing, and transfer size have no keys in this block and stay at the guard's defaults.

FieldTypeRequiredDefault
enabled
bool
no
false
clipboard
bool
no
false
file_transfer
bool
no
false
audio
bool
no
true
drive_mapping
bool
no
false

input_injection

Synthetic input during a computer-use session. allowed_types lists the input types the agent may inject, and require_postcondition_probe requires a post-condition probe after an injection.

FieldTypeRequiredDefault
enabled
bool
no
false
allowed_types
Vec<String>
no
[]
require_postcondition_probe
bool
no
false

browser_automation

Domain and verb gating for browser automation, with credential detection in typed input. An empty allowed_verbs turns the verb check off; the guard lower-cases a verb before comparing it. extra_credential_patterns adds regexes to the guard's credential detector.

FieldTypeRequiredDefault
enabled
bool
no
false
allowed_domains
Vec<String>
no
[]
blocked_domains
Vec<String>
no
[]
allowed_verbs
Vec<String>
no
[]
credential_detection
bool
no
true
extra_credential_patterns
Vec<String>
no
[]

code_execution

Sandboxed-interpreter limits: the languages the agent may run, the modules it may not import, whether executed code may reach the network, and a wall-clock ceiling in milliseconds. max_scan_bytes is the largest code payload the guard scans for denylisted modules; it denies a larger payload outright, and the field overrides the guard's default only when set.

FieldTypeRequiredDefault
enabled
bool
no
false
language_allowlist
Vec<String>
no
[]
module_denylist
Vec<String>
no
[]
network_access
bool
no
false
max_execution_time_ms
Option<u64>
no
unset
max_scan_bytes
Option<usize>
no
unset

velocity

Token-bucket limits on invocations and spend. max_invocations_per_window or max_spend_per_window produces the grant-scoped velocity guard; max_requests_per_agent or max_requests_per_session produces the identity-scoped agent-velocity guard. With none of them set, the block adds no guard. The compiler clamps window_secs to at least 1 and replaces a burst_factor that is not finite and positive with 1.0. max_spend_per_window is in integer minor units, the unit of a grant's max_cost_per_invocation.

FieldTypeRequiredDefault
enabled
bool
no
true
max_invocations_per_window
Option<u32>
no
unset
max_spend_per_window
Option<u64>
no
unset
max_requests_per_agent
Option<u32>
no
unset
max_requests_per_session
Option<u32>
no
unset
window_secs
u64
no
60
burst_factor
f64
no
1.0

human_in_loop

Approval gating. This block adds no guard. When the default scope carries grants, the compiler attaches a RequireApprovalAbove constraint: a grant whose tool matches a require_confirmation glob gets threshold 0, and otherwise approve_above becomes the threshold in integer minor units. The compiler reads enabled, require_confirmation, and approve_above; approve_above_currency, timeout_seconds, and on_timeout stay in the parsed document and change neither the compiled scope nor any guard.

FieldTypeRequiredDefault
enabled
bool
no
true
require_confirmation
Vec<String>
no
[]
approve_above
Option<u64>
no
unset
approve_above_currency
Option<String>
no
unset
timeout_seconds
Option<u64>
no
unset
on_timeout
HumanInLoopTimeoutAction
no
deny
policy.yamlyaml
rules:
  velocity:
    max_requests_per_agent: 600
    window_secs: 60
    burst_factor: 1.5

  human_in_loop:
    require_confirmation:
      - "transfer_*"
    approve_above: 50000
    approve_above_currency: USD
    timeout_seconds: 300
    on_timeout: deny

Built-in rulesets

The crate embeds 7 rulesets with include_str! and lists them in BUILTIN_RULESETS. builtin_yaml returns one by bare name or by a name with a chio: or hushspec: prefix; load_builtin parses and compiles it. Each ruleset's name equals its file stem, except panic, whose name is the reserved identifier __hushspec_panic__.

ai-agent

The default forbidden paths, with exceptions for **/.env.example and **/.env.template. Egress adds api.together.xyz, api.fireworks.ai, gitlab.com, and bitbucket.org. Secret patterns add the Anthropic key and skip fixtures and mocks. Patch integrity allows 2000 additions and 1000 deletions at ratio 20.0.

Shell commands forbid recursive deletion of / and curl or wget piped to bash. Tool access is default: allow, blocking shell_exec and run_command, confirming git_push, deploy, and publish, with max_args_size: 2097152 (2 MiB).

cicd

Forbids SSH, AWS, environment, git-credential, GnuPG, and CI secret directories, excepting .github/workflows, .gitlab-ci.yml, and .circleci/config.yml. Egress is limited to named package registries (npm, PyPI, crates.io, RubyGems, Packagist, Gradle plugins), container registries (Docker Hub, docker.com, GCR, ECR, GHCR), Maven Central, and Gradle services. Secret patterns cover AWS keys, GitHub tokens, and private keys.

Tool access is default: block, allowing read_file, write_file, list_directory, run_tests, and build, and blocking shell_exec and deploy_production.

default

The default ruleset, as the crate embeds it:

crates/guards/chio-policy/src/rulesets/default.yamlyaml
hushspec: "0.1.0"
name: default
description: Default security rules for AI agent execution

rules:
  forbidden_paths:
    patterns:
      # SSH keys
      - "**/.ssh/**"
      - "**/id_rsa*"
      - "**/id_ed25519*"
      - "**/id_ecdsa*"
      # Cloud/infra credentials
      - "**/.aws/**"
      - "**/.gnupg/**"
      - "**/.kube/**"
      - "**/.docker/**"
      - "**/.npmrc"
      # Environment files
      - "**/.env"
      - "**/.env.*"
      # Git credentials
      - "**/.git-credentials"
      - "**/.gitconfig"
      # Password stores
      - "**/.password-store/**"
      - "**/pass/**"
      - "**/.1password/**"
      # Unix system paths
      - "/etc/shadow"
      - "/etc/passwd"
      - "/etc/sudoers"
      # Windows credentials and registry hives
      - "**/AppData/Roaming/Microsoft/Credentials/**"
      - "**/AppData/Local/Microsoft/Credentials/**"
      - "**/AppData/Roaming/Microsoft/Vault/**"
      - "**/NTUSER.DAT"
      - "**/Windows/System32/config/SAM"
      - "**/Windows/System32/config/SECURITY"
      - "**/Windows/System32/config/SYSTEM"
    exceptions: []

  egress:
    allow:
      - "*.openai.com"
      - "*.anthropic.com"
      - "api.github.com"
      - "github.com"
      - "*.githubusercontent.com"
      - "*.npmjs.org"
      - "pypi.org"
      - "files.pythonhosted.org"
      - "crates.io"
      - "static.crates.io"
    block: []
    default: block

  secret_patterns:
    patterns:
      - name: aws_access_key
        pattern: "AKIA[0-9A-Z]{16}"
        severity: critical
      - name: github_token
        pattern: "gh[ps]_[A-Za-z0-9]{36}"
        severity: critical
      - name: openai_key
        pattern: "sk-[A-Za-z0-9]{48}"
        severity: critical
      - name: private_key
        pattern: "-----BEGIN\\s+(RSA\\s+)?PRIVATE\\s+KEY-----"
        severity: critical
    skip_paths:
      - "**/test/**"
      - "**/tests/**"
      - "**/*_test.*"
      - "**/*.test.*"

  patch_integrity:
    max_additions: 1000
    max_deletions: 500
    require_balance: false
    max_imbalance_ratio: 10.0
    forbidden_patterns:
      - "(?i)disable[\\s_\\-]?(security|auth|ssl|tls)"
      - "(?i)skip[\\s_\\-]?(verify|validation|check)"
      - "(?i)rm\\s+-rf\\s+/"
      - "(?i)chmod\\s+777"

  tool_access:
    allow: []
    block:
      - shell_exec
      - run_command
      - raw_file_write
      - raw_file_delete
    require_confirmation:
      - file_write
      - file_delete
      - git_push
    default: allow
    max_args_size: 1048576

panic

Deny-all, activated by panic mode. forbidden_paths matches **, egress blocks * with default: block, shell_commands forbids .*, tool_access blocks * with default: block, and computer_use runs in fail_closed mode.

permissive

Egress allows * with default: allow. Patch integrity allows 10000 additions and 5000 deletions, with no balance requirement, at ratio 50.0. The ruleset sets no other block.

remote-desktop

computer_use is enabled in guardrail mode with the remote session, input injection, clipboard, file transfer, audio, drive mapping, printing, and session-share action types allowed. remote_desktop_channels has audio on and clipboard, file transfer, and drive mapping off. input_injection allows keyboard and mouse with no post-condition probe.

strict

The default forbidden paths plus NTUSER.DAT variants, system certificates, registry exports, and vault, secrets, credentials, and private directories. Egress has empty allow and block lists and default: block, so the policy evaluator permits no host while the compiled guard falls back to its built-in allowlist. Secret patterns add Anthropic, npm, Slack, and generic API-key tokens.

Patch integrity allows 500 additions and 200 deletions with balance required at ratio 5.0, forbidding eval, exec, and reverse or bind shells. Tool access is default: block, allowing read_file, list_directory, search, and grep, with max_args_size: 524288.

Inheritance

Resolving one rule blockChoose what the child declares.
π₀base.yamlπ₁policy.yaml∅field defaultsdefaultblockallow[quote]max_args_size4096κtool_namequotemax_args_size4096π₀base.yamlπ₁child∅field defaultsdefaultblockallow[quote]max_args_size4096κtool_namequotemax_args_size4096

An omitted block is inherited whole

policy.yaml declares only secret_patterns, so tool_access resolves to the base block: default block, allow [quote], max_args_size: 4096. The default grant names quote with a 4,096-byte argument limit. Author a policy.

The tool_access block of base.yaml resolved under policy.yaml, strict.yaml, and incomplete.yaml, with the first default grant from each captured compile. Other rule blocks, extensions, and the runtime-assurance constraint are left out.HushSpec policy format

extends names a base document. resolve_from_path loads the child, follows extends as a filesystem path, resolves the base's own extends first, and merges the base into the child. It uses an absolute path as written and joins a relative path to the directory of the document that names it. The CLI and the trust control plane's policy loader resolve through this function.

The resolver reads the filesystem; the crate has no HTTP loader. It rejects a document larger than DEFAULT_MAX_POLICY_DOCUMENT_BYTES (4 MiB), a chain deeper than DEFAULT_MAX_EXTENDS_DEPTH (32), and a cycle, which it reports as the chain of canonical paths. resolve_from_path_with_limits takes both limits and refuses a limit of 0.

Built-in names belong to builtin_yaml and load_builtin, which accept the chio: and hushspec: prefixes. The filesystem resolver skips BUILTIN_RULESETS, so extends: chio:strict resolves as a file of that name next to the child document and fails with a read error when no such file exists.

Merge strategies

merge composes a base and a child according to the child's merge_strategy, default deep_merge. Under every strategy the result takes the child's hushspec, clears extends, and keeps the child's merge_strategy.

  • replace. The child's rules, extensions, name, description, and metadata, as written. The base contributes nothing.
  • merge. A rule block present in the child replaces the base's block whole, and a block absent from the child comes from the base. Each extension block comes whole from the child when present, else from the base. name, description, and metadata come from the child when present, else from the base.
  • deep_merge. Rules, name, description, and metadata as under merge. Extensions merge per block, as listed next.

Under deep_merge, each extension block merges by its own rule:

  • posture: the child's initial and transitions, and the union of states, with the child winning a name.
  • origins: profiles merged by id; the child's default_behavior, else the base's.
  • detection: within each of prompt_injection, jailbreak, and threat_intel, each key from the child when set, else from the base.
  • reputation: the union of tiers and per-key scoring, including weights.
  • runtime_assurance: whole from the child when present, else from the base.
  • chio: each sub-block from the child when present, else from the base.
hardened-agent.yamlyaml
hushspec: "0.1.0"
name: hardened-agent
extends: ./base.yaml
merge_strategy: deep_merge

rules:
  egress:
    allow:
      - api.github.com
    default: block

This child takes every rule block of base.yaml except egress, which it replaces whole, so the base's egress.block list is dropped. Fields a declared block omits take their own defaults, never the base's values.

Extensions

extensions holds the blocks below. Each is optional, and each nested struct rejects an unknown key.

posture

A state machine over named states. The block requires initial, states, and transitions. Each state has an optional description, a capabilities list, and a budgets map from name to integer. Each transition has from, to, on with a TransitionTrigger, and an optional after.

policy.yamlyaml
extensions:
  posture:
    initial: restricted
    states:
      restricted:
        description: Limited tool access
        capabilities: [read_file]
        budgets:
          tool_calls: 50
      standard:
        capabilities: [read_file, write_file, search]
        budgets:
          tool_calls: 500
    transitions:
      - from: restricted
        to: standard
        on: user_approval
      - from: standard
        to: restricted
        on: critical_violation

origins

Profiles matched against the origin of a request. default_behavior is deny or minimal_profile for an unmatched origin. Each profile has a required id; an optional match with provider, tenant_id, organization_id, space_id, space_type, visibility, external_participants, tags, groups, roles, sensitivity, and actor_role; and optional per-origin posture (a state name), tool_access and egress (the rule-block shapes above), data, budgets, bridge, and explanation.

data holds allow_external_sharing, redact_before_send, and block_sensitive_outputs. budgets holds tool_calls, egress_calls, and shell_commands. bridge holds allow_cross_origin, require_approval, and allowed_targets, each with provider, space_type, tags, and visibility. The tightest budgets.tool_calls across profiles compiles to an agent-velocity guard, described under Compilation.

policy.yamlyaml
extensions:
  origins:
    default_behavior: deny
    profiles:
      - id: internal-prod
        match:
          provider: google-workspace
          sensitivity: confidential
        tool_access:
          default: allow
        egress:
          default: block
          allow: [api.github.com]
        budgets:
          tool_calls: 500
      - id: external-partner
        match:
          external_participants: true
        tool_access:
          default: block
          allow: [read_file]

detection

The sub-blocks are optional, and in each one enabled defaults to true when the sub-block is present.

  • prompt_injection takes enabled, warn_at_or_above, block_at_or_above, and max_scan_bytes, and compiles to the prompt-injection guard. block_at_or_above sets its score threshold: safe 0.1, suspicious 0.4, high 0.8, critical 1.0. The compiler leaves warn_at_or_above unread.
  • jailbreak takes enabled, block_threshold, warn_threshold, and max_input_bytes, and compiles to the jailbreak guard. block_threshold divided by 100, capped at 100, becomes its threshold; max_input_bytes becomes the detector's scan limit.
  • threat_intel takes enabled, pattern_db, similarity_threshold, and top_k, and compiles to the embedding-anomaly guard, built from the JSON pattern database at pattern_db. A relative pattern_db resolves against the policy file's directory. similarity_threshold and top_k override the guard's defaults when set.

Validation rejects a max_scan_bytes or max_input_bytes of 0, and the compiler raises a max_scan_bytes below 1 to 1. It warns when the prompt-injection block level is below the warn level, treating an omitted warn_at_or_above as suspicious and an omitted block_at_or_above as high for the comparison.

The jailbreak thresholds are integer percentages. Validation rejects a value above 100 and warns when block_threshold is below warn_threshold, treating an omitted block_threshold as 80 and an omitted warn_threshold as 50 for that comparison. Those two numbers are validation assumptions only: an omitted block_threshold leaves the compiled guard at its own default, and the compiler accepts and discards warn_threshold because the guard has no warn-threshold field. Setting warn_threshold alone changes no runtime behavior. An enabled threat_intel requires pattern_db, and validation rejects an empty one.

policy.yamlyaml
extensions:
  detection:
    prompt_injection:
      block_at_or_above: high
      max_scan_bytes: 65536
    jailbreak:
      block_threshold: 85
      max_input_bytes: 32768
    threat_intel:
      pattern_db: ./data/threat-intel.json
      similarity_threshold: 0.82
      top_k: 5

reputation

scoring carries optional weights for boundary_pressure, resource_stewardship, least_privilege, history_depth, tool_diversity, delegation_hygiene, reliability, and incident_correlation, plus temporal_decay_half_life_days, probationary_receipt_count, probationary_score_ceiling, and probationary_min_days.

tiers maps a tier name to a required score_range pair and max_scope, with optional promotion (target, min_score, min_receipts, min_days, required_metrics) and demotion (target and triggers, each with type, threshold, and count). A max_scope has a required ttl_seconds and optional operations, max_invocations, max_cost_per_invocation, max_total_cost, max_delegation_depth, and constraints_required.

runtime_assurance

tiers maps a name to a rule with required minimum_attestation_tier (a RuntimeAssuranceTier) and max_scope, the same shape as a reputation tier's. trusted_verifiers maps a name to a rule with required schema, verifier, and effective_tier, and optional verifier_family, max_evidence_age_seconds, allowed_attestation_types, and required_assertions, a map from assertion name to expected value.

chio

A Chio-specific block. The compiler reads human_in_loop.approvers from it, as described under Threshold approvers. The rest of the block stays verbatim in the parsed document, and the kernel leaves it uninterpreted.

FieldTypeKeys
market_hours
ChioMarketHours
tz, open, and close required; days list.
signing
ChioSigning
algo required; required, default true; optional key_ref.
k8s_namespaces
ChioK8sNamespaces
allow, human_in_loop, and deny lists.
rollback
ChioRollback
on_guard_fail and on_timeout, default false; optional strategy.
human_in_loop
ChioHumanInLoopAdvanced
approve_when list; optional approvers with n required, of, and timeout_seconds.

Governance metadata

metadata records who wrote and approved a policy and where it stands in its lifecycle. Every field is optional. A merged document takes the child's whole metadata block when present, else the base's.

FieldTypeHolds
author
String
Policy author.
approved_by
String
Approver name or id.
approval_date
String
Date of approval.
classification
Classification
public, internal, confidential, or restricted.
change_ticket
String
Change-management ticket reference.
lifecycle_state
LifecycleState
draft, review, approved, deployed, deprecated, or archived.
policy_version
usize
Version number.
effective_date
String
Date the policy takes effect.
expiry_date
String
Date the policy expires.
policy.yamlyaml
metadata:
  author: larkfield-pricing
  approved_by: mara.okafor@larkfield.example
  approval_date: "2026-04-01"
  classification: internal
  change_ticket: "#415"
  lifecycle_state: deployed
  policy_version: 3
  effective_date: "2026-04-01"
  expiry_date: "2026-10-01"

Compilation

Compiled controlsChoose a control.

The host signs authority from the compiled scope

tool_access compiles into default_scope: one grant to invoke quote on server *, constrained by MaxArgsSize(4096). The scope is unsigned. The host issues a capability token from it, and the agent presents that token with each request. Capability reference.

base.yaml and policy.yaml resolve into one HushSpec, which compiles into a capability scope, request guards, and an output hook. Threshold approval and detection guards compile the same way and are left out; the grant's operation (invoke) and server (*) are omitted from the labels.Policy compilation

compile_policy validates the document, then builds a CompiledPolicy: a guard pipeline, a post-invocation pipeline, a default capability scope, an optional threshold-approval requirement, and guard_names, the runtime name of each guard in the order it was added. A missing section compiles to nothing, and a document that leaves a guard unused compiles without error.

Guard per block

BlockGuardNotes
forbidden_paths
ForbiddenPathGuard (forbidden-path)
Built-in patterns when patterns is empty.
velocity
VelocityGuard (velocity), AgentVelocityGuard (agent-velocity)
Each only when its limits are set; placed second.
shell_commands
ShellCommandGuard (shell-command)
Default forbidden set when forbidden_patterns is empty.
egress
EgressAllowlistGuard (egress-allowlist), InternalNetworkGuard (internal-network)
default is applied by the policy evaluator.
tool_access
McpToolGuard (mcp-tool)
allow, block, default, max_args_size; the rest shape the default scope.
secret_patterns
SecretLeakGuard (secret-leak)
Plus a SanitizerHook on the post-invocation pipeline.
patch_integrity
PatchIntegrityGuard (patch-integrity)
Every field carried.
path_allowlist
PathAllowlistGuard (path-allowlist)
read, write, patch become access, write, and patch allowlists.
computer_use
ComputerUseGuard (computer-use)
mode maps to the guard's enforcement mode.
remote_desktop_channels
RemoteDesktopSideChannelGuard (remote-desktop-side-channel)
Session sharing, printing, transfer size at guard defaults.
input_injection
InputInjectionCapabilityGuard (input-injection-capability)
allowed_types becomes the input-type allowlist.
browser_automation
BrowserAutomationGuard (browser-automation)
Every field carried.
code_execution
CodeExecutionGuard (code-execution)
max_scan_bytes overrides the guard default only when set.
human_in_loop
none
RequireApprovalAbove constraints on the default scope.
extensions.detection.prompt_injection
PromptInjectionGuard (prompt-injection)
enabled defaults to true when present.
extensions.detection.jailbreak
JailbreakGuard (jailbreak)
block_threshold divided by 100 is the threshold.
extensions.detection.threat_intel
EmbeddingAnomalyGuard (embedding-anomaly)
pattern_db read at compile time, relative to the policy file.
extensions.origins.profiles[].budgets.tool_calls
AgentVelocityGuard (agent-velocity)
The tightest value across profiles, 60 second window, burst 1.0.
extensions.chio.human_in_loop.approvers
none
A ThresholdApprovalRequirement; see Threshold approvers.

Guards enter the pipeline in the order of the rule rows above, then the detection guards, then the origin-budget guard. The velocity guards sit second so the pipeline observes a rate-limit denial before shell semantics fire. The runtime name in parentheses is the name the guard registers and the one that appears in guard_names.

Default scope

The default scope is an unsigned ChioScope derived from tool_access; the host issues capability tokens from it. With no rules, no tool_access, or enabled: false, it holds one grant on server * and tool * with the Invoke operation and no constraints.

With default: allow, a require_confirmation list that names particular tools instead of *, in tool_access or in an enabled human_in_loop, cannot widen to a wildcard, so the compiler emits an empty scope and leaves those confirmations to the policy evaluator. With nothing to constrain, the scope is the unconstrained wildcard grant. With max_args_size, require_runtime_assurance_tier, a * confirmation, or an enabled human_in_loop that sets approve_above or a * confirmation, the scope is one wildcard grant carrying those constraints. Any other combination, including a non-empty allow or block list, yields an empty scope.

With default: block, an empty allow yields an empty scope, as does either workload-identity field. Otherwise each allow entry that does not overlap the block list becomes a grant on server * for that tool pattern with the Invoke operation. The compiler drops an entry that overlaps, and a document whose entries all overlap yields an empty scope.

A grant carries its constraints in this order:

  1. MaxArgsSize from max_args_size.
  2. RequireApprovalAbove with threshold 0 when the grant's tool matches a confirmation glob in tool_access or in an enabled human_in_loop, else with approve_above when set.
  3. MinimumRuntimeAssurance from require_runtime_assurance_tier.

Threshold approvers

extensions.chio.human_in_loop.approvers compiles to a ThresholdApprovalRequirement bound to the policy hash. compile_policy has no approver directory and refuses a document that sets approvers; compile_policy_with_approver_directory and compile_policy_with_source_and_approver_directory take one.

FieldTypeCompiles to
approvers.n
u32, required
The quorum. Refused when 0 or larger than the resolved approver set.
approvers.of
Vec<String>
Approver identifiers, resolved through the directory to public keys.
approvers.timeout_seconds
Option<u64>
The approval deadline. Default 900; refused when 0 or above 3600.

The compiler refuses an approver identifier the directory cannot resolve, one the directory returns under a different identifier, and a set that does not resolve from one non-empty directory version. The requirement refuses an empty identifier, a duplicate identifier, a duplicate public key, and more than MAX_THRESHOLD_APPROVAL_TOKENS (32) approvers.

The timeout default is DEFAULT_THRESHOLD_APPROVAL_TIMEOUT_SECONDS (900), and the ceiling is MAX_THRESHOLD_APPROVAL_TIMEOUT_SECONDS (3600).

Examples

An authored document that sets several rule blocks. Tool names, domains, and patterns are values a deployment chooses; the schema fixes the shape.

policy.yamlyaml
hushspec: "0.1.0"
name: production-workspace
description: Governed code agent

rules:
  forbidden_paths:
    patterns:
      - "**/.env"
      - "**/.env.*"
      - "**/.ssh/**"
    exceptions:
      - "**/.env.example"

  path_allowlist:
    enabled: true
    read:
      - ./workspace/**
    write:
      - ./workspace/output/**

  egress:
    allow:
      - api.github.com
      - "*.npmjs.org"
    default: block

  secret_patterns:
    patterns:
      - name: aws_access_key
        pattern: "AKIA[0-9A-Z]{16}"
        severity: critical
    skip_paths:
      - "**/tests/**"

  shell_commands:
    forbidden_patterns:
      - "(?i)rm\\s+-rf\\s+/"

  tool_access:
    default: block
    allow:
      - read_file
      - list_directory
    require_confirmation:
      - write_file

metadata:
  author: security-team
  classification: internal
  lifecycle_state: deployed
  policy_version: 1

chio policy explain prints what the document compiles to: the default grants from tool_access, the request guards in pipeline order, the output hook that secret_patterns adds, and the approval that require_confirmation sets.

Terminalbash
chio policy explain policy.yaml
Output
policy     policy.yaml
grants     read_file on server *
           list_directory on server *
           write_file on server *      approval required
guards     1 forbidden-path
           2 shell-command
           3 egress-allowlist
           4 internal-network
           5 mcp-tool
           6 secret-leak
           7 path-allowlist
output     1 hook
approvals  write_file on every call
Source files

The schema is the chio-policy crate in the Chio repository: the document types in crates/guards/chio-policy/src/models.rs, crates/guards/chio-policy/src/models/rules.rs, crates/guards/chio-policy/src/models/enums.rs, and crates/guards/chio-policy/src/models/extensions.rs; the bundled rulesets under crates/guards/chio-policy/src/rulesets/; inheritance in crates/guards/chio-policy/src/resolve.rs and crates/guards/chio-policy/src/merge.rs; validation in crates/guards/chio-policy/src/validate.rs; and the compiler in crates/guards/chio-policy/src/compiler.rs and crates/guards/chio-policy/src/compiler/. HUSHSPEC_VERSION lives in crates/guards/chio-policy/src/version.rs.

Next: author a policy and compile it

Policy Schema Reference · Chio Docs