CLI Firewall
Quick example
- curl
- Python
- Node.js
curl "$TRINITITE_BASE/v1/cli/evaluate" \
-H "Authorization: Bearer $TRINITITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_platform": "claude_code",
"command": "rm -rf /var/lib/postgres",
"cwd": "/repo"
}'
import os, requests
resp = requests.post(
f"{os.environ['TRINITITE_BASE']}/v1/cli/evaluate",
headers={"Authorization": f"Bearer {os.environ['TRINITITE_API_KEY']}"},
json={"agent_platform": "claude_code", "command": "rm -rf /var/lib/postgres", "cwd": "/repo"},
)
print(resp.json())
const resp = await fetch(`${process.env.TRINITITE_BASE}/v1/cli/evaluate`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TRINITITE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
agent_platform: 'claude_code',
command: 'rm -rf /var/lib/postgres',
cwd: '/repo',
}),
});
console.log(await resp.json());
See Cookbook → Sandbox a coding agent's CLI for an end-to-end recipe.
The CLI Firewall is the platform's agentic command firewall — a governed proxy that AI agents call instead of executing shell commands directly. Every command goes through:
- Parse the raw command into executable + arguments.
- Evaluate against the active governance policy and any matching firewall rules.
- Render a verdict:
pass,correct(modified command), orblock. - Optionally execute the (corrected) command in a sandbox.
- Append an immutable audit-log entry.
This mirrors the Pass / Correct / Block vocabulary used by Guardian Mode and the MCP Gateway, but operates at the CLI level — targeting the "Inner Loop" of agent-driven development. Combined with the Skill Vault, it forms an Agentic SBOM around what an agent loads (skills) and what it executes (commands).
Authentication: Authorization: Bearer <session_token | api_key> with the relevant cli:* permission.
Endpoints
Evaluation & execution
| Method | Path | Permission |
|---|---|---|
POST | /v1/cli/evaluate | cli:read |
POST | /v1/cli/execute | cli:create |
Governance policies
| Method | Path | Permission |
|---|---|---|
GET | /v1/cli/governance/policies | cli:read |
POST | /v1/cli/governance/policies | cli:create |
GET | /v1/cli/governance/policies/active | cli:read |
GET | /v1/cli/governance/policies/{id} | cli:read |
POST | /v1/cli/governance/policies/{id}/version | cli:update |
POST | /v1/cli/governance/policies/{id}/activate | cli:update |
DELETE | /v1/cli/governance/policies/active | cli:delete |
DELETE | /v1/cli/governance/policies/{id} | cli:delete |
Firewall rules
| Method | Path | Permission |
|---|---|---|
GET | /v1/cli/governance/firewall-rules | cli:read |
POST | /v1/cli/governance/firewall-rules | cli:create |
GET | /v1/cli/governance/firewall-rules/{id} | cli:read |
PATCH | /v1/cli/governance/firewall-rules/{id} | cli:update |
DELETE | /v1/cli/governance/firewall-rules/{id} | cli:delete |
Audit logs
| Method | Path | Permission |
|---|---|---|
GET | /v1/cli/governance/logs | cli:read |
GET | /v1/cli/governance/logs/session/{sessionId} | cli:read |
Verdict & risk vocabulary
| Verdict | Meaning | Behaviour |
|---|---|---|
pass | Command is safe to execute as-is | Forwarded to execution if requested |
correct | Command modified to remove risk | The corrected_command is executed instead of the original |
block | Command rejected | Returns 403 forbidden with details.block_reason |
Risk categories: read, write, delete, execute, exfiltrate, install, network, privilege_escalation. Severities: low, medium, high, critical.
POST /v1/cli/evaluate
Evaluate a command without executing it.
{
"raw_command": "rm -rf /var/log/*",
"working_directory": "/home/agent",
"shell_type": "bash",
"agent_platform": "cursor",
"agent_version": "0.42.0",
"session_id": "clisess_01JF8RCSE1A2B3C4D5E6F7G8H9I",
"nhi_id": "nhi_01JF8RNHI1A2B3C4D5E6F7G8H9"
}
| Field | Type | Required | Description |
|---|---|---|---|
raw_command | string | Yes | The full command line as the agent would run it |
working_directory | string | No | The agent's cwd |
shell_type | enum | No | bash, zsh, sh, fish, pwsh |
agent_platform | string | No | E.g. cursor, codex, claude_code |
agent_version | string | No | — |
session_id | string | No | Reuse an existing session ID |
nhi_id | string | No | NHI principal making the call |
Pass / Correct response — 200 OK
{
"session_id": "clisess_01JF8RCSE1A2B3C4D5E6F7G8H9I",
"verdict": "correct",
"corrected_command": "rm -rf /var/log/agent-tmp/*",
"action": "Scoped destructive path to /var/log/agent-tmp.",
"block_reason": null,
"violations": [
{ "rule": "pattern_block:rm-broad-glob", "severity": "high", "detail": "rm against a system log path." }
],
"risk_score": 38.5,
"risk_category": "delete",
"sandbox_route": {
"provider_id": "sbprov_01JF8RSBP1A2B3C4D5E6F7G8H9I",
"provider_name": "E2B",
"route_action": "sandbox",
"timeout_ms": 30000
}
}
Block response — 403 Forbidden
{
"error": {
"code": "forbidden",
"message": "Command blocked by governance policy.",
"details": {
"block_reason": "rm is blocked",
"violations": [
{ "rule": "blocklist:rm", "severity": "high", "detail": "rm is blocked" }
],
"risk_score": 85.0
},
"request_id": "req_01J..."
}
}
POST /v1/cli/execute
Evaluate and execute. Same request shape as /evaluate. If the verdict is block, returns 403. Otherwise the (possibly corrected) command runs and the result is returned.
{
"session_id": "clisess_01JF8RCSE1A2B3C4D5E6F7G8H9I",
"verdict": "pass",
"corrected_command": null,
"action": "Passed CLI governance.",
"risk_score": 5.0,
"sandbox_route": null,
"execution": {
"exit_code": 0,
"stdout": "file1.txt\nfile2.txt",
"stderr": "",
"duration_ms": 42,
"truncated": false
}
}
stdout / stderr are size-limited; commands producing very large output have their streams truncated and truncated: true is set.
Governance policies
Policies follow an immutable versioning model: edits to an active policy create a new version. Exactly one version is active at a time per organisation.
Create a policy
POST /v1/cli/governance/policies
{
"name": "Production v1",
"description": "Production agent fleet — destructive commands corrected, not blocked.",
"rules": ["fwr_01JF8RFR1...", "fwr_01JF8RFR2..."],
"auto_pass_risk_threshold": 25.0,
"auto_block_risk_threshold": 80.0,
"default_action": "evaluate",
"metadata": { "team": "platform", "owner": "agent-platform@acme.com" }
}
| Field | Type | Description |
|---|---|---|
name | string | Display name |
rules | string[] | Firewall rule IDs (fwr_*) referenced by this policy |
auto_pass_risk_threshold | number | Verdict gates to pass below this score, regardless of individual rule matches |
auto_block_risk_threshold | number | Verdict gates to block above this score |
default_action | string | evaluate, pass, or block (catch-all when no rule matches) |
Activate a version
POST /v1/cli/governance/policies/{id}/activate — atomically swaps the active policy. The previous version is preserved.
Get the active policy
GET /v1/cli/governance/policies/active — convenience endpoint for the current effective policy.
Suspend governance
DELETE /v1/cli/governance/policies/active — temporarily deactivates all CLI governance for the org. Subsequent /evaluate calls return verdict: "pass" until a new policy is activated. Useful for incident response when you need to fail-open knowingly. The suspension is logged.
Firewall rules
Reusable rules referenced by policies.
| Rule type | What it matches |
|---|---|
executable_block | Blocks specific binaries (rm, dd, nc) |
pattern_block | Regex/glob over the full command |
arg_constraint | Constrain specific argument values |
semantic_intent | Guardian-evaluated semantic intent |
Create a rule
{
"rule_type": "pattern_block",
"name": "rm-broad-glob",
"patterns": ["^rm\\s+-rf?\\s+/(var|etc|usr|bin|sbin|home|root)\\b"],
"severity": "critical",
"category": "delete",
"action": "block",
"enabled": true
}
System rules ship with the platform and are immutable.
Audit logs
GET /v1/cli/governance/logs
| Query | Type | Description |
|---|---|---|
session_id | string | Filter to one session |
verdict | string | pass, correct, block |
from / to | RFC 3339 | Time window |
limit / cursor | — | Cursor pagination |
{
"data": [
{
"log_id": "clilog_01JF8RCLG1A2B3C4D5E6F7G8H9I",
"session_id": "clisess_01JF8RCSE1A2B3C4D5E6F7G8H9I",
"timestamp": "2026-05-01T22:41:00Z",
"agent_platform": "cursor",
"nhi_id": "nhi_01JF8RNHI1A2B3C4D5E6F7G8H9",
"verdict": "block",
"command_hash": "sha256:e3b0c44...",
"block_reason": "rm is blocked",
"risk_score": 85.0,
"risk_category": "delete",
"policy_id": "fwp_01JF8RFWP1A2B3C4D5E6F7G8H9I",
"policy_version": 3
}
],
"page": { "next_cursor": null, "has_more": false }
}
Raw commands are redacted by default; the command_hash lets you correlate without exposing content. To see raw commands, the calling principal must additionally hold cli:admin (and the org must enable raw-command storage).
GET /v1/cli/governance/logs/session/{sessionId}
Convenience: the full timeline for one session, ordered chronologically.
Errors
| HTTP | error.code | Cause |
|---|---|---|
400 | validation_error | Body or query failed schema validation |
401 | unauthenticated | Missing or invalid credential |
403 | forbidden | Caller lacks the required cli:* permission, or governance verdict is block |
404 | not_found | Policy, rule, session, or log not found |
409 | conflict | Duplicate policy name, attempt to delete an active policy |
422 | unprocessable_entity | Sandbox provider unavailable |
429 | rate_limited | Per-organization rate limit exceeded |
Next steps
- Govern what the agent loads in addition to what it runs → Skill Vault
- Bind a Guardian to evaluate semantic intent of high-risk commands → Identities
- Inspect every command in the Glass Box Ledger → Logs API