Chio/Docs

Computer Use Agent Guards

Five guards in chio-guards work the CUA surface: a coarse allowlist of action types, a fine-grained gate on input injection, per-channel toggles for remote-desktop side channels, a browser-automation gate with credential detection, and a cosine-similarity anomaly detector. They compose conjunctively so a coarse pass does not weaken a fine-grained deny.

Verdict shape

Every guard on this page returns Verdict::Allow or Verdict::Deny. The CUA surface does not produce Verdict::PendingApproval; approval is a separate guard. See Approval & HITL.

ComputerUseGuard

Source: crates/chio-guards/src/computer_use.rs. Coarse gate that recognises three CUA surfaces:

  • Remote-session and side-channel actions whose tool name or action_type / custom_type argument starts with remote. or input..
  • Browser navigation verbs (navigate, goto, open) checked against a blocked-domain list and an optional allowed-domain list.
  • Screenshot-family verbs rate-limited via a token bucket (screenshot, screen_capture, screen_shot, capture, capture_screen, browser_screenshot).

Config

ComputerUseConfig fields:

KnobTypeDefaultPurpose
enabledbooltrueMaster switch. Disabled guard returns Allow.
allowed_action_typesVec<String>10 entries (see below)Allowlist for remote.* / input.* action types.
modeEnforcementModeGuardrailObserve / Guardrail / FailClosed.
blocked_domainsVec<String>emptyHosts denied for navigation. Supports *.suffix.
allowed_domainsVec<String>emptyWhen non-empty, only listed hosts pass under FailClosed.
screenshot_rate_per_secondOption<f64>NoneRefill rate. None disables rate limiting.
screenshot_burstOption<u32>Some(5) when rate is setToken-bucket burst capacity.

Default allowed action types

crates/chio-guards/src/computer_use.rs
pub fn default_allowed_action_types() -> Vec<String> {
    vec![
        "remote.session.connect".into(),
        "remote.session.disconnect".into(),
        "remote.session.reconnect".into(),
        "input.inject".into(),
        "remote.clipboard".into(),
        "remote.file_transfer".into(),
        "remote.audio".into(),
        "remote.drive_mapping".into(),
        "remote.printing".into(),
        "remote.session_share".into(),
    ]
}

Enforcement modes

The mode determines how the allowlist outcome maps to a verdict:

ModeIn allowlistOut of allowlist
ObserveAllowAllow (logged)
GuardrailAllowAllow (warning)
FailClosedAllowDeny

Guardrail does not block

Guardrail mode is the default and never returns Deny on the action-type allowlist path. It emits a warning. Operators that want hard enforcement on this surface must set mode: fail_closed. The navigation and screenshot paths are stricter (Guardrail denies on a blocked domain or empty bucket).

Detection algorithm

  • Step 1: extract a CUA action type. Tool name itself if it begins with remote. or input.; otherwise an action_type / actionType / custom_type / customType argument with the same prefix. If found, apply the mode mapping against the action-type allowlist.
  • Step 2: extract the action via extract_action. If it is ToolAction::BrowserAction, first match against the screenshot verb set; on a screenshot, consume a token from the bucket. On nav verbs, parse the host and check the blocked / allowed lists.
  • Step 3: anything else passes with Verdict::Allow.

Failure modes

  • Token-bucket mutex poisoning collapses to "no tokens" so the rate-limiter denies under Guardrail / FailClosed.
  • Opaque navigation targets (CSS selectors, data URIs, about:, javascript:) are passed through. Fine-grained domain checks belong to BrowserAutomationGuard.
  • Mixed-case schemes, scheme-relative URLs (//host), userinfo segments (user@host), and bracketed IPv6 hosts all parse to the bare host so wildcards match correctly.

InputInjectionCapabilityGuard

Source: crates/chio-guards/src/input_injection.rs. Fine-grained gate that fires when tool_name == "input.inject" / "input_inject", when an action_type / custom_type argument equals input.inject, or when a recognised tool name (keyboard, mouse, touch, input) carries an input_type field.

Config

KnobTypeDefaultPurpose
enabledbooltrueMaster switch.
allowed_input_typesVec<String>["keyboard", "mouse", "touch"]Permitted input_type values.
require_postcondition_probeboolfalseWhen true, requires a non-empty postcondition_probe_hash.
strictbooltrueDeny calls that look like injection but lack input_type.

Failure modes

  • input_type outside the allowlist: Deny.
  • Missing input_type on an injection-flagged call with strict = true: Deny; otherwise Allow.
  • require_postcondition_probe set and the call is missing both postcondition_probe_hash and postconditionProbeHash: Deny. Empty strings count as missing.

Postcondition probes bind blind action

Setting require_postcondition_probe forces the agent to attest a hash (typically a screenshot digest) it will verify after the injection. The hash binds the action to a verification step, so the agent cannot inject keystrokes without committing to confirm the result.

RemoteDesktopSideChannelGuard

Source: crates/chio-guards/src/remote_desktop.rs. Per-channel toggles for six named side channels, plus a transfer-size ceiling. Session-lifecycle actions (remote.session.connect, remote.session.disconnect, remote.session.reconnect) are not claimed here; they belong to ComputerUseGuard.

ChannelAction typeConfig field
Clipboardremote.clipboardclipboard_enabled
File transferremote.file_transferfile_transfer_enabled
Session shareremote.session_sharesession_share_enabled
Audioremote.audioaudio_enabled
Drive mappingremote.drive_mappingdrive_mapping_enabled
Printingremote.printingprinting_enabled

Every channel toggle defaults to true. The max_transfer_size_bytes knob is Option<u64> and defaults to None. When set, remote.file_transfer calls must include a transfer_size / transferSize u64 argument. Missing or non-integer transfer size with a configured ceiling: Deny.

Unknown remote.* channels deny

The default branch is fail-closed. A new remote.webrtc action type the kernel has never seen returns Deny rather than silently passing. New side channels need a config update before they go live.

BrowserAutomationGuard

Source: crates/chio-guards/src/browser_automation.rs. Three policy axes on ToolAction::BrowserAction:

  • Verb allowlist. Empty means "any verb"; defaults to a read-only set.
  • Navigation domain gating with separate blocked and allowed lists.
  • Credential detection on type / input verbs via built-in regex patterns plus operator-supplied extras.

Default allowed verbs

crates/chio-guards/src/browser_automation.rs
pub fn default_allowed_verbs() -> Vec<String> {
    vec![
        "navigate".into(),
        "goto".into(),
        "open".into(),
        "screenshot".into(),
        "screen_capture".into(),
        "capture".into(),
        "browser_screenshot".into(),
        "get_url".into(),
        "get_title".into(),
        "read".into(),
        "get_content".into(),
        "close".into(),
        "back".into(),
        "forward".into(),
        "reload".into(),
    ]
}

The default verb set is read-only. Verbs like type, click, fill, and submit_form are denied unless added explicitly. Operators that want a writeable session need to widen allowed_verbs and accept the credential-detection cost on every type call.

Config

KnobTypeDefaultPurpose
enabledbooltrueMaster switch.
allowed_domainsVec<String>emptyWhen set, the only hosts the agent may navigate to.
blocked_domainsVec<String>emptyAlways denied. Evaluated before the allowlist.
allowed_verbsVec<String>15-entry read-only setPermitted action verbs. Empty means any verb.
credential_detectionbooltrueRun regex detection on type / input action text.
extra_credential_patternsVec<String>emptyOperator regex patterns layered on top of built-ins.

Built-in credential patterns

Compiled once via OnceLock. Cover AWS access keys (AKIA / ASIA + 16 chars), GitHub PATs (gh[pousr]_*), Slack tokens (xox[abopsr]-*), JWT shapes (eyJ*.*.* ), PEM private-key headers, generic password = ... / token = ... assignments, OpenAI sk-* keys, and Stripe sk_live_* / sk_test_* keys.

Selectors are not URLs

The guard treats values starting with #, ., [, single /, or xpath= as CSS selectors and skips navigation gating on them. URLs like //host still parse and check.

SpiderSenseGuard

Source: crates/chio-guards/src/spider_sense.rs. Cosine-similarity anomaly detector over a pre-loaded pattern database of known-threat embeddings.

Detection algorithm

  1. Extract a query embedding from the arguments. Preferred shapes: embedding (array of numbers), vector, or embeddings (list of vectors, mean-pooled).
  2. If no embedding is present: Allow.
  3. If the embedding has the wrong dimension or non-finite values: Deny.
  4. Score the request via cosine similarity against every pattern in the DB. Hand-rolled f64 accumulation, no BLAS dependency.
  5. Apply the threshold band: top score ≥ threshold + ambiguity_band: Deny. Top score ≤ threshold - ambiguity_band: Allow. In-band: dispatch on the configured ambiguous_policy.

Config

KnobTypeDefaultPurpose
similarity_thresholdf640.85Center of the deny / allow band.
ambiguity_bandf640.10Half-width of the band.
top_kusize5Top-K matches retained per query.
ambiguous_policyAmbiguousPolicyAllowVerdict for in-band scores.

The pattern DB is loaded at construction time (PatternDb::from_json or SpiderSenseGuard::from_json_file). It must be non-empty, have a uniform non-zero dimensionality, and carry only finite values. Construction errors surface as SpiderSenseError::Parse, SpiderSenseError::Invalid, or SpiderSenseError::Config.

Evidence

Each PatternEntry carries an id, category (e.g. prompt_injection), stage (perception / cognition / action / feedback), a human-readable label, and the embedding vector. The guard scans the full DB for the maximum cosine; receipts emit the top scoring entry alongside the threshold and band.


Performance class

Cost ordering, lowest first:

GuardHot pathClass
RemoteDesktopSideChannelGuardSet lookup + optional u64 compareO(1)
InputInjectionCapabilityGuardHashSet lookup on input typeO(1)
ComputerUseGuardAction-type set + URL parse + token bucketO(1) amortised
BrowserAutomationGuardVerb set + URL parse + 8 regex passes on type textO(N regex) per type call
SpiderSenseGuardCosine over the full pattern DBO(N·D)

HushSpec snippet

policy.yaml
hushspec: "0.1.0"
guards:
  cua:
    computer_use:
      enabled: true
      mode: fail_closed
      blocked_domains:
        - "169.254.169.254"
        - "*.internal.example"
      allowed_domains:
        - "*.corp.example"
      screenshot_rate_per_second: 2.0
      screenshot_burst: 5
    input_injection:
      enabled: true
      allowed_input_types: ["keyboard", "mouse"]
      require_postcondition_probe: true
      strict: true
    remote_desktop:
      enabled: true
      clipboard_enabled: false
      file_transfer_enabled: true
      session_share_enabled: false
      audio_enabled: false
      drive_mapping_enabled: false
      printing_enabled: false
      max_transfer_size_bytes: 1048576
    browser_automation:
      enabled: true
      allowed_domains:
        - "*.corp.example"
      blocked_domains:
        - "169.254.169.254"
      allowed_verbs:
        - "navigate"
        - "screenshot"
        - "get_content"
        - "type"
      credential_detection: true
    spider_sense:
      enabled: true
      pattern_db_path: "/etc/chio/spider_sense_patterns.json"
      similarity_threshold: 0.85
      ambiguity_band: 0.10
      top_k: 5
      ambiguous_policy: deny

Next steps

  • Approval & HITL for human escalation on CUA actions that exceed automatic policy.
  • Network Guards for the broader egress and SSRF posture that browser nav inherits.
  • Advisory Guards for non-blocking observation of CUA patterns before promotion.
Computer Use Agent Guards · Chio Docs