A cooperative gate for the shell commands AI agents run. Previews, backups, policy, audit. Claude Code + Cursor. A windshield, not a sandbox.
Run AI coding agents with confidence.
Termaxa gates the shell commands an agent runs — previews the blast radius, backs up first, blocks the dangerous ones, and escalates repeat offenders. It's a cooperative windshield, not a sandbox.
Your AI agent wants to run git push --force, DROP TABLE users, terraform apply, rm -rf. Most of the time it's right. Sometimes it isn't. Today your only options are supervise every command (which defeats the point of an agent) or trust it blindly (which defeats your Friday).
Termaxa is a third option: a gate the agent's commands pass through. It reads a policy you wrote, shows you what's actually about to happen, backs up what's about to change, and records everything. Built for Claude Code and Cursor today; works as a standalone CLI anywhere.
Claude Code --> TERMAXA --> git . postgres . docker . terraform . your shell | +- decide allow / ask / deny (your policy) +- preview commits lost, rows affected, resources destroyed +- insure automatic backup before destructive ops +- escalate repeated destructive intent -> auto-deny +- record every attempt, with an execution report
1. Install. Download a prebuilt binary from Releases and put it on your PATH — or, with a Rust toolchain:
cargo install --git https://github.com/termaxa/termaxa
2. Wire up a project.
cd your-project termaxa init --claude-code # writes .termaxa/policy.yaml, installs the Claude Code hook
3. See it work.
termaxa check "git push --force origin main"
From now on, every Bash command Claude Code runs in this project passes through Termaxa first. Runtime state (logs, backups) lives in ~/.termaxa/, safely outside your repo.
$ termaxa check "git status && rm -rf /" decision: deny reason : segment 2/2 `rm -rf /` — Recursive delete from root is blocked.
Termaxa splits compound commands and judges each part. git status && buys nothing.
$ termaxa check "psql -d shop -c 'DROP TABLE users'" decision: deny reason : DROP TABLE is blocked. Archive or rename instead. preview : postgres impact DROP TABLE users rows (estimate) : 50,000 referenced by : audit_log, orders, sessions (3 tables) without CASCADE : this DROP will FAIL (dependents exist) insurance : pg_dump users before execution (automatic on run/hook)
Row estimates come from the planner (pg_class.reltuples, stale between ANALYZEs) — Termaxa never scans your tables.
An agent blocked on rm -rf . will often just try again with different words. Termaxa classifies the intent, not the spelling, and trips a per-session circuit breaker on repeat attempts:
$ rm -rf . -> ask (file-delete #1) $ Remove-Item -Recurse -Force . -> ask (file-delete #2, different shell) $ del /s /q . -> DENY circuit breaker: 2 prior file-delete attempts this session
Three shells, one intent, third variant auto-denied — no rule enumerated per spelling. find -exec rm, xargs rm, and unlink count too. Configure via circuit_breaker: in policy.yaml (on by default, threshold 2).
$ termaxa run -- git push --force origin main ┌ push preview (main -> origin) │ ⚠ remote will LOSE 1 commit(s): │ ✗ 44510f1 important work └ Proceed? [y/N] y 🛟 backup b-1783006590625 — origin/main @ 44510f1 pinned to termaxa/backup/b-1783006590625 $ termaxa rollback b-1783006590625 ✓ origin/main restored to 44510f1
Force push measures what the remote will lose, not just gain — and pins it to a backup branch first.
$ termaxa report ┌─ Termaxa Execution Report ───────────────────────── │ commands : 4 ✓ 1 allow · ? 2 ask · ✗ 1 deny │ blocked : ✗ psql -d shop -c "DROP TABLE users" │ impact : • DROP users ~50,000 rows, 3 dependent(s) │ • plan: +3 ~0 -1 │ backups : 1 — rollback available │ risk : Medium (deny×3 + escalation×2 + ask×1 = 5) └──────────────────────────────────────────────────
Every line is a fact with a source in the audit log. Nothing invented.
"Claude Code already asks permission — why do I need this?"
The built-in prompt tells you the command. Termaxa tells you the consequence: 50,000 rows, 3 dependent tables, 1 commit lost. It takes the backup before you approve, and when it blocks something it tells the model why, so the agent proposes an alternative instead of retrying.
Why not a sandbox / Docker?
A sandbox contains damage to the sandbox. But your repo, your database, and your Terraform state are exactly the real things an agent must touch to be useful. Sandboxes are walls; Termaxa is a windshield. They're complementary — run both.
Why not OPA / policy engines?
OPA decides allow/deny well. It has no execution previews, no automatic backups, no rollback, and no agent-native hook. Termaxa is policy plus the things you actually want when an agent is holding the keyboard.
a command the agent wants to run | +-------------------------v-------------------------+ | TERMAXA | | | | shell split -> policy -> context -> decision | | (&&, ;, |) (yaml) (branch, (allow/ | | flags, ask/deny) | | prod, SQL) | | | v | | preview <-------------- consequential| | (git loss, pg blast radius, | | | terraform plan) v | | insurance <------------- destructive | | (git ref / pg_dump / files) | | | v | | execute | | | | | audit (JSONL, ~/.termaxa) <---------------+ | | notify (webhook) report (session summary) | +---------------------------------------------------+
Six engines, one binary. Policy is in-repo (.termaxa/policy.yaml, reviewable in PRs); logs and backups live in ~/.termaxa/ where no git operation can touch them.
.termaxa/policy.yaml — first match wins, * is a wildcard, matching is case- and whitespace-insensitive:
version: 1 default: ask # unmatched commands require approval rules: - match: "git status*" action: allow - match: "git push*--force*" action: ask reason: "Force push — remote history will be overwritten." - match: "*drop table*" action: deny reason: "DROP TABLE is blocked. Archive or rename instead." circuit_breaker: # optional (on by default) enabled: true threshold: 2 # trip on the 3rd repeated destructive attempt notify: # optional webhook: https://hooks.slack.com/services/... on: [deny, ask]
| Command | Purpose |
|---|---|
termaxa init [--claude-code] |
scaffold .termaxa/, detect tools, install the hook |
termaxa check "<cmd>" |
dry-run: verdict + preview (exit 0/3/4) |
termaxa run -- <cmd> |
gated execution: preview → approve → backup → run |
termaxa hook |
Claude Code PreToolUse mode (stdin JSON → decision) |
termaxa log [--decision D] [--source S] [--json] |
the audit trail |
termaxa stats |
totals, sessions, top blocked |
termaxa backups · termaxa rollback <id> |
list / restore backups |
termaxa report [--session ID] [--all] [--md] |
session summary |
termaxa notify --test |
verify your webhook |
termaxa paths |
where policy and state live |
Termaxa is pre-1.0. It's real and tested, and it is not magic. Specifically:
deny and propose an alternative, which is what makes the gate work. An agent running in full-auto mode could, in principle, retry a blocked action through a different command or shell; the circuit breaker raises the cost of that, but a hook is an integration point for visibility and policy, not an enforcement boundary. True enforcement means owning the execution path — that's Termaxa Runtime, on the roadmap. For hard guarantees today, pair Termaxa with OS-level sandboxing.termaxa run. An agent with raw, unhooked shell access is not contained — that needs OS-level sandboxing, a complementary layer. The threat model is agents making expensive mistakes, not a malicious agent actively evading you.&&, ||, ;, | and flags $(...). Subshells ( ), deeply nested quoting, and variable-expanded commands are judged conservatively, not deeply understood.terraform plan. Remote Terraform state is versioned by its backend, not by Termaxa.rm insurance keys on the literal rm command. Postgres backups use pg_dump/psql and must be on your PATH. No retention/pruning yet — backups accumulate.See SECURITY.md for the full threat model.
Issues and PRs welcome. cargo test must pass; CI runs on Linux, macOS, and Windows. The codebase is ~3,600 lines of dependency-light Rust — src/policy.rs and src/preview.rs are the best places to start reading.