A practical guide to building, configuring, and running the two-program system that puts a deterministic policy layer between shell commands and Unix execution — for AI agents and human operators alike.
aishell-gate-policy · aishell-gate-exec · aishell-gate-mcp
Copyright © 2026 AIShell Labs LLC Winston-Salem NC USA. All Rights Reserved. Use of this software requires a valid license. — www.aishellgate.com · www.aishell.org
--dry-run before live execution.
--dry-run performs full policy evaluation and fires all confirmation gates but does not
execute anything — it is the safest way to validate your configuration before committing to live runs../aishell-gate-exec --policy-preset ops_safe --dry-runThis guide covers AIShell-Gate's interactive mode end to end. You start at a terminal with the policy engine — no execution, no network, nothing runs — and learn how decisions work by watching them happen. Then you extend the same gate to the executor: same typed-command feel, now commands actually run, under full policy enforcement and audit. That two-step arc is the foundation of everything else the system does.
When you're ready to extend the gate to a remote AI agent over SSH, the Remote Deployment Guide picks up where this one ends. The same policy, the same decisions, now applied to a connection from somewhere else. The final section of this guide points you there.
Using Claude Code or Cursor? You can skip the learning arc. See the Using MCP guide for two-file configuration that gets the AI calling evaluate_plan and execute_plan against the gate in ten minutes.
This guide assumes familiarity with Unix command-line environments and basic JSON structure.
AIShell-Gate is two C programs that work together to put a deterministic policy gate between shell commands and Unix execution. It serves two distinct user populations from a single unified policy engine.
AI agents submit structured JSON execution plans. Every command in the plan is evaluated against policy before anything runs. The AI never receives direct shell access; it submits requests and receives decisions.
Human operators work interactively at a terminal. The same policy engine, the same confirmation gates, and the same audit chain apply to commands typed by a human as to commands proposed by an AI. For human operators the system functions as three things simultaneously: a safety net that catches dangerous commands before execution, a teaching tool that explains why each flag raises the risk profile it does, and a disciplined workflow that provides compliance-grade audit accountability for shell activity.
The two programs:
aishell-gate-policy is the policy engine. It receives a proposed command, normalizes it, evaluates it against the loaded policy stack, computes a risk score, and emits a structured JSON decision. It has no ability to execute anything. Its interactive mode is purely educational — it assesses commands and explains every flag with its documented reasoning. It never executes.
aishell-gate-exec is the execution gateway. It accepts a JSON plan from an AI agent, or reads commands typed interactively at a terminal, submits each through the policy engine, collects human confirmation where required, and calls execve() with the validated argument array. It contains no policy logic — it cannot approve or deny anything. Every execution decision is made by the policy engine in a separate process across a hard OS boundary.
That separation is the security property the system depends on. A compromised executor cannot grant itself permission to run a command the policy engine has denied.
AI agent path:
Human operator path:
Both paths use the same policy file. There is no separate human-mode configuration.
aishell-gate is the stable user-facing entry point to the entire system — present and future. Today it is a thin pre-flight wrapper around aishell-gate-exec. In v2 it becomes the multi-agent, multi-operator broker: session coordination, multi-agent routing, multi-operator confirmation. Because all invocations already flow through aishell-gate, the v2 transition will require no changes to deployed SSH forced commands, MCP configurations, or user muscle memory.
Use aishell-gate in preference to calling aishell-gate-exec directly. It runs pre-flight checks, injects --policy-binary automatically, and hands off to the executor via exec(). All flags pass through unchanged.
Synopsis:
aishell-gate [exec-flags] [policy-flags] [-- other-policy-flags]
aishell-gate --policy-preset ops_safe --audit-log ./exec.jsonl
echo '<json-plan>' | aishell-gate --policy-preset dev_sandbox
What it does before handing off:
aishell-gate-exec and aishell-gate-policy are present and executable.--policy-binary pointing at the verified policy engine — callers and SSH forced commands never need to supply it.exec() to replace itself with aishell-gate-exec. The launcher does not remain in the process tree after handoff.Default binary paths (system install):
/usr/bin/aishell-gate-exec
/usr/bin/aishell-gate-policy
Environment overrides for non-standard installations or testing:
| Variable | Default | Purpose |
|---|---|---|
| AISHELL_EXEC_BIN | /usr/bin/aishell-gate-exec | Path to aishell-gate-exec |
| AISHELL_POLICY_BIN | /usr/bin/aishell-gate-policy | Path to aishell-gate-policy |
For local use without a system install, set both variables to point at your local binaries:
AISHELL_EXEC_BIN=./aishell-gate-exec \
AISHELL_POLICY_BIN=./aishell-gate-policy \
./aishell-gate --policy-preset ops_safe
AIShell-Gate is available in two editions: Standard and Enterprise. Both editions share the same policy engine, execution gateway, built-in presets, confirmation gates, interactive mode, audit logging, and jail-root enforcement. Enterprise adds cryptographic audit chain integrity and full policy inspection tooling.
To check which edition you have:
./aishell-gate-policy --version
./aishell-gate-exec --version
The version output identifies the edition explicitly — for example: aishell-gate-policy standard or aishell-gate-policy enterprise.
| Feature | Standard | Enterprise |
|---|---|---|
| Policy evaluation engine | ✓ | ✓ |
| Execution gateway | ✓ | ✓ |
| Built-in presets (all 7) | ✓ | ✓ |
| Confirmation gates (none / plan / action / typed) | ✓ | ✓ |
| Interactive mode (educational, never executes) | ✓ | ✓ |
| Audit logging (JSON Lines) | ✓ | ✓ |
| Jail-root path enforcement | ✓ | ✓ |
| Session policy gating | ✓ | ✓ |
| Custom policy files (base / project / user layers) | ✓ | ✓ |
--dump-standard-template — export editable policy template | ✓ | ✓ |
--dump-policy — dump fully resolved effective policy as JSON | — | ✓ |
| HMAC-SHA256 tamper-evident exec audit chain | — | ✓ |
--audit-verify — verify exec audit log chain integrity | — | ✓ |
--audit-key — keyed HMAC exec audit chain | — | ✓ |
| Cryptographic session ID in audit and confirmation | — | ✓ |
not available in standard edition message and exits cleanly — it does not silently ignore the flag or produce a generic unknown-option error.--audit-log is supplied. The standard edition log records every decision and is append-only. The enterprise edition log additionally carries an HMAC-SHA256 chain — each entry is cryptographically linked to the previous one, sequence-numbered, and tied to a cryptographic session ID. Only the enterprise binary can verify its own chain with --audit-verify. Do not feed a standard-edition log to enterprise --audit-verify or vice versa.Contact [email protected] or visit www.aishellgate.com to upgrade from standard to enterprise.
AIShell-Gate has two interactive modes. This section covers the first — the policy engine's educational mode, which assesses commands and explains its reasoning but never executes. The second — the executor's interactive mode, where the same decisions are enforced against commands that actually run — is introduced later in this guide and walked through end-to-end in the Beta Tester Guide (Path A).
Run the policy engine at a terminal with no arguments:
./aishell-gate-policy
aishell-gate-policy [educational mode]
Preset: ops_safe Default-deny: yes Net-default-deny: yes
Source: human User: alice CWD: /home/alice
This tool assesses commands and explains every flag.
It does NOT execute. To execute, pipe through aishell-gate-exec.
────────────────────────────────────────────────
policy> git status
ALLOW git status
Confirmation required: none (no prompt needed)
Flag analysis: all flags are known-safe.
To execute through the gateway:
echo 'git status' | aishell-gate-exec --source human
policy> rm -rf /var/log
DENY rm -rf /var/log
Reason: -rf flag denied by arg_rules
Fix: Remove or replace the denied argument.
policy> ls -la
ALLOW ls -la
Confirmation required: none (no prompt needed)
Flag analysis: all flags are known-safe.
To execute through the gateway:
echo 'ls -la' | aishell-gate-exec --source human
policy> quit
The default preset is ops_safe, which allows a conservative set of read-only and repository inspection commands. You will see this behavior without any configuration files in place.
Type json after any command to see the full JSON decision record for the previous evaluation. Type quit or exit to leave.
The policy engine's flag catalog carries a documented reason for every flag that raises risk — not a generic label, a specific plain-English explanation:
sed -i — "in-place edit; writes back to source file"touch -t — "timestamp forgery can backdate files to evade time-based audit trails"dmesg -c — "clears the kernel ring buffer after reading — destroying evidence impedes forensic investigation"make -t — "touch mode updates timestamps without executing commands — creates misleading audit trail about what was built when"tcpdump -w — "writing raw packets to disk captures full payload including cleartext credentials"An unknown flag is explicitly labelled "risk unassessed, not evaluated as safe" — the system never implies it checked something and found it safe when it did not. For a junior operator, this is instruction delivered in context at the moment of the command. For an experienced operator, it is a disciplined workflow with confirmation gates and full audit trail on every command run.
A preset selects a named command allow/deny configuration for the builtin policy layer. You select one with --policy-preset. Presets replace the builtin layer's cmd_allow and cmd_deny lists while leaving arg_rules, path_rules, and net_rules unchanged. Seven presets are built in:
ops_safe for read-only exploration or dev_sandbox for hands-on code work. The CI presets require specific runtime conditions (--jail-root, batch mode) and are not meant for interactive evaluation.| Preset | Description |
|---|---|
| read_only | Allows inspection commands only: ls, cat, grep, find, ps, df, git status, git log, and similar. Denies everything that writes, deletes, or escalates privilege. Use this when you want to let an AI agent observe a system without being able to change anything. |
| dev_sandbox | Allows developer workflow commands: git, make, compilers, package managers (pip, cargo, go, npm). Blocks disk destructors and privilege escalation. A reasonable starting point for AI-assisted coding workflows. |
| ops_safe | The default. A small conservative set of read and repository commands. Denies shells, interpreters, most write operations, and all privilege commands. |
| ci_build | Unattended build and test pipeline. All allowed commands carry CONFIRM_NONE — confirmation prompts would hang a headless CI runner. Safety comes from the allow list boundary and --jail-root. Requires --jail-root to constrain file operations. |
| ci_deploy | Unattended deployment pipeline. Superset of ci_build adding remote transfer, container operations, Kubernetes, Helm, Terraform, and service restart. Destructive operations (kubectl delete, helm uninstall, terraform destroy) remain in deny. |
| ci_admin | CI/CD infrastructure management with mixed confirmation levels. Reads and inspections at CONFIRM_NONE; writes and destructive operations at CONFIRM_ACTION; terraform destroy and interactive shells at CONFIRM_TYPED. Intended for supervised admin sessions. |
| danger_zone | Minimal restrictions. A wildcard allow rule permits most commands, but the built-in deny list (shells, interpreters, disk destructors, privilege escalation) still applies. Use cautiously and only when you have a good reason. All commands require typed confirmation. |
Whatever preset you choose, confirmation levels and risk scoring always apply on top of it. A high-risk command does not bypass confirmation just because the preset allows it.
When the policy engine allows a command, it also assigns a confirmation level that tells the executor how much human review is required before the command actually runs. There are four levels:
| Level | Meaning |
|---|---|
| none | Proceed immediately — no human review needed. |
| plan | Show the plan to a human before executing; review is suggested. |
| action | Require explicit per-command human approval. |
| typed | The human must type a confirmation code derived from the exact command text. |
Your policy rules can set a confirmation level for any allowed command. But the engine also scores every command for risk on a scale of 0 to 100, and will automatically raise the confirmation level if the score is high enough:
planactiontypedRisk-based escalation is strictly one-way. The risk score can only raise a confirmation level, never lower it. Within a single policy layer, the flag catalog similarly only raises. Cross-layer behaviour is different: policy layers are scanned highest-authority first, and the first layer that produces any match wins outright — lower-authority layers are not consulted for their allow rules, their deny rules, or their confirmation levels. A user-layer allow rule with confirm: none therefore takes effect even if a base-layer rule would have required typed, because the base layer is not read. See §09 for the layering model and how to position rules that must not be overridden.
Some representative scores: ls and cat score 0–5 and require no confirmation. git scores 20 and requires a plan-level review. curl and wget score 60 and require action confirmation. rm scores 80 and requires typed confirmation. dd, parted, mkfs, and wipefs score 95–98, also requiring typed confirmation. Arguments make the score worse: targeting a system path adds 15 points, a recursive flag on a destructive command adds 10, --force with rm or mv adds 10.
For real use, you do not call the policy engine directly. You pass a JSON plan to aishell-gate-exec, and it handles the rest. The plan format is simple: a goal description, an optional source label, an optional strategy, and an array of commands.
echo '{
"protocol": {"name": "aishell-gate-exec-input", "version": "1.0"},
"goal": "check repository state",
"source": "ai",
"strategy": "fail_fast",
"actions": [
{"cmd": "git pull"},
{"cmd": "git status"},
{"cmd": "npm test"}
]
}' | ./aishell-gate --policy-preset dev_sandbox
The executor submits each command to the policy engine in sequence, reads the JSON verdict back, collects human confirmation if the confirmation level requires it, and calls execve() with the validated argument array from the verdict. The raw command string never touches a shell.
The strategy field controls what happens when a command is denied or fails. fail_fast stops at the first problem. best_effort continues through failures and reports what happened.
aishell-gate, aishell-gate-exec, and aishell-gate-policy all live in the same directory and you run from that directory, no --policy-binary flag is ever needed. The executor locates the policy engine at ./aishell-gate-policy by default, and the launcher handles co-location automatically. You only pass --policy-binary explicitly when binaries are installed elsewhere and you are calling aishell-gate-exec directly rather than via the launcher.Because the executor reads a JSON plan from standard input, connecting a real AI model is a pipe — the model generates the plan, the gate evaluates and executes it. For a worked example using a local inference backend, see the AI Pipeline Script section of the Remote Deployment Guide.
In practice you want the model to generate commands appropriate for a goal you describe, not repeat back a fixed structure. A system prompt that instructs the model to return only JSON — no explanation, no markdown — is the integration point. The minimal working system prompt:
You are a Unix operations assistant. When given a goal, respond with ONLY
a valid JSON object — no explanation, no markdown, no code fences.
Format:
{
"goal": "the goal as stated",
"source": "ai",
"strategy": "fail_fast",
"actions": [
{"type": "command", "cmd": "command here"}
]
}
Rules: use only simple Unix commands. No shell pipelines, semicolons, or
redirects. One command per action. No text outside the JSON object.
The script bin/aishell-pipe.sh in the distribution wraps this with pre-flight checks and exit code reporting. Adapt the model invocation to your inference backend — any command that writes a valid JSON plan to stdout works in its place.
You can pipe a single command to the policy engine directly, without using the executor or writing a JSON plan. This is useful for scripting, testing, or integrating the policy check into other tools.
echo "git status" | ./aishell-gate-policy --policy-preset dev_sandbox
Add --json to get the full machine-readable output:
echo "git status" | ./aishell-gate-policy --policy-preset dev_sandbox --json
The JSON output includes the overall decision, the confirmation level, the matched rule, the policy layer that matched it, the validated argument array, the risk score and flags, the blast radius classification, and a short plain-text summary suitable for display to a human.
If you are integrating this into a script, the exit code from the policy engine is always 0 when it runs without internal error — regardless of whether the decision was ALLOW or DENY. The decision itself lives in the JSON output. Exit code 1 means an internal error; exit code 2 means a usage problem. This allows the policy engine to be used in pipelines without being mistaken for a command failure. The calling script reads the JSON to determine what happened.
aishell-gate-policy with --json and parse the result. If the thought had not occurred yet: this is how you bolt the gate's reasoning into systems that do their own execution.Presets are a starting point. For real use you will want to layer your own rules on top. Three optional JSON files are evaluated on top of the selected preset:
aishell-gate-policy_base.json — organization-level defaultsaishell-gate-policy_project.json — project-specific rulesaishell-gate-policy_user.json — per-user preferences on topBy default the engine looks for all three files in the current directory. You can point to different paths with --policy-base, --policy-project, and --policy-user. If a file is absent it is silently ignored. If it exists but cannot be parsed, the engine fails closed and reports the problem.
Here is a minimal project policy that allows a few specific git commands without confirmation, requires action confirmation for npm publish, and blocks curl entirely:
{
"cmd_allow": [
{ "pattern": "git status", "confirm": "none", "reason": "safe read" },
{ "pattern": "git diff", "confirm": "none", "reason": "safe read" },
{ "pattern": "npm test", "confirm": "plan", "reason": "run tests" },
{ "pattern": "npm publish", "confirm": "action", "reason": "publish step" }
],
"cmd_deny": [
{ "pattern": "curl", "reason": "no outbound network in this env" }
],
"writable_dirs": [ "/home/user/myproject" ]
}
Save this as aishell-gate-policy_project.json in your working directory. It will be picked up automatically on the next run.
A few things worth knowing. Rule lists append to the preset by default. If you want your project policy to replace the preset's cmd_allow list entirely rather than extend it, add "cmd_allow_replace": true alongside your cmd_allow array. Unknown keys in a policy file are an immediate hard error, not a silent no-op — a typo like "cmd_denny" will fail the load and tell you what went wrong. A failed load has zero effect on the running policy; the engine restores the previous state atomically.
Run --help-policy at any time to print the complete policy format reference:
./aishell-gate-policy --help-policy
Beyond allowing or denying commands by name, you can write rules that match on specific arguments or on the paths a command targets. These let you allow a command in general while blocking specific dangerous invocations of it.
An argument rule matches a glob pattern against each argument of a given command. This example denies rm with the -rf flag regardless of what the preset would otherwise say:
{
"arg_rules": [
{ "cmd_pattern": "rm", "arg_glob": "-rf", "decision": "deny",
"reason": "rm -rf blocked by project policy" }
]
}
A path rule matches against the canonicalized path of any argument. Path rules apply only to commands that the engine classifies as write-capable — read-only commands like ls or cat are not subject to path rule evaluation. This example denies any write command that targets the /etc tree:
{
"path_rules": [
{ "path_glob": "/etc/*", "decision": "deny",
"reason": "writes to /etc are not permitted" }
]
}
Network rules work the same way but match against the hostname extracted from URL arguments. The built-in default base policy already denies 169.254.* and metadata.google.internal to block cloud metadata endpoint access. AIShell-Gate enforces a network default-deny model in ops_safe, read_only, and dev_sandbox presets: any command with a detected network target must have an explicit net_rules allow entry, or it is denied. Add a "net_default_deny": false key to your policy file to opt out for CI pipelines that need unrestricted registry access.
Every evaluation can be written to a tamper-evident JSON Lines audit log. Add --audit-log with a path to the policy engine to enable it:
./aishell-gate-policy --policy-preset ops_safe --audit-log ./policy.jsonl
Each log entry carries a sequence number, session identifier, the full decision context, and a SHA-256 hash that links it to the previous entry. A gap in sequence numbers or a hash mismatch identifies deleted or altered entries. Verify the chain at any time without interrupting operation:
./aishell-gate-policy --audit-verify ./policy.jsonl
For environments that need authenticated audit trails, generate a key and enable HMAC-SHA256 mode:
head -c 64 /dev/urandom > audit.key && chmod 640 audit.key
./aishell-gate-policy --audit-key audit.key \
--audit-log ./policy.jsonl \
--policy-preset ops_safe
With a key, only a holder of that key can forge valid chain hashes. The policy key file contains 64 raw binary bytes. The audit log file is append-only; the engine does not rotate it. Protect it with appropriate filesystem permissions.
| Policy log | Exec log | |
|---|---|---|
| Chain field in JSON | entry_hash | chain_hmac |
| Chain algorithm | SHA-256 sentinel-substitution | HMAC-SHA256 |
| Verifier | aishell-gate-policy --audit-verify | aishell-gate-exec --audit-verify |
| Default log path | none (off unless --audit-log passed) | /var/log/aishell/audit.log |
| Env var for log path | none | AISHELL_EXEC_AUDIT_LOG |
| HMAC key file format | 64 raw binary bytes | 64 ASCII hex characters (32 bytes) |
| Generate key | head -c 64 /dev/urandom | dd if=/dev/urandom bs=32 count=1 | xxd -p | tr -d '\n' |
The two HMAC key file formats are not interchangeable. A policy key fed to the executor (or vice versa) will fail key validation at load time.
The executor has its own separate audit log for the execution side. Add --audit-log to the launcher invocation to enable it:
./aishell-gate \
--policy-preset ops_safe \
--audit-log ./exec.jsonl
The --jail-root flag tells the policy engine to enforce path containment during evaluation. Any write-like command whose path arguments fall outside the jail root will be denied, regardless of what the policy rules say:
./aishell-gate-policy --jail-root /home/user/myproject --policy-preset dev_sandbox
This is the only sandbox enforcement that happens inside the policy engine itself. The other sandbox modes (chroot, container, userns) are advisory hints that are passed through to the executor in the JSON output, where the surrounding infrastructure can act on them.
The jail root check is strict about directory boundaries. A jail root of /tmp/jail will not accidentally allow /tmp/jailbreak/x — the path must be actually inside the named directory, not merely share its prefix.
Here is what a full setup looks like for a developer workflow: a project policy file, an audit log, a jail root, and a plan passed through the executor.
First, create the project policy file:
# aishell-gate-policy_project.json
{
"cmd_allow": [
{ "pattern": "git status", "confirm": "none" },
{ "pattern": "git diff", "confirm": "none" },
{ "pattern": "git pull", "confirm": "plan" },
{ "pattern": "make", "confirm": "plan" },
{ "pattern": "npm test", "confirm": "plan" }
],
"cmd_deny": [
{ "pattern": "curl", "reason": "no outbound network" },
{ "pattern": "wget", "reason": "no outbound network" }
],
"writable_dirs": [ "/home/user/myproject" ]
}
Then run a plan through the launcher with audit logging:
echo '{
"goal": "pull latest and run tests",
"source": "human",
"strategy": "fail_fast",
"actions": [
{"cmd": "git pull"},
{"cmd": "make clean"},
{"cmd": "npm test"}
]
}' | ./aishell-gate \
--policy-preset dev_sandbox \
--audit-log ./exec.jsonl
The executor will submit each command to the policy engine. git pull scores in the plan-level range — the executor pauses and shows the plan for human review. After confirmation, it executes via execve(). If any action is denied, fail_fast stops the sequence immediately. A tamper-evident audit entry is written for each decision.
When the executor reaches a command that requires human approval, it pauses and prints a prompt to your terminal. The exact text depends on the confirmation level.
At the plan level — an entire plan that contains one or more plan-level actions is displayed as a numbered list, followed by a single yes-or-no gate:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PLAN REVIEW (3 actions)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[1] ALLOW git pull
[2] ALLOW make clean
[3] ALLOW npm test
Proceed with this plan? [y/N]
Type y (or yes) and press Enter to proceed. Anything else — empty input, n, Ctrl-C — is treated as a refusal and no commands run.
At the action level — each such action fires its own approval prompt after the plan evaluation summary:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CONFIRMATION REQUIRED (action 2)
Level: action
Command: curl https://example.com/install.sh
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠ Explicit operator approval required.
Review the evaluation summary above carefully.
Approve? [yes/NO]
Only the literal string yes is accepted as approval at this level. Anything else is refusal.
At the typed level — the highest tier, used for truly destructive operations — the prompt displays a challenge code derived from the exact command text. The operator must type that code back:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CONFIRMATION REQUIRED (action 1)
Level: typed
Command: rm -rf /home/user/myproject/build
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠ HIGH-RISK — Command-specific typed confirmation required.
Carefully read the command above, then type the code below.
This code is derived from the exact command text.
Challenge code: A7K2-9QWM
Type code:
A mismatch between what the operator types and the challenge code is treated as refusal. The code is a friction-on-purpose mechanism: it cannot be muscle-memory approved, and it forces visual confirmation that the command about to run is the command intended.
A few things are worth being explicit about, especially if you are coming from a security background.
No quoted arguments. Quotes are shell syntax, and the engine deliberately rejects all shell metacharacters before tokenization. This is intentional: supporting shell quoting would require implementing a shell grammar subset, introducing the same parsing ambiguity and injection surface the system is designed to eliminate. A command like grep 'hello world' file.txt cannot be expressed; the single quotes are rejected outright. Arguments with spaces are not supported.
Network rules match hostnames, not resolved IPs. A rule blocking example.com will not catch a URL that resolves to the same IP through a different hostname. Network rules capture intent, not strong enforcement — use firewall rules for that.
The engine is not a sandbox. It decides whether a command should run before it runs. It does not intercept what the command does while running. It complements OS-level access controls, kernel sandboxing, and proper permission management; it does not replace them.
The engine does not guarantee security against a determined attacker with local system access. It raises the cost and visibility of unsafe AI-generated actions and ensures that every attempt is recorded. That is what it is designed to do.
Before the engine evaluates any command at all, it checks whether the current session is permitted to make requests. Session policy can gate evaluation on the caller's uid or gid, their username, whether they are in an SSH session, whether stdin is a TTY, the session mode (interactive, batch, or daemon), and the time of day.
Session constraints go in the "session" key of any policy file. Here is an example that restricts operation to two specific users and denies access from SSH sessions:
{
"session": {
"allow_users": ["alice", "bob"],
"deny_ssh": true
}
}
Session evaluation happens before any command rule is checked. If the session does not satisfy policy, the engine returns a denial without evaluating the command at all. This makes session policy a hard outer gate on the entire system.
You now know how to use AIShell-Gate interactively at a terminal: the policy engine explains its decisions; the executor enforces them. The same gate, the same policy file, and the same audit chain extend to two further deployment shapes:
authorized_keys entry forces aishell-gate as the command, and every plan the agent submits is evaluated by the same policy engine you just learned. Human confirmation at higher levels is relayed to an operator in a second terminal via aishell-confirm. The Remote Deployment Guide covers the full setup: the dedicated ai-agent account, constrained-account hardening (making aishell-gate-exec the only binary the account can reach), the forced SSH command configuration, and the operator relay. The same guide also covers outgoing pipelines — invoking an AI model locally and piping its JSON plan to the gate — for users who want to drive the gate from a script rather than via an inbound SSH connection.evaluate_plan and execute_plan as tools; the AI can inspect plans before committing and execute gated plans under the same policy. No pipeline scripting, no SSH configuration../aishell-gate-policy
./aishell-gate --policy-preset ops_safe --audit-log ./exec.jsonl
echo '<json-plan>' | ./aishell-gate --policy-preset dev_sandbox
echo "git status" | ./aishell-gate-policy --policy-preset dev_sandbox --json
cat plan.json | ./aishell-gate \
--policy-preset dev_sandbox \
--audit-log ./exec.jsonl
cat plan.json | ./aishell-gate \
--policy-preset ops_safe \
--dry-run-json
./aishell-gate-policy --policy-preset ops_safe --test-plan tests/policy_tests.json
./aishell-gate-policy --audit-verify ./policy.jsonl
./aishell-gate-policy --help-policy
For complete option documentation, policy file syntax, session policy fields, exit codes, and known limitations, see the man page: aishell-gate-policy(1). For connecting a local inference model as a source of JSON plans, see the AI Pipeline Script section of the Remote Deployment Guide. For the security architecture and design rationale, see the AIShell-Gate white paper.