Pre-Approval Policies

Every secret use requires an active pre-approval policy that matches the (secret, tool, host) triple. Without a matching policy, the request is denied, even if the agent has the secret.use:<name> capability.

Policies answer the question: "Is this agent allowed to use secret X with tool Y to reach host Z?"

Policies are persisted to SQLite and survive daemon restarts. No re-registration is required after restarting agentd.

Creating a Policy

ash secrets policy add \
  --label "OpenRouter API access" \
  --secret "openrouter-key" \
  --tool "web.fetch" \
  --host "openrouter.ai"

All fields:

ash secrets policy add \
  --label "Nightly analytics batch" \
  --secret "analytics-api-key" \    # glob: which secrets
  --tool "web.fetch" \              # glob: which tools
  --host "api.analytics.example.com" \   # optional: which host
  --expires "2026-12-31T00:00:00Z" \     # optional: expiry
  --max-uses 100 \                       # optional: use limit
  --trust-level "trusted"                # optional: agent trust level floor

Policy Fields

FieldTypeDescription
--labelstringHuman-readable name for audit entries
--secretglobMatches secret names (openai-*, db-password, *)
--toolglobMatches tool names (web.fetch, sandbox.exec, *)
--hostglobOptional. Matches destination host for network tools
--expiresRFC3339Optional. Policy stops applying after this time
--max-usesintegerOptional. Policy disabled after N auto-approvals
--trust-levellevelOptional. Only applies to agents at this trust level or above

Listing Policies

ash secrets policy list
# ID                                    LABEL                     SECRET        TOOL       EXPIRES
# a1b2c3d4-...                          OpenRouter API access      openrouter-*  web.fetch  never
# e5f6a7b8-...                          Nightly analytics batch    analytics-*   web.fetch  2026-12-31

Inspecting a Single Policy

ash secrets policy show a1b2c3d4-e5f6-...
# ID:          a1b2c3d4-e5f6-...
# Label:       OpenRouter API access
# Secret:      openrouter-*
# Tool:        web.fetch
# Host:        openrouter.ai
# Expires:     never
# Max uses:    unlimited
# Use count:   42
# Created by:  operator
# Created at:  2026-01-15T10:00:00Z

Removing a Policy

ash secrets policy remove <policy-id>

Immediately revokes the policy. Subsequent secret resolutions that would have matched this policy are denied.

Manifest-Declared Policies

Policies can be declared in the manifest so they are applied automatically at spawn time:

spec:
  capabilities:
    - secret.use:openrouter-key
  secret_policy:
    - label: "LLM completions"
      secret_pattern: "openrouter-key"
      tool_pattern: "web.fetch"
      host_pattern: "openrouter.ai"
      max_uses: 1000

Manifest-level policies are scoped to the specific agent spawned from that manifest. Runtime policies created via ash secrets policy add can optionally be scoped by agent_matcher.

Policy Matching

When an agent uses a secret handle in a tool call, agentd checks all active policies for a match:

  1. Does secret_pattern match the secret name?
  2. Does tool_pattern match the tool being invoked?
  3. If host_pattern is set, does it match the destination host in the request?
  4. Is the policy not expired?
  5. Is the policy under its max_uses limit?
  6. If agent_matcher is set, does the calling agent match?

If all matching conditions pass, the policy approves the use, increments its use_count, and records the policy ID in the audit trail.

If no policy matches, the request is denied with NoPolicyMatch and the event is logged as a warning. Full HITL queue routing for unmatched requests is planned for a future phase.