How ArmorCodex Works
Architecture, intent enforcement, drift detection, and the hook system
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:
| Hook | When | What ArmorCodex Does |
|---|---|---|
| SessionStart | Session opens | Initialize, show enforcement status |
| UserPromptSubmit | User sends a prompt | Inject plan directive, handle policy commands |
| PreToolUse | Before any tool runs | Enforce: check plan, policy, token |
| PostToolUse | After tool succeeds | Send audit log to backend |
| Notification | Permission request | Return allow/deny decision |
| Stop | End of a turn | Check 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 BashIf 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:
- Codex calls
register_intent_planwith its tool list - ArmorCodex sends the plan to the backend's token endpoint
- Backend returns a signed JWT with a configurable TTL (default 5 minutes)
- Each tool call is validated against the plan embedded in the token
- 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_planCodex needs this to declare planspolicy_read/policy_updatefor 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/:
| File | What | Lifecycle |
|---|---|---|
runtime.json | Sessions, tokens, plans, discovered tools | Per-session, pruned after 24h |
policy.json | Policy rules + version history | Persistent |
pending-plan.json | Plan awaiting consumption by PreToolUse | Consumed + deleted on next tool call |
audit.log | Append-only audit trail of every tool decision | Persistent |
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.