Oxvault Oxvault

Policy Guide

Write policy.json rules for the Gateway — schema, matching semantics, and examples.

The Gateway enforces a JSON policy file loaded at startup. When no .oxvault/policy.json exists, a built-in default policy applies. Create the default on disk to edit it:

oxvault-gw init      # writes .oxvault/policy.json

Point the Gateway at a specific file with --policy <path>.

File format

{
  "version": 1,
  "default_action": "allow",
  "rules": [
    {
      "id": "block-ssh-keys",
      "enabled": true,
      "description": "Block access to SSH private keys",
      "match": {
        "tool": "*",
        "arguments_contain": ["/.ssh/id_"],
        "arguments_match": ["regex pattern"],
        "response_contain": ["substring"],
        "response_match": ["regex pattern"],
        "method": "tools/call",
        "direction": "client_to_server"
      },
      "action": "block",
      "alert": true
    }
  ]
}

Top level

FieldTypeDescription
versionintSchema version (currently 1)
default_actionstringAction applied when no rule matches: allow or block
rulesarrayOrdered list of rules

Rule

FieldTypeDescription
idstringRule identifier, surfaced in the audit log
enabledboolDisabled rules are skipped entirely
descriptionstringHuman-readable explanation (shown in alerts/audit)
matchobjectConditions under which the rule fires
actionstringallow or block (redact is planned but not yet implemented)
alertboolEmit an alert whenever the rule matches, even if the action is allow

match conditions

FieldTypeFires when…
toolstring (glob)The tools/call tool name matches the glob (* matches all)
arguments_containstring[]Any argument value contains one of these substrings
arguments_matchstring[]Any argument value matches one of these regexes
response_containstring[]The serialized response contains one of these substrings
response_matchstring[]The serialized response matches one of these regexes
methodstring (glob)The JSON-RPC method matches (for non-tool messages)
directionstringLimit to a direction: client_to_server, server_to_client, or "" (both)

Matching semantics

  • Rules are evaluated in order; the first match wins. Put your most specific rules first.
  • Within a rule, all non-empty match fields must pass (AND). A rule with both a tool glob and arguments_contain only fires when the tool matches and an argument matches.
  • Within a list field, any one element is enough (OR). arguments_contain: ["a", "b"] fires if an argument contains a or b.
  • Matching is case-sensitive. Substrings are literal; *_match fields are Go regular expressions, compiled once at startup (an invalid regex fails policy load).
  • Tool and method globs use filepath.Match semantics, with * as a special-cased “match everything”.
  • When no rule matches, default_action is applied.

Default policy

If you never run oxvault-gw init, these five rules apply. They block the most common credential-theft argument patterns and alert on credential-shaped values in responses:

Rule IDActionFires on
block-ssh-keysblock + alertArguments containing /.ssh/id_, /.ssh/authorized_keys, or /.ssh/config
block-aws-credentialsblock + alertArguments containing /.aws/credentials or /.aws/config
block-env-filesblock + alertArguments containing .env
block-mcp-config-accessblock + alertArguments containing /mcp.json, /.cursor/mcp, or /claude_desktop_config
alert-credential-in-responseallow + alertA response matching an AWS key (AKIA…), OpenAI key (sk-…), GitHub PAT (ghp_…), or PEM private key header

alert-credential-in-response is server_to_client only and its action is allow — responses are never blocked, only surfaced, which matches the Gateway’s runtime inspection order.

How policy relates to --scanner-block-severity

The policy engine and the scanner run as separate stages (policy first, then the scanner). The policy engine gives you exact, declarative control over specific tools, arguments, and responses. --scanner-block-severity controls the broader, pattern-based scanner rules (injection, secrets, poisoning) by severity. A message can be blocked by either:

  • Policy block → immediate JSON-RPC error, regardless of scanner settings.
  • Scanner finding at or above --scanner-block-severity → blocked (arguments) or alerted (responses are alert-only).

Use the policy for precise allow/deny rules you can reason about, and the scanner threshold as a severity-based safety net. See the Gateway inspection order.

Worked examples

Deny a tool entirely:

{
  "id": "deny-shell-tool",
  "enabled": true,
  "description": "This server's run_shell tool is never allowed",
  "match": { "tool": "run_shell" },
  "action": "block",
  "alert": true
}

Block writes to a sensitive directory across all tools:

{
  "id": "block-etc-writes",
  "enabled": true,
  "description": "Block any argument referencing /etc/",
  "match": {
    "tool": "*",
    "arguments_match": ["/etc/(passwd|shadow|sudoers)"]
  },
  "action": "block",
  "alert": true
}

Alert (but allow) when a response looks like it contains a JWT:

{
  "id": "alert-jwt-in-response",
  "enabled": true,
  "description": "Surface JWTs returned by the server",
  "match": {
    "direction": "server_to_client",
    "response_match": ["eyJ[A-Za-z0-9_-]+\\.eyJ[A-Za-z0-9_-]+\\."]
  },
  "action": "allow",
  "alert": true
}

Default-deny posture — flip default_action to block and add explicit allow rules for the tools you trust:

{
  "version": 1,
  "default_action": "block",
  "rules": [
    { "id": "allow-read", "enabled": true, "description": "Allow read_file", "match": { "tool": "read_file" }, "action": "allow" },
    { "id": "allow-list", "enabled": true, "description": "Allow list_dir", "match": { "tool": "list_dir" }, "action": "allow" }
  ]
}

Every match and decision lands in the audit log with the matching rule’s id, so you can tune rules from real traffic.