HushSpec Policy Format
Write a HushSpec policy, inspect the controls it compiles to, and look up any field, default, or inheritance rule.
On this page
A HushSpec policy is one YAML document that sets an agent's tool authority, filesystem and network rules, output checks, resource limits, and approval requirements. The compiler resolves the policy's local bases and emits request guards, output hooks, and a default capability scope for the host to install on the kernel.
A child policy inherits every rule block it leaves out
The examples govern the pricing service from Quickstart part 2 and its three tools: quote, refund, and discount. Download the HushSpec examples and unpack them anywhere. The parent permits quote with an argument limit of 4,096 bytes; the child adds a pattern for your live API keys and inherits the whole tool_access block without repeating it.
hushspec: "0.1.0"
name: pricing-base
rules:
tool_access:
enabled: true
default: block
allow: [quote]
max_args_size: 4096hushspec: "0.1.0"
extends: base.yaml
rules:
secret_patterns:
enabled: true
patterns:
- name: larkfield-api-key
pattern: 'lark_live_[A-Za-z0-9]{24}'
severity: errorchio policy explain hushspec-policies/policy.yamlpolicy hushspec-policies/policy.yaml extends hushspec-policies/base.yaml
grants quote on server * max_args_size 4096
guards 1 mcp-tool
2 secret-leak
output 1 hook
approvals noneThe child compiles to two request guards, one output hook, and one grant: invoke quote on any server with arguments under 4,096 bytes. A refund request finds no grant at all. The host installs those controls and issues a capability for the intended server and agent; Policy Compilation shows the complete serialized scope.
A rule block the child declares replaces the parent's block whole
Under both merge and deep_merge, a declared rule block takes the parent's place entirely. Repeat every field whose restriction you want to keep.
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.strict.yaml keeps quote, lowers the argument limit to 2,048 bytes, and requires an attested runtime by repeating the complete block.
hushspec: "0.1.0"
extends: policy.yaml
rules:
tool_access:
enabled: true
default: block
allow: [quote]
max_args_size: 2048
require_runtime_assurance_tier: attestedchio policy explain hushspec-policies/strict.yamlpolicy hushspec-policies/strict.yaml extends hushspec-policies/policy.yaml
grants quote on server * max_args_size 2048, runtime assurance attested
guards 1 mcp-tool
2 secret-leak
output 1 hook
approvals noneThe three strategies differ in what they do with extensions and with the parent as a whole:
deep_merge(default). Extension sub-blocks merge with their own functions: posture states by name, origin profiles by id.runtime_assuranceandmetadataare replaced whole when the child supplies them, and rule blocks are replaced whole, as undermerge.merge. Each rule or extension sub-block the child declares replaces the parent's. Omitted sub-blocks carry forward.replace. The child replaces the parent. Theextendschain is still loaded for source provenance, and no fields carry forward.
Whether a list is appended depends on the extension's merge function. For environment overlays, see Inherit & Merge Policies.
Setting one field resets the rest of the block
incomplete.yaml is valid YAML that looks as though it changes only the argument limit. The tool fields it omits deserialize to their own defaults: default becomes allow and allow becomes empty.
hushspec: "0.1.0"
name: incomplete-override
extends: policy.yaml
rules:
tool_access:
max_args_size: 2048chio policy explain hushspec-policies/incomplete.yamlpolicy hushspec-policies/incomplete.yaml extends hushspec-policies/policy.yaml
grants * on server * max_args_size 2048
guards 1 mcp-tool
2 secret-leak
output 1 hook
approvals noneThe compiler emits a wildcard tool grant with the smaller size limit: an agent holding it may call refund and discount as freely as quote. The secret checks survive because the child omits secret_patterns. Inspect the resolved scope before installing any override, and use the strict.yaml form to keep a named-tool restriction.
Compilation turns the resolved document into guards, hooks, and a default scope
The loader parses the YAML, resolves and merges its local bases, and compiles the result into CompiledPolicy: request guards, output hooks, and a default capability scope. The host installs those pipelines and issues authority for its workload.
The policy hash names the policy the kernel applies
SHA-256 over a JSON fingerprint of the parsed policy: its format, kernel settings, guard configuration, and default capabilities. Comments and formatting are not part of it. The kernel binds this hash into every receipt as policy_hash, and chio check prints it on its policy: line. Matching hashes mean two decisions ran under the same policy; a changed hash does not say which rule changed. Receipts.
In Rust · loader.rs:4414 lines
pub fn load_policy(path: &Path) -> Result<LoadedPolicy, PolicyError> {
let contents = std::fs::read_to_string(path)?;
let source_hash = hash_bytes(contents.as_bytes());
if chio_policy::is_hushspec_format(&contents) {
return load_hushspec_policy(path, source_hash);
}
let policy: ChioPolicy = serde_yml::from_str(&contents)?;
policy.kernel.validate()?;
let default_capabilities = build_runtime_default_capabilities(&policy)?;
let runtime_hash = runtime_hash_for_chio_yaml(&policy, &default_capabilities)?;
// …
}
The separate evaluate interpreter returns Allow, Warn, or Deny directly from the policy and runs none of the compiled guards. chio check builds a short-lived compiled kernel instead, and Testing Guards & Policies matches each test to a deployment. External cloud-guardrail and threat-intel adapters take the control plane's separate external-guard configuration.
Read more in Policy Compilation, with the complete serialized scope, the rule mapping, and the compiler entry points.
Schema version, top-level fields, bases, rule fields and defaults, conditions, extensions, metadata, and validation.
Schema version
hushspec: "0.1.0"The supported version is 0.1.0. The hushspec key identifies the format, and validation rejects any other value. A document is a YAML mapping, and unknown root keys are rejected. A version-only document is valid and carries no explicit restrictions.
Top-level fields
Only hushspec is required. The other fields configure behavior, choose a base, or describe the policy for people and deployment tooling.
| Field | Type | Required | What it carries |
|---|---|---|---|
hushspec | string | Yes | Required schema version: 0.1.0. |
name | string | No | Human-readable policy identifier. |
description | string | No | Free-form prose explaining intent. |
extends | string | No | One base reference. The filesystem resolver expects a local path. |
merge_strategy | enum | No | How this document folds into its parent. Defaults to deep_merge. |
rules | object | No | Per-guard configuration. |
extensions | object | No | Domain-specific add-ons: posture, origins, detection, reputation, runtime assurance, and the chio overlay. |
metadata | object | No | Governance fields for people and deployment tooling. The compiler reads none of them. |
Base policies
extends takes one reference string. resolve_from_path and create_composite_loader load local files, and a relative path resolves from the referring policy's directory. A parent may extend another file; resolution merges the chain back toward the child.
| Reference or API | What it loads | How to use it |
|---|---|---|
extends: base.yaml | A file beside the child policy | Ship that file with the child, as in the example archive. |
extends: ../base/team.yaml | A file relative to the child | Keep the directory relationship intact when deploying. |
Absolute filesystem path | The named local file | The default resolver accepts paths outside the project directory. |
rulesets:: | Embedded YAML for a named built-in | Accepts a bare name, chio: prefix or hushspec: prefix; returns YAML to the Rust host. |
rulesets:: | A parsed and compiled built-in | A separate Rust API; the filesystem loader still reads built-in names as paths. |
HTTP or HTTPS URL | Unsupported by the default loaders | Fetch and review the base as a local file first. |
Built-in names: ai-agent, cicd, default, panic, permissive, remote-desktop, strict. The team example includes builtin-default.yaml as a file, copied from the public default ruleset. Review that base together with the child, since a child can replace its restrictions.
hushspec: "0.1.0"
extends: "chio:default"chio policy explain hushspec-policies/wrong-builtin.yamlerror[CHIO-CLI-POLICY]: invalid policy: failed to read HushSpec document at hushspec-policies/chio:default: No such file or directory (os error 2) help: fix the policy entry the message names, then run the command again code: urn:chio:error:policy:compile-failed docs: https://chio.computer/docs/reference/errors#error-policy-compile-failed
The filesystem loader reads chio:default as a file name beside the child and fails, and the command exits 2 before any control exists.
Cycles, missing bases, and resolution limits
The resolver compares canonical source paths and rejects a cycle with its chain. Default limits are 4 MiB per document and 32 documents in the inheritance chain, counting the starting document. A missing base or an invalid parent also fails resolution, even when the child uses replace. Policy Compilation describes the resolver APIs and limits.
Rule blocks
The rules object holds 14 optional blocks. A missing block installs no guard for that rule, and enabled: false skips it. Capability defaults are a separate output: omitting tool_access can produce a permissive default scope, so inspect both the guard list and the scope before installing a policy.
| Block | Struct | Compiles to |
|---|---|---|
forbidden_paths | Forbidden | Forbidden |
path_allowlist | Path | Path |
egress | EgressRule | Egress |
secret_patterns | Secret | SecretLeakGuard for input checks; a SanitizerHook for output handling |
patch_integrity | Patch | Patch |
shell_commands | Shell | Shell |
tool_access | ToolAccessRule | McpToolGuard, plus the capability grants and their MaxArgsSize and Minimum |
computer_use | ComputerUseRule | Computer |
remote_ | Remote | Remote |
input_injection | Input | Input |
browser_ | Browser | Browser |
code_execution | Code | Code |
velocity | VelocityRule | VelocityGuard for invocation or spending limits; Agent |
human_in_loop | HumanInLoopRule | Constraint:: |
egress adds a companion internal-network check, so an allowed hostname still leaves private and metadata addresses refused. secret_patterns configures input checks and creates output handling, mapped in Policy Compilation.
Rule fields and defaults
These are the accepted wire names and Rust value types. A default applies when the enclosing block exists and the field is omitted; it never creates a missing block. Unknown rule or field names are rejected.
| Field | Type | Default |
|---|---|---|
forbidden_ | bool | true |
forbidden_ | Vec< | [] |
forbidden_ | Vec< | [] |
path_ | bool | false |
path_ | Vec< | [] |
path_ | Vec< | [] |
path_ | Vec< | [] |
egress. | bool | true |
egress. | Vec< | [] |
egress. | Vec< | [] |
egress. | Default | block |
secret_ | bool | true |
secret_ | Vec< | [] |
secret_ | Vec< | [] |
patch_ | bool | true |
patch_ | usize | 1000 |
patch_ | usize | 500 |
patch_ | Vec< | [] |
patch_ | bool | false |
patch_ | f64 | 10.0 |
shell_ | bool | true |
shell_ | Vec< | [] |
tool_ | bool | true |
tool_ | Vec< | [] |
tool_ | Vec< | [] |
tool_ | Vec< | [] |
tool_ | Default | allow |
tool_ | Option< | unset |
tool_ | Option< | unset |
tool_ | Option< | unset |
tool_ | Option< | unset |
tool_ | Option< | unset |
computer_ | bool | false |
computer_ | Computer | guardrail |
computer_ | Vec< | [] |
remote_ | bool | false |
remote_ | bool | false |
remote_ | bool | false |
remote_ | bool | true |
remote_ | bool | false |
input_ | bool | false |
input_ | Vec< | [] |
input_ | bool | false |
browser_ | bool | false |
browser_ | Vec< | [] |
browser_ | Vec< | [] |
browser_ | Vec< | [] |
browser_ | bool | true |
browser_ | Vec< | empty |
code_ | bool | false |
code_ | Vec< | [] |
code_ | Vec< | [] |
code_ | bool | false |
code_ | Option< | unset |
code_ | Option< | unset |
velocity. | bool | true |
velocity. | Option< | unset |
velocity. | Option< | unset |
velocity. | Option< | unset |
velocity. | Option< | unset |
velocity. | u64 | 60 |
velocity. | f64 | 1.0 |
human_ | bool | true |
human_ | Vec< | [] |
human_ | Option< | unset |
human_ | Option< | unset |
human_ | Option< | unset |
human_ | Human | deny |
These blocks default enabled to false: path_allowlist, computer_use, remote_desktop_channels, input_injection, browser_automation, code_execution. Set enabled: true when installing them; the remaining blocks default it to true. In remote_desktop_channels, audio defaults to permitted while clipboard, file transfer, and drive mapping default to disabled.
Tool access constraints
Permit a tool before asking for confirmation on it. This document allows quote and refund, blocks discount, limits arguments, and requires attestation. Its confirmation pattern matches the allowed refund grant, so every refund waits for an approver such as your pricing lead.
hushspec: "0.1.0"
name: scoped-pricing-tools
rules:
tool_access:
enabled: true
default: block
allow: [quote, refund]
block: [discount]
require_confirmation: [refund]
max_args_size: 4096
require_runtime_assurance_tier: attested
prefer_runtime_assurance_tier: verifiedchio policy explain hushspec-policies/tool-access.yamlpolicy hushspec-policies/tool-access.yaml
grants quote on server * max_args_size 4096, runtime assurance attested
refund on server * max_args_size 4096, approval required, runtime assurance attested
guards 1 mcp-tool
output none
approvals refund on every callmax_args_size becomes MaxArgsSize, and require_runtime_assurance_tier becomes MinimumRuntimeAssurance. A matching require_confirmation produces RequireApprovalAbove with threshold_units: 0; at admission, the kernel holds a call under it as pending until a person approves that exact request. prefer_runtime_assurance_tier is an interpreter preference and compiles to no second minimum.
Workload identity requirements
workload.yaml adds this requirement to the tool-access document. Replace the trust domain and permitted identity paths with your operator's values.
require_workload_identity:
scheme: spiffe
trust_domain: larkfield.example
path_prefixes: ["/agents/"]
credential_kinds: [x509_svid]chio policy explain hushspec-policies/workload.yamlpolicy hushspec-policies/workload.yaml grants none workload identity has no default-grant form guards 1 mcp-tool output none approvals none
The compiler emits no default grants when required or preferred workload identity is present. The policy interpreter evaluates these identity fields, and the compiler encodes none of them as capability constraints. Workload Identity covers the host integrations that supply and evaluate identity evidence. Credential kinds are uri, x509_svid, and jwt_svid; the supported scheme is spiffe.
Conditions
Conditions live outside the YAML. A Rust caller passes a map of rule names to Condition values to evaluate_with_context; compile_policy takes no such map. The interpreter's decision describes that evaluation, separate from the compiled kernel pipeline.
| Predicate | Input | Meaning |
|---|---|---|
time_window | start, end, optional timezone and days | Match the runtime time against a window. |
context | Key/value expectations | Match environment or values from the user, agent, session, request, deployment and custom maps. |
all_of | Nested conditions | Require every child condition. |
any_of | Nested conditions | For a nonempty list, require at least one child. |
not | One nested condition | Invert that condition's result. |
A false condition removes its rule from the effective policy and leaves the request to the remaining rules. Gating a deny-by-default tool rule on environment: production removes that rule whenever the environment is absent, so keep mandatory restrictions unconditional. Unknown rule names in the map are rejected with a denial, and recursion checks stop a nested evaluation beyond depth eight. Review composition, especially negation, as policy logic.
Extensions
The extensions object has a fixed set of sub-blocks, and unknown names are rejected. Each sub-block takes effect through the integration that reads it:
posture. Posture states, capabilities, budgets, and transition definitions. The compiler mapping names the installed enforcement path.origins. Per-origin profiles with their owntool_accessandegressoverrides; the default behavior isdenyorminimal_profile.detection. Prompt-injection, jailbreak, and threat-intel detectors, regex and similarity based.reputation. Reputation tiers, scoring weights, promotion and demotion triggers, and metrics requirements.runtime_assurance. Runtime assurance tier and verifier configuration, interpreted by the runtime integration.chio. Threshold approver configuration, plus descriptive integration fields for market hours, signing, namespaces, and rollback.
Within extensions.chio, market_hours, signing, k8s_namespaces, and rollback are carried configuration that compiles to no runtime guard. human_in_loop.approvers differs: the compiler requires an authenticated approver directory and refuses the plain compile entry point when that configuration is present. Use the threshold-aware compiler entry point.
Governance metadata
Metadata records who owns a policy and how it was reviewed, for people and deployment systems. The compiler reads none of it: approved_by is a label rather than an authenticated approval, and effective_date and expiry_date neither schedule activation nor revoke issued authority.
| Field | Type | Notes |
|---|---|---|
author | string | Free-form. |
approved_by | string | Free-form. |
approval_date | string | RFC 3339 recommended. |
classification | enum | public | internal | confidential | restricted |
change_ticket | string | Reference to your change- |
lifecycle_state | enum | draft | review | approved | deployed | deprecated | archived |
policy_version | int | Internal version counter, separate from hushspec. |
effective_date | string | Recorded intended start date. |
expiry_date | string | Recorded intended expiry date. |
Team policy example
This document combines a project tool allowlist, filesystem boundaries, egress rules, secret detection, and velocity limits. The export tool is allowed explicitly and matched by confirmation. Its local base, builtin-default.yaml, ships in the archive.
hushspec: "0.1.0"
name: larkfield-pricing-data
description: Pricing analysis with scoped read, query, and export tools.
extends: builtin-default.yaml
merge_strategy: deep_merge
rules:
tool_access:
enabled: true
default: block
allow:
- read_file
- list_directory
- search_files
- sql
- export_to_csv
block:
- run_command
require_confirmation:
- export_to_csv
max_args_size: 8192
forbidden_paths:
enabled: true
patterns:
- "**/.env*"
- "**/secrets/**"
- "**/.aws/credentials"
- "**/id_rsa*"
exceptions:
- "/workspace/.env.testing"
path_allowlist:
enabled: true
read:
- "/workspace/src/**"
- "/workspace/config/*.yaml"
write: ["/workspace/exports/**"]
patch: []
egress:
enabled: true
default: block
allow:
- "pricing.larkfield.example"
- "pypi.org"
secret_patterns:
enabled: true
patterns:
- name: aws_access_key
pattern: "AKIA[0-9A-Z]{16}"
severity: critical
- name: larkfield-api-key
pattern: "lark_live_[A-Za-z0-9]{24}"
severity: error
velocity:
enabled: true
max_invocations_per_window: 200
window_secs: 60
burst_factor: 1.5
human_in_loop:
enabled: true
require_confirmation:
- "export_*"
timeout_seconds: 300
on_timeout: deny
extensions:
chio:
market_hours:
tz: "America/New_York"
open: "09:30"
close: "16:00"
days: [Mon, Tue, Wed, Thu, Fri]
metadata:
author: "larkfield-pricing"
approved_by: "Mara Okafor"
classification: confidential
lifecycle_state: deployed
policy_version: 7
effective_date: "2026-10-01"chio policy explain hushspec-policies/team-policy.yamlpolicy hushspec-policies/team-policy.yaml extends hushspec-policies/builtin-default.yaml
grants read_file on server * max_args_size 8192
list_directory on server * max_args_size 8192
search_files on server * max_args_size 8192
sql on server * max_args_size 8192
export_to_csv on server * max_args_size 8192, approval required
guards 1 forbidden-path
2 velocity
3 egress-allowlist
4 internal-network
5 mcp-tool
6 secret-leak
7 patch-integrity
8 path-allowlist
output 1 hook
approvals export_to_csv on every callchio policy explain compiles the document only; chio policy explain --tool <T> --params <JSON> walks one call through the guards in order. Adapt the tools, paths, and domains to your application, then send allowed and refused requests through its host. The child's forbidden_paths replaces the base's entire list, so base entries it omits, such as **/.ssh/**, no longer apply. The market-hours overlay and the metadata are carried as description and set no trading window or activation date.
Validation failures
| Failure | Layer | Action |
|---|---|---|
Unknown field or rule name | Parsing | Use the exact names in this reference. conditions belongs to a separate API. |
Unsupported hushspec version | Validation | Use the supported schema version; a recognized format can still fail validation. |
Scalar or sequence instead of a mapping | Parsing | Start with a YAML object containing hushspec. |
Oversized quoted whitespace run | Parser precheck | Correct the scalar; the precheck bounds pathological parser input. |
Missing base, cycle, URL or resolution limit | Resolution | Ship local bases, break the cycle and keep the chain within limits. |
Invalid or unsafe regex | Validation or compilation | Correct patterns in secret_patterns, shell_commands or patch_integrity. |
Invalid default action or severity | Parsing | Default actions are allow or block. A secret pattern requires severity: critical, error or warn. |
Undefined posture state | Validation | Define the initial and transition states. A transition cannot target *. |
Threshold approvers without an authenticated directory | Compilation | Use the threshold-aware compiler with the operator's directory. |
Parsing and validation check structure and references. Validation does not link every origin profile to a posture state or enforce metadata dates, so inspect the resolved scope and exercise application requests, including refusals, before deploying a change.
The field inventory is generated from the public rule definitions. chio policy explain lists the controls a document compiles to; a signed receipt comes from executing a call, as Testing Guards & Policies shows.