ReferenceSpec
Policy Schema Reference
The HushSpec document chio-policy parses: top-level fields, rule blocks with their defaults, built-in rulesets, inheritance through extends, and what compiles to which guard.
Source
This page normatively reflects 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/.
The schema version is the HUSHSPEC_VERSION constant in crates/guards/chio-policy/src/version.rs, 0.1.0. is_supported accepts only the entries of HUSHSPEC_SUPPORTED_VERSIONS, which lists that version alone.
Synopsis
hushspec: "0.1.0"The version line alone is a valid document: it parses, validation reports no error, and the validator warns that no rules section is present.
| 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 | How the child composes with its base: 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. |
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 marked value 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_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. A block that is omitted adds no guard. A block with enabled: false adds no guard either; 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<String> | no | [] |
exceptions | Vec<String> | 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<String> | no | [] |
write | Vec<String> | no | [] |
patch | Vec<String> | 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<String> | no | [] |
block | Vec<String> | 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<SecretPattern> | no | [] |
skip_paths | Vec<String> | 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_patterns | Vec<String> | no | [] |
require_balance | bool | no | false |
max_imbalance_ratio | 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_patterns | Vec<String> | 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<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 |
WorkloadIdentityMatch
require_workload_identity and prefer_workload_identity take 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<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 are not modeled here 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<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.
| Field | Type | Required | Default |
|---|---|---|---|
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.
| Field | Type | Required | Default |
|---|---|---|---|
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.
| Field | Type | Required | Default |
|---|---|---|---|
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.
| Field | Type | Required | Default |
|---|---|---|---|
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 |
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__.
| Name | Sets |
|---|---|
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 at 2000 additions and 1000 deletions, ratio 20.0. Shell commands forbid recursive deletion of / and curl or wget piped to bash. Tool access 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 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 for AWS keys, GitHub tokens, and private keys. Tool access default: block allowing read_file, write_file, list_directory, run_tests, and build, blocking shell_exec and deploy_production. |
default | The file is quoted in full after the table. |
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 at 10000 additions and 5000 deletions, no balance, ratio 50.0. No other block. |
remote-desktop | computer_use 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 with audio on and clipboard, file transfer, and drive mapping off. input_injection allowing 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 with 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 at 500 additions and 200 deletions with balance required at ratio 5.0, forbidding eval, exec, and reverse or bind shells. Tool access default: block allowing read_file, list_directory, search, and grep, with max_args_size: 524288. |
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: 1048576Inheritance
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 control plane's policy loader resolve through this function.
The resolver reads the filesystem only; 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 in extends
BUILTIN_RULESETS. A value such as 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. The chio: and hushspec: prefixes are accepted by builtin_yaml and load_builtin, which take a ruleset name directly.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.
| Strategy | Rules | Extensions | name, description, metadata |
|---|---|---|---|
replace | The child's, as written. The base contributes nothing. | The child's, as written. | The child's, as written. |
merge | A rule block present in the child replaces the base's block whole. A block absent from the child comes from the base. | Each extension block comes whole from the child when present, else from the base. | From the child when present, else from the base. |
deep_merge | As merge. | 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. | As merge. |
hushspec: "0.1.0"
name: hardened-agent
extends: ./base.yaml
merge_strategy: deep_merge
rules:
egress:
allow:
- api.github.com
default: blockThe child above takes every rule block of base.yaml except egress, which it replaces whole: the base's egress.block list does not carry over.
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. In each, enabled defaults to true when the sub-block is present.
| Sub-block | Keys | Compiles to |
|---|---|---|
prompt_injection | enabled, warn_at_or_above, block_at_or_above, max_scan_bytes | The prompt-injection guard. block_at_or_above sets its score threshold: safe 0.1, suspicious 0.4, high 0.8, critical 1.0. warn_at_or_above is not read by the compiler. |
jailbreak | enabled, block_threshold, warn_threshold, max_input_bytes | 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 | enabled, pattern_db, similarity_threshold, top_k | The embedding-anomaly guard, built from the JSON pattern database at pattern_db, resolved relative to the policy file's directory when relative. 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: when block_threshold is omitted, the compiled guard keeps its own default threshold, and the compiler accepts warn_threshold and discards it, 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, described under Threshold approvers. The rest of the block stays verbatim in the parsed document; the kernel does not interpret it.
| 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 | 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.
| 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-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. |
metadata:
author: security-team
approved_by: ciso@example.com
approval_date: "2026-04-01"
classification: internal
change_ticket: SEC-1234
lifecycle_state: deployed
policy_version: 3
effective_date: "2026-04-01"
expiry_date: "2026-10-01"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 the compiler raises no error for a document that does not exercise every guard.
Guard per block
| Block | Guard | Notes |
|---|---|---|
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 so the pipeline observes a rate-limit denial before shell semantics fire. |
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, not by the guard. |
tool_access | McpToolGuard (mcp-tool) | allow, block, default, and max_args_size; the other fields shape the default scope. |
secret_patterns | SecretLeakGuard (secret-leak) | Plus a SanitizerHook on the post-invocation pipeline with the same patterns. |
patch_integrity | PatchIntegrityGuard (patch-integrity) | Every field carried. |
path_allowlist | PathAllowlistGuard (path-allowlist) | read, write, and patch become the guard's 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, and transfer size stay at the guard's defaults. |
input_injection | InputInjectionCapabilityGuard (input-injection-capability) | allowed_types becomes the guard's 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 | No guard. RequireApprovalAbove constraints on the default scope. |
extensions.detection.prompt_injection | PromptInjectionGuard (prompt-injection) | enabled defaults to true when the sub-block is present. |
extensions.detection.jailbreak | JailbreakGuard (jailbreak) | block_threshold divided by 100 becomes the guard threshold. |
extensions.detection.threat_intel | EmbeddingAnomalyGuard (embedding-anomaly) | pattern_db is read at compile time, relative to the policy file. |
extensions.origins.profiles[].budgets.tool_calls | AgentVelocityGuard (agent-velocity) | The tightest value across profiles, over a 60 second window with burst factor 1.0. |
extensions.chio.human_in_loop.approvers | none | No guard. 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 runtime name in parentheses is the name the guard registers, the one that appears in guard_names.
Default scope
The default scope is a ChioScope derived from tool_access. 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, in tool_access or in an enabled human_in_loop, that names particular tools instead of * cannot be widened 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's constraints, in order: MaxArgsSize from max_args_size; 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; and 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.
| 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. The compiler resolves each through the directory to a public key and refuses an 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. |
approvers.timeout_seconds | Option<u64> | The approval deadline. Default DEFAULT_THRESHOLD_APPROVAL_TIMEOUT_SECONDS (900). Refused when 0 or above MAX_THRESHOLD_APPROVAL_TIMEOUT_SECONDS (3600). |
Examples
An authored document that sets several rule blocks. Tool names, domains, and patterns are values a deployment would choose; the shape is what the schema fixes.
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