Skip to content

Repository files navigation

Allowatron

Use Pattern Matching to Auto-approve Codex Tool-Calls

Coding robot approves everything that matches

Allowatron is a local Codex PermissionRequest plugin for recurring Bash approvals. It keeps Codex's normal approval UI for anything it cannot prove from an explicit rule, and records those deferred requests in a local review queue.

The v0.3 implementation is deterministic and offline. It does not call a GPT model, execute pending commands, replace Codex's client, or change Codex's native permission rules.

Install and runtime

The plugin uses Python 3.11+ and the pinned regex and interactive questionary dependencies in requirements.txt. The hook never installs packages or accesses the network. If the host runtime lacks the pinned regex dependency, the hook defers and reports a repair command. The Questionary dependency is only needed for the interactive review widget; --plain remains available without it.

Run the management CLI from the plugin checkout (quote the path if it contains spaces):

python3 "/path/to/allowatron/scripts/allowatron.py" doctor --json
python3 "/path/to/allowatron/scripts/bootstrap_runtime.py" \
  --data-dir "$PLUGIN_DATA"

The bootstrap command is explicit and creates a mutable virtualenv under the selected data directory. It may download the pinned package; do not run it from the hook. When that virtualenv exists, the quoted hook and CLI launchers use it; they never create or update it automatically. The installed hook receives PLUGIN_DATA from Codex and the CLI accepts --data-dir so both use the same store. Without either, the CLI uses ~/.codex/plugins/data/allowatron; there is no checkout-relative fallback.

For the personal marketplace workflow:

codex plugin add allowatron@personal

Codex may require the PermissionRequest hook to be reviewed/trusted separately from plugin installation. Start a new Codex thread after an update.

First rule

Register project roots explicitly, then import a rule document. A checked-out JSON file is inert until it is imported:

allowatron() {
  python3 "/path/to/allowatron/scripts/allowatron.py" \
    --data-dir "$PLUGIN_DATA" "$@"
}
allowatron project add /absolute/path/to/project
allowatron rules validate examples/rules.json
allowatron rules import examples/rules.json
allowatron rules list

The function keeps plugin paths with spaces safely quoted.

Rule documents are versioned JSON. Each rule has an ID, description, decision, global/project/session scope, executable, and an ordered argument list. Argument matchers are literal, glob (* and ?, neither crosses /), or full-string regex:

{
  "version": 1,
  "rules": [
    {
      "id": "read-pr-comments",
      "enabled": true,
      "description": "Read PR comments in one project",
      "decision": "allow",
      "scope": { "project": "/absolute/path/to/project" },
      "command": {
        "executable": "gh",
        "arguments": [
          { "literal": "api" },
          { "regex": "repos/acme/app/pulls/[0-9]+/comments" },
          { "literal": "--paginate" }
        ]
      }
    }
  ]
}

Rules compare the complete argv: executable, argument order, argument count, and scope must all match. Across matching enabled rules, deny wins over defer, which wins over allow. A disabled rule does not match.

Scopes may be project-wide ({"project": "/absolute/path/to/project"}), session-specific ({"project": "/absolute/path/to/project", "session_id": "..."}), or global ({"global": true}). Global rules apply to all sessions in every registered project; they do not authorize commands from unregistered directories.

Review workflow

When no rule matches, the hook returns {}. Codex retains its ordinary prompt behavior and Allowatron records a sanitized, deduplicated queue entry for commands that can be represented by the rule model:

python3 "/path/to/allowatron/scripts/allowatron.py" \
  --data-dir "$PLUGIN_DATA" queue list
python3 "/path/to/allowatron/scripts/allowatron.py" \
  --data-dir "$PLUGIN_DATA" review 12

review opens a newest-first pending queue when no ID is supplied. In a real terminal it uses an inline arrow-key/shortcut selector with Back at every menu and Ctrl+B at text prompts; it keeps ordinary terminal scrollback rather than using a full-screen interface. Select a request, then choose allow/deny/defer, project/session/global scope, exact literals or customized matchers for one or more arguments, preview positive and negative examples against the real evaluator, and make an explicit final save choice. The matcher editor lists every positional argument and supports revisiting, replacing, or resetting an argument matcher before selecting Done. Use review ID --plain for numbered/text prompts, or when stdin/stdout is not a TTY. The rule is saved before the queue entry is marked resolved. EOF, cancel, invalid patterns, concurrent changes, or a failed write leave the entry pending.

If a pending request is close to an existing same-shape rule, review lists the nearby rule and offers editing it or creating a new rule. Editing an existing rule starts from its saved matchers; Reset restores the saved matcher rather than treating a regex or glob as a literal. A positive example is required and the preview shows original/proposed rules, changed fields, example results, and effective precedence. The queued command may be used as a safe example when it was not redacted. Saving never resolves a request unless the user explicitly chooses save-and-resolve and the fresh effective result is allow or deny.

To diagnose a prompt without changing anything, use explain:

allowatron explain --project /absolute/path/to/project \
  --command 'git commit -m subject -m body' --json
allowatron explain --project /absolute/path/to/project \
  --command 'git commit -m subject -m body' --rule commit-rule

When no rule matches, the output identifies up to five nearby enabled rules and the exact argument, count, executable, or scope cause. JSON adds near_misses and diagnostic completeness metadata. These suggestions are navigation aids; they never authorize a command or alter the evaluator's decision.

Commands with unsupported shell syntax or unsupported wrappers still defer to Codex, but are not added to the review queue by default because no Allowatron rule can match them. Set ALLOWATRON_QUEUE_UNSUPPORTED=1 before hook use to retain those requests for audit; the review UI treats them as dismiss-only.

Useful management commands:

allowatron doctor [--json]
allowatron project add|list|remove PATH
allowatron rules list|validate|import|export|preview|upsert|disable|remove
allowatron explain --project PATH [--session ID] --command '...' [--rule ID]
allowatron queue list|show|dismiss|resolve|prune [--all]
allowatron review [ENTRY_ID] [--rule ID] [--plain]

See the complete command reference for command options, JSON formats, rule matching, queue lifecycle, and safety boundaries.

explain is read-only: it never executes a command, queues a request, or changes active rules. --json is available on read-only listing, validation, export, explain, doctor, queue inspection, preview, and upsert commands.

Codex skill

The plugin includes an allowatron skill for inspecting and tuning rules from within a Codex conversation. It uses the same CLI, evaluator, and data store as the hook. The skill can list deferred requests, explain a decision, preview a single-rule draft with positive and negative examples, and apply an explicitly authorized guarded update. It does not run queued commands or use the interactive terminal review flow.

Queue inspection uses a read-only SQLite connection so the skill can inspect a Codex data directory when its sandbox mounts that directory read-only. Rule activation and queue resolution still require write access to the selected store; the skill must report a sandbox write failure rather than creating a second store.

For a conversational edit, the skill keeps a draft file separate from active rules, previews the full argument match and scope, then uses rules upsert only after authorization. Same-ID edits include the current expected_rule, so a concurrent change is rejected instead of overwriting it. The terminal review --rule ID editor provides the same guarded saved-rule workflow, while pending review can optionally edit a nearby rule or create a new one. If the installed marketplace data directory cannot be identified, the skill requires an explicit ALLOWATRON_DATA_DIR or --data-dir.

Safety boundaries

The parser accepts only one simple command made from literal words, spaces/tabs, and single/double quotes. It rejects shell operators, redirects, substitutions, backslashes, comments, glob/brace/tilde expansions, control characters, leading assignments, and wrappers such as env, sudo, and sh -c. These are deliberate false negatives: the request is deferred rather than interpreted as safe. Allowatron never runs a command to inspect it and never resolves an executable through PATH.

Project roots are registered explicitly and resolved by path components using the longest registered ancestor. Sibling names cannot match by string prefix; distinct worktrees are distinct scopes. Session rules require the exact project/session pair. Global rules still require a registered project context.

Queue data lives in queue.db with a private HMAC key, mode 0700 data directories, and mode 0600 files. It retains entries for at most 7 days and 100 records by default; the same limits are enforced when new requests are recorded. Use allowatron queue prune --all to explicitly delete pending, resolved, and dismissed entries. Common token/password flags, authorization headers, and URL credentials are redacted; arbitrary secrets can evade best-effort redaction. Set ALLOWATRON_METADATA_ONLY=1 before queue use to store no command/argv content. Redacted or metadata-only entries require a clean user-supplied example before promotion to a rule.

The old feasibility review.jsonl hash-only file is not imported or promoted. Deleting the data directory removes active rules, registered projects, queue history, and the fingerprint key; uninstalling the plugin does not delete it.

Verification

Run the offline verification suite from the repository root:

python3 -X tracemalloc=10 -W error::ResourceWarning \
  -m unittest discover -s tests -v

Validate the plugin manifest with the plugin-creator validator, and measure local hook latency with synthetic state:

python3 /path/to/plugin-creator/scripts/validate_plugin.py .
python3 scripts/benchmark.py --rules 100 --queue 1000 --iterations 100

The benchmark reports the machine and sample count only when you run it; its target is p95 below 200 ms locally, with evaluation bounded below Codex's hook timeout. No live trust or prompt behavior is inferred from these automated checks; verify those in a fresh Codex thread after installation.

About

More flexible Codex permissions

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages