Chio/Docs
LOGIN · JOIN

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

yaml
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.

FieldTypeRequiredHolds
hushspecStringyesSchema version. The accepted value is 0.1.0.
nameOption<String>noPolicy name. A merged document takes the base's name when the child has none.
descriptionOption<String>noFree text. A merged document takes the base's description when the child has none.
extendsOption<String>noPath of the base document. Resolved as described under Inheritance.
merge_strategyOption<MergeStrategy>noHow the child composes with its base: replace, merge, or deep_merge. Default deep_merge.
rulesOption<Rules>noThe rule blocks. Each block is optional.
extensionsOption<Extensions>noposture, origins, detection, reputation, runtime_assurance, and chio.
metadataOption<GovernanceMetadata>noGovernance 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.

TypeValuesUsed by
MergeStrategyreplace, merge, deep_merge (default)merge_strategy
DefaultActionallow, blockegress.default, tool_access.default
Severitycritical, error, warnsecret_patterns.patterns[].severity
ComputerUseModeobserve, guardrail (default), fail_closedcomputer_use.mode
HumanInLoopTimeoutActiondeny (default), deferhuman_in_loop.on_timeout
RuntimeAssuranceTiernone, basic, attested, verifiedtool_access.require_runtime_assurance_tier, tool_access.prefer_runtime_assurance_tier, runtime_assurance tiers and verifiers
WorkloadIdentitySchemespiffeWorkloadIdentityMatch.scheme
WorkloadCredentialKinduri, x509_svid, jwt_svidWorkloadIdentityMatch.credential_kinds
TransitionTriggeruser_approval, user_denial, critical_violation, any_violation, timeout, budget_exhausted, pattern_matchposture.transitions[].on
OriginDefaultBehaviordeny (default), minimal_profileorigins.default_behavior
DetectionLevelsafe, suspicious, high, criticaldetection.prompt_injection.warn_at_or_above, detection.prompt_injection.block_at_or_above
Classificationpublic, internal, confidential, restrictedmetadata.classification
LifecycleStatedraft, review, approved, deployed, deprecated, archivedmetadata.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.

FieldTypeRequiredDefault
enabledboolnotrue
patternsVec<String>no[]
exceptionsVec<String>no[]
yaml
rules:
  forbidden_paths:
    patterns:
      - "**/.env"
      - "**/.env.*"
      - "**/.ssh/**"
    exceptions:
      - "**/.env.example"

path_allowlist

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

FieldTypeRequiredDefault
enabledboolnofalse
readVec<String>no[]
writeVec<String>no[]
patchVec<String>no[]
yaml
rules:
  path_allowlist:
    enabled: true
    read:
      - ./workspace/**
      - ./docs/**
    write:
      - ./workspace/output/**
    patch:
      - ./workspace/src/**

egress

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

FieldTypeRequiredDefault
enabledboolnotrue
allowVec<String>no[]
blockVec<String>no[]
defaultDefaultActionnoblock
yaml
rules:
  egress:
    allow:
      - api.github.com
      - "*.npmjs.org"
    block: []
    default: block

secret_patterns

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

FieldTypeRequiredDefault
enabledboolnotrue
patternsVec<SecretPattern>no[]
skip_pathsVec<String>no[]

Each entry of patterns is a SecretPattern:

FieldTypeRequiredHolds
nameStringyesIdentifier, unique within the list.
patternStringyesA regular expression.
severitySeverityyescritical, error, or warn.
descriptionOption<String>noFree text.
yaml
rules:
  secret_patterns:
    patterns:
      - name: aws_access_key
        pattern: "AKIA[0-9A-Z]{16}"
        severity: critical
      - name: github_token
        pattern: "gh[ps]_[A-Za-z0-9]{36}"
        severity: critical
    skip_paths:
      - "**/tests/**"

patch_integrity

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

FieldTypeRequiredDefault
enabledboolnotrue
max_additionsusizeno1000
max_deletionsusizeno500
forbidden_patternsVec<String>no[]
require_balanceboolnofalse
max_imbalance_ratiof64no10.0
yaml
rules:
  patch_integrity:
    max_additions: 500
    max_deletions: 200
    require_balance: true
    max_imbalance_ratio: 5.0
    forbidden_patterns:
      - "(?i)eval\\s*\\("
      - "(?i)exec\\s*\\("

shell_commands

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

FieldTypeRequiredDefault
enabledboolnotrue
forbidden_patternsVec<String>no[]
yaml
rules:
  shell_commands:
    forbidden_patterns:
      - "(?i)rm\\s+-rf\\s+/"
      - "curl.*\\|.*bash"
      - "wget.*\\|.*bash"

tool_access

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

FieldTypeRequiredDefault
enabledboolnotrue
allowVec<String>no[]
blockVec<String>no[]
require_confirmationVec<String>no[]
defaultDefaultActionnoallow
max_args_sizeOption<usize>nounset
require_runtime_assurance_tierOption<RuntimeAssuranceTier>nounset
prefer_runtime_assurance_tierOption<RuntimeAssuranceTier>nounset
require_workload_identityOption<WorkloadIdentityMatch>nounset
prefer_workload_identityOption<WorkloadIdentityMatch>nounset

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.
yaml
rules:
  tool_access:
    default: block
    allow:
      - read_file
      - list_directory
      - search
    block:
      - shell_exec
    require_confirmation:
      - write_file
    max_args_size: 65536
    require_runtime_assurance_tier: attested

computer_use

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

FieldTypeRequiredDefault
enabledboolnofalse
modeComputerUseModenoguardrail
allowed_actionsVec<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.

FieldTypeRequiredDefault
enabledboolnofalse
clipboardboolnofalse
file_transferboolnofalse
audioboolnotrue
drive_mappingboolnofalse

input_injection

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

FieldTypeRequiredDefault
enabledboolnofalse
allowed_typesVec<String>no[]
require_postcondition_probeboolnofalse

browser_automation

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

FieldTypeRequiredDefault
enabledboolnofalse
allowed_domainsVec<String>no[]
blocked_domainsVec<String>no[]
allowed_verbsVec<String>no[]
credential_detectionboolnotrue
extra_credential_patternsVec<String>no[]

code_execution

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

FieldTypeRequiredDefault
enabledboolnofalse
language_allowlistVec<String>no[]
module_denylistVec<String>no[]
network_accessboolnofalse
max_execution_time_msOption<u64>nounset
max_scan_bytesOption<usize>nounset

velocity

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

FieldTypeRequiredDefault
enabledboolnotrue
max_invocations_per_windowOption<u32>nounset
max_spend_per_windowOption<u64>nounset
max_requests_per_agentOption<u32>nounset
max_requests_per_sessionOption<u32>nounset
window_secsu64no60
burst_factorf64no1.0

human_in_loop

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

FieldTypeRequiredDefault
enabledboolnotrue
require_confirmationVec<String>no[]
approve_aboveOption<u64>nounset
approve_above_currencyOption<String>nounset
timeout_secondsOption<u64>nounset
on_timeoutHumanInLoopTimeoutActionnodeny
yaml
rules:
  velocity:
    max_requests_per_agent: 600
    window_secs: 60
    burst_factor: 1.5

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

Built-in rulesets

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

NameSets
ai-agentThe 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).
cicdForbids 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.
defaultThe file is quoted in full after the table.
panicDeny-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.
permissiveEgress allows * with default: allow. Patch integrity at 10000 additions and 5000 deletions, no balance, ratio 50.0. No other block.
remote-desktopcomputer_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.
strictThe 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:

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

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

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

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

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

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

Inheritance

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

The filesystem resolver does not consult 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.

StrategyRulesExtensionsname, description, metadata
replaceThe child's, as written. The base contributes nothing.The child's, as written.The child's, as written.
mergeA 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_mergeAs 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.
yaml
hushspec: "0.1.0"
name: hardened-agent
extends: ./base.yaml
merge_strategy: deep_merge

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

The 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.

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

origins

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

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

yaml
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-blockKeysCompiles to
prompt_injectionenabled, warn_at_or_above, block_at_or_above, max_scan_bytesThe 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.
jailbreakenabled, block_threshold, warn_threshold, max_input_bytesThe jailbreak guard. block_threshold divided by 100, capped at 100, becomes its threshold; max_input_bytes becomes the detector's scan limit.
threat_intelenabled, pattern_db, similarity_threshold, top_kThe 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.

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

reputation

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

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

runtime_assurance

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

chio

A Chio-specific block. The compiler reads human_in_loop.approvers from it, described under Threshold approvers. The rest of the block stays verbatim in the parsed document; the kernel does not interpret it.

FieldTypeKeys
market_hoursChioMarketHourstz, open, and close required; days list.
signingChioSigningalgo required; required, default true; optional key_ref.
k8s_namespacesChioK8sNamespacesallow, human_in_loop, and deny lists.
rollbackChioRollbackon_guard_fail and on_timeout, default false; optional strategy.
human_in_loopChioHumanInLoopAdvancedapprove_when list; optional approvers with n required, of, and timeout_seconds.

Governance metadata

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

FieldTypeHolds
authorStringPolicy author.
approved_byStringApprover name or id.
approval_dateStringDate of approval.
classificationClassificationpublic, internal, confidential, or restricted.
change_ticketStringChange-management ticket reference.
lifecycle_stateLifecycleStatedraft, review, approved, deployed, deprecated, or archived.
policy_versionusizeVersion number.
effective_dateStringDate the policy takes effect.
expiry_dateStringDate the policy expires.
yaml
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

BlockGuardNotes
forbidden_pathsForbiddenPathGuard (forbidden-path)Built-in patterns when patterns is empty.
velocityVelocityGuard (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_commandsShellCommandGuard (shell-command)Default forbidden set when forbidden_patterns is empty.
egressEgressAllowlistGuard (egress-allowlist), InternalNetworkGuard (internal-network)default is applied by the policy evaluator, not by the guard.
tool_accessMcpToolGuard (mcp-tool)allow, block, default, and max_args_size; the other fields shape the default scope.
secret_patternsSecretLeakGuard (secret-leak)Plus a SanitizerHook on the post-invocation pipeline with the same patterns.
patch_integrityPatchIntegrityGuard (patch-integrity)Every field carried.
path_allowlistPathAllowlistGuard (path-allowlist)read, write, and patch become the guard's access, write, and patch allowlists.
computer_useComputerUseGuard (computer-use)mode maps to the guard's enforcement mode.
remote_desktop_channelsRemoteDesktopSideChannelGuard (remote-desktop-side-channel)Session sharing, printing, and transfer size stay at the guard's defaults.
input_injectionInputInjectionCapabilityGuard (input-injection-capability)allowed_types becomes the guard's input-type allowlist.
browser_automationBrowserAutomationGuard (browser-automation)Every field carried.
code_executionCodeExecutionGuard (code-execution)max_scan_bytes overrides the guard default only when set.
human_in_loopnoneNo guard. RequireApprovalAbove constraints on the default scope.
extensions.detection.prompt_injectionPromptInjectionGuard (prompt-injection)enabled defaults to true when the sub-block is present.
extensions.detection.jailbreakJailbreakGuard (jailbreak)block_threshold divided by 100 becomes the guard threshold.
extensions.detection.threat_intelEmbeddingAnomalyGuard (embedding-anomaly)pattern_db is read at compile time, relative to the policy file.
extensions.origins.profiles[].budgets.tool_callsAgentVelocityGuard (agent-velocity)The tightest value across profiles, over a 60 second window with burst factor 1.0.
extensions.chio.human_in_loop.approversnoneNo 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.

FieldTypeCompiles to
approvers.nu32, requiredThe quorum. Refused when 0 or larger than the resolved approver set.
approvers.ofVec<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_secondsOption<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.

yaml
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

Policy Schema Reference · Chio Docs