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.
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.
Try max_args_size, deep_merge, or agent-velocity. Each entry has its own link.
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.
hushspec: "0.1.0"| Field | Type | Required | Holds |
|---|---|---|---|
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_, and chio. |
metadata | Option<Governance | 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.
| Type | Values | Used by |
|---|---|---|
MergeStrategy | replace, merge, deep_merge (default) | merge_strategy |
DefaultAction | allow, block | egress.default, tool_ |
Severity | critical, error, warn | secret_ |
ComputerUseMode | observe, guardrail (default), fail_closed | computer_ |
Human | deny (default), defer | human_ |
Runtime | none, basic, attested, verified | tool_, tool_, runtime_ tiers and verifiers |
Workload | spiffe | Workload |
Workload | uri, x509_svid, jwt_svid | Workload |
Transition | user_approval, user_denial, critical_, any_violation, timeout, budget_, pattern_match | posture. |
Origin | deny (default), minimal_profile | origins. |
DetectionLevel | safe, suspicious, high, critical | detection., detection. |
Classification | public, internal, confidential, restricted | metadata. |
LifecycleState | draft, review, approved, deployed, deprecated, archived | metadata. |
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.
| Field | Type | Required | Default |
|---|---|---|---|
enabled | bool | no | true |
patterns | Vec< | no | [] |
exceptions | Vec< | no | [] |
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.
| Field | Type | Required | Default |
|---|---|---|---|
enabled | bool | no | false |
read | Vec< | no | [] |
write | Vec< | no | [] |
patch | Vec< | no | [] |
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.
| Field | Type | Required | Default |
|---|---|---|---|
enabled | bool | no | true |
allow | Vec< | no | [] |
block | Vec< | no | [] |
default | DefaultAction | no | block |
rules:
egress:
allow:
- api.github.com
- "*.npmjs.org"
block: []
default: blocksecret_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.
| Field | Type | Required | Default |
|---|---|---|---|
enabled | bool | no | true |
patterns | Vec< | no | [] |
skip_paths | Vec< | no | [] |
Each entry of patterns is a SecretPattern:
| Field | Type | Required | Holds |
|---|---|---|---|
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. |
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.
| Field | Type | Required | Default |
|---|---|---|---|
enabled | bool | no | true |
max_additions | usize | no | 1000 |
max_deletions | usize | no | 500 |
forbidden_ | Vec< | no | [] |
require_balance | bool | no | false |
max_ | f64 | no | 10.0 |
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.
| Field | Type | Required | Default |
|---|---|---|---|
enabled | bool | no | true |
forbidden_ | Vec< | no | [] |
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 /.
| Field | Type | Required | Default |
|---|---|---|---|
enabled | bool | no | true |
allow | Vec< | no | [] |
block | Vec< | no | [] |
require_ | Vec< | no | [] |
default | DefaultAction | no | allow |
max_args_size | Option< | no | unset |
require_ | Option< | no | unset |
prefer_ | Option< | no | unset |
require_ | Option< | no | unset |
prefer_ | Option< | 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.
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: attestedcomputer_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.
| Field | Type | Required | Default |
|---|---|---|---|
enabled | bool | no | false |
mode | ComputerUseMode | no | guardrail |
allowed_actions | Vec< | 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.
| Field | Type | Required | Default |
|---|---|---|---|
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.
| Field | Type | Required | Default |
|---|---|---|---|
enabled | bool | no | false |
allowed_types | Vec< | no | [] |
require_ | 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.
| Field | Type | Required | Default |
|---|---|---|---|
enabled | bool | no | false |
allowed_domains | Vec< | no | [] |
blocked_domains | Vec< | no | [] |
allowed_verbs | Vec< | no | [] |
credential_ | bool | no | true |
extra_ | Vec< | 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.
| Field | Type | Required | Default |
|---|---|---|---|
enabled | bool | no | false |
language_ | Vec< | no | [] |
module_denylist | Vec< | no | [] |
network_access | bool | no | false |
max_ | Option< | no | unset |
max_scan_bytes | Option< | 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.
| Field | Type | Required | Default |
|---|---|---|---|
enabled | bool | no | true |
max_ | Option< | no | unset |
max_ | Option< | no | unset |
max_ | Option< | no | unset |
max_ | Option< | 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.
| Field | Type | Required | Default |
|---|---|---|---|
enabled | bool | no | true |
require_ | Vec< | no | [] |
approve_above | Option< | no | unset |
approve_ | Option< | no | unset |
timeout_seconds | Option< | no | unset |
on_timeout | Human | no | deny |
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: denyBuilt-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:
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: 1048576panic
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
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.
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 formatextends 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, andmetadata, 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, andmetadatacome from the child when present, else from the base.deep_merge. Rules,name,description, andmetadataas undermerge. Extensions merge per block, as listed next.
Under deep_merge, each extension block merges by its own rule:
posture: the child'sinitialandtransitions, and the union ofstates, with the child winning a name.origins: profiles merged byid; the child'sdefault_behavior, else the base's.detection: within each ofprompt_injection,jailbreak, andthreat_intel, each key from the child when set, else from the base.reputation: the union oftiersand per-keyscoring, includingweights.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.
hushspec: "0.1.0"
name: hardened-agent
extends: ./base.yaml
merge_strategy: deep_merge
rules:
egress:
allow:
- api.github.com
default: blockThis 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.
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_violationorigins
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.
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_injectiontakesenabled,warn_at_or_above,block_at_or_above, andmax_scan_bytes, and compiles to the prompt-injection guard.block_at_or_abovesets its score threshold:safe0.1,suspicious0.4,high0.8,critical1.0. The compiler leaveswarn_at_or_aboveunread.jailbreaktakesenabled,block_threshold,warn_threshold, andmax_input_bytes, and compiles to the jailbreak guard.block_thresholddivided by 100, capped at 100, becomes its threshold;max_input_bytesbecomes the detector's scan limit.threat_inteltakesenabled,pattern_db,similarity_threshold, andtop_k, and compiles to the embedding-anomaly guard, built from the JSON pattern database atpattern_db. A relativepattern_dbresolves against the policy file's directory.similarity_thresholdandtop_koverride 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.
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: 5reputation
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.
| Field | Type | Keys |
|---|---|---|
market_hours | ChioMarketHours | tz, open, and close required; days list. |
signing | ChioSigning | algo required; required, default true; optional key_ref. |
k8s_namespaces | Chio | allow, human_in_loop, and deny lists. |
rollback | ChioRollback | on_guard_fail and on_timeout, default false; optional strategy. |
human_in_loop | Chio | 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.
| Field | Type | Holds |
|---|---|---|
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- |
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. |
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
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.
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
| Block | Guard | Notes |
|---|---|---|
forbidden_paths | Forbidden (forbidden-path) | Built-in patterns when patterns is empty. |
velocity | VelocityGuard (velocity), Agent (agent-velocity) | Each only when its limits are set; placed second. |
shell_commands | Shell (shell-command) | Default forbidden set when forbidden_ is empty. |
egress | Egress (egress-), Internal (internal-) | 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 | Patch (patch-integrity) | Every field carried. |
path_allowlist | Path (path-allowlist) | read, write, patch become access, write, and patch allowlists. |
computer_use | Computer (computer-use) | mode maps to the guard's enforcement mode. |
remote_ | Remote (remote-) | Session sharing, printing, transfer size at guard defaults. |
input_injection | Input (input-) | allowed_types becomes the input-type allowlist. |
browser_ | Browser (browser-) | Every field carried. |
code_execution | Code (code-execution) | max_scan_bytes overrides the guard default only when set. |
human_in_loop | none | Require constraints on the default scope. |
extensions. | Prompt (prompt-) | enabled defaults to true when present. |
extensions. | JailbreakGuard (jailbreak) | block_threshold divided by 100 is the threshold. |
extensions. | Embedding (embedding-) | pattern_db read at compile time, relative to the policy file. |
extensions. | Agent (agent-velocity) | The tightest value across profiles, 60 second window, burst 1.0. |
extensions. | none | A Threshold; 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:
MaxArgsSizefrommax_args_size.RequireApprovalAbovewith threshold 0 when the grant's tool matches a confirmation glob intool_accessor in an enabledhuman_in_loop, else withapprove_abovewhen set.MinimumRuntimeAssurancefromrequire_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.
| Field | Type | Compiles 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. | 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.
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: 1chio 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.
chio policy explain policy.yamlpolicy 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 callSource 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.