Policy Rules
Control what tools Codex can use with allow/deny/hold rules managed from the chat prompt
Policy Rules
Policy rules control what tools Codex can use. Rules are evaluated before intent plans - a denied tool stays denied even if it appears in the plan. Applying a policy change is human-only: Codex can read policy and stage a proposal, but only you can confirm it.
Type policy commands as plain prompts with no leading slash (armor policy list, not /armor). Codex reserves / for its own built-in commands, so ArmorCodex intercepts the armor ... text in the UserPromptSubmit hook.
How Rules Work
- Rules evaluate top-to-bottom in the order they appear
- First matching rule wins (
allow,deny, orhold) - If no rule matches, the default decision applies (allow unless you change it)
- New rules are added at the top, giving them the highest priority
Rule Effects
| Effect | Keyword | What happens |
|---|---|---|
| Allow | allow | Tool call proceeds |
| Deny | deny, block | Tool call is blocked before execution |
| Hold | hold, require_approval | Tool call pauses for approval |
Adding Rules
Use armor policy add to stage a rule change. All changes go through a stage then confirm workflow:
armor policy add deny bashArmorCodex responds with a diff and risk warnings showing what will change. Then confirm:
armor yesMultiple Rules at Once
Comma-separate different effects:
armor policy add allow bash and apply_patch, deny apply_patchThis stages all the rules in one proposal. Confirm with armor yes.
Tool Names
Use Codex tool names directly. Codex's shell tool has several internal names, all normalized to bash:
| You type | Matches |
|---|---|
bash | The shell tool (exec_command, shell, local_shell, unified_exec, container.exec) |
apply_patch | File edits |
mcp__<server>__<tool> | A specific MCP tool |
mcp__<server>__* | Every tool from an MCP server |
* | All tools (wildcard) |
Setting the Default Decision
The default decision applies when no rule matches. By default tools with no matching rule are allowed. To fail closed, set the default to deny (this appends a catch-all rule) and confirm:
armor policy default deny
armor yesOptions: allow, deny, hold.
Removing Rules
Remove by rule ID (shown in armor policy list), then confirm:
armor policy remove policy1
armor yesClear all rules:
armor policy reset
armor yesBoth go through the stage then confirm workflow.
Policy Templates
Apply a pre-built policy in one command, then confirm:
armor policy template lockdown
armor yes| Template | Description |
|---|---|
all-allow | Everything permitted - intent planning still enforced |
strict-read-only | Only read-style tools allowed. Everything else denied |
balanced | Reads allowed. Shell and file edits require approval |
lockdown | All tools require approval. Nothing auto-allowed |
Data Classification
ArmorCodex auto-detects sensitive data in tool arguments:
| Class | What it detects |
|---|---|
| PCI | Credit card numbers (Luhn validation), card-related keywords |
| PAYMENT | Payment tool names, banking keywords (IBAN, SWIFT, routing numbers) |
| PHI | Health/medical data (manual policy) |
| PII | Personal data, SSN patterns (manual policy) |
Example: Block Payment Data
armor policy add deny * for payment data
armor yesIf Codex tries to write a credit card number to a file, ArmorCodex detects PCI data, matches the payment rule, and blocks the tool call.
The Stage then Confirm Workflow
Every policy mutation goes through staging. This prevents accidental changes:
- You type a command (e.g.,
armor policy add deny bash) - ArmorCodex stages the proposal and shows a diff plus risk warnings. Nothing is enforced yet
- You review and either confirm or cancel:
| Action | Command |
|---|---|
| Apply staged change | armor yes or armor policy confirm |
| Apply by proposal ID | armor policy confirm pol_abc12345 |
| Discard staged change | armor no or armor policy cancel |
Staged proposals are written to policy-pending.json with an id, hashes, the base version, and a 30 minute expiry.
Confirm re-checks the base version, base hash, proposal hash, and expiry before applying. If the active policy changed since you staged, the proposal is rejected and you re-stage.
Human-Only Apply
Applying a policy change is human-only. The policy_command MCP tool (callable by Codex) can read policy and stage proposals, but it cannot confirm. Only a terminal armor yes applies a staged change, and confirm echoes the applied diff and notes who staged it.
A legacy policy_update MCP tool is retained for back-compat and applies immediately. Prefer the staged armor commands and policy_command for the human-in-the-loop guarantee. See MCP Tools.
Viewing Policy
| Command | What it shows |
|---|---|
armor policy list | Human-readable summary with rule IDs and effects |
armor policy view | The active policy as raw JSON |
Policy Schema
ArmorCodex stores policy as a simple rule list:
{
"rules": [
{ "id": "policy1", "action": "deny", "tool": "bash" },
{ "id": "policy2", "action": "deny", "tool": "*" }
]
}Rules are evaluated in order; the first match wins. Each rule matches by tool name (with an optional trailing * glob, e.g. mcp__github__*) and can add matchers for finer control:
| Field | Meaning |
|---|---|
action | allow, deny, or hold |
tool | Tool name or trailing-* glob |
dataClass | Match a detected data class (PCI, PAYMENT, PHI, PII) |
anyParam | Apply an operator across every string field on the tool input |
Matcher Operators
| Operator | Meaning |
|---|---|
$equals | Exact string match |
$contains | Substring match |
$startsWith / $endsWith | Prefix / suffix match |
$matches | Regex match |
$pathContains | Path match (canonicalizes ~ and absolute forms) |
anyParam | Apply the operator across every string field on the tool input |
Plain string sugar is supported: anyParam: "curl" is the same as anyParam: { "$contains": "curl" }.
Natural Language (Back-Compat)
The older natural-language form still works and applies immediately (no staging):
Policy list
Policy new: deny bash containing curl
Policy delete policy1
Policy resetPrefer the structured armor policy commands for the stage then confirm safety.
Where Policies are Stored
Policies persist across sessions in the ArmorCodex data directory:
cat ~/.codex/armorcodex/policy.jsonThey survive restarts, plugin updates, and re-installs. Override the location with ARMORCODEX_POLICY_FILE.