ArmorCodex LogoArmorCodex
Concepts

How ArmorCodex Works

Architecture, intent enforcement, drift detection, and the hook system

View Source

How ArmorCodex Works

ArmorCodex is a Codex plugin that intercepts every tool call via lifecycle hooks. It enforces a simple rule: Codex must declare what it intends to do before doing it.

Architecture

User Prompt
     |
     v
UserPromptSubmit Hook
  Injects directive: "register your plan first"
     |
     v
Codex calls register_intent_plan MCP tool
  Plan validated + stored (local or signed JWT)
     |
     v
Codex calls a tool (Bash, apply_patch, MCP, etc.)
     |
     v
PreToolUse Hook (ENFORCEMENT)
  1. Is this tool in the registered plan?
  2. Do the parameters match?
  3. Do policy rules allow it?
  4. Is the intent token still valid?
     |
  ALLOW or DENY
     |
     v
PostToolUse Hook
  Audit log sent to ArmorIQ (if API key set)

Hook Events

ArmorCodex registers lifecycle hooks via ~/.codex/hooks.json:

HookWhenWhat ArmorCodex Does
SessionStartSession opensInitialize, show enforcement status
UserPromptSubmitUser sends a promptInject plan directive, handle policy commands
PreToolUseBefore any tool runsEnforce: check plan, policy, token
PostToolUseAfter tool succeedsSend audit log to backend
NotificationPermission requestReturn allow/deny decision
StopEnd of a turnCheck token expiry

Codex's hooks fire today for Bash, apply_patch, and MCP tool calls. File reads, web search, and other surfaces are not yet directly gated.

Intent Drift Detection

If Codex tries a tool that was not in its declared plan, ArmorCodex blocks it:

ArmorCodex intent drift: tool not in plan (Bash)

This prevents prompt injection from silently steering the agent into unauthorized tool use. Codex sees the denial and either re-registers a new plan that includes the tool, or tells the user it cannot perform that action.

Parameter Enforcement

Plan steps can constrain expected parameters. If Codex's actual tool call parameters do not match the plan's declared inputs, the call is blocked:

ArmorCodex intent mismatch: parameters not allowed for Bash

If the agent's declared keys don't overlap with the tool's real input keys (a common shape mismatch), ArmorCodex falls back to a tool-name match rather than blocking on a benign discrepancy.

Token Lifecycle

With an API key, ArmorCodex gets a signed JWT from the ArmorIQ backend:

  1. Codex calls register_intent_plan with its tool list
  2. ArmorCodex sends the plan to the backend's token endpoint
  3. Backend returns a signed JWT with a configurable TTL (default 5 minutes)
  4. Each tool call is validated against the plan embedded in the token
  5. After the TTL, the token expires and Codex must register a new plan

Without an API key, the plan is stored locally with no expiry.

Codex's MCP transport closes the pipe ~1s after a tool call returns. ArmorCodex caps the SDK round-trip at 500ms (ARMORCODEX_INTENT_DEADLINE_MS); if the backend takes longer, the registration completes in the background and PreToolUse uses the locally-stored plan as a fallback.

Fail-Closed

In enforce mode (the default), any of these causes a tool call to be blocked:

  • No intent plan registered (and intent is required)
  • Tool not in plan (intent drift)
  • Parameters do not match plan constraints
  • Policy rule denies the tool
  • Intent token expired
  • Internal hook error

In monitor mode, these events are logged but tool calls proceed.

Whitelisted Tools

These tools are never blocked (they are needed for ArmorCodex to function):

  • register_intent_plan Codex needs this to declare plans
  • policy_read / policy_update for policy management
  • The MCP server itself (armorcodex-policy.*) is always allowed

State Management

Hooks are stateless (new Node process per event). All state persists to files in ~/.armoriq/armorCodex/plugins/armorcodex/data/:

FileWhatLifecycle
runtime.jsonSessions, tokens, plans, discovered toolsPer-session, pruned after 24h
policy.jsonPolicy rules + version historyPersistent
pending-plan.jsonPlan awaiting consumption by PreToolUseConsumed + deleted on next tool call
audit.logAppend-only audit trail of every tool decisionPersistent

No Separate LLM Call

ArmorCodex does not call a separate LLM to generate plans. Codex itself generates the plan as part of its normal reasoning turn, using the session's own credentials. Zero extra cost, zero extra latency.

On this page