AIShell-Gate — Deployment Guide

Running a Remote AI Through
AIShell-Gate Using SSH

How to allow an AI system to submit controlled command plans for execution on a remote machine

Copyright © 2026 AIShell Labs LLC. All Rights Reserved. — www.aishell.org/aishellgate

01 Overview

This guide explains how to allow an AI system running on one machine to submit controlled command plans for execution on another machine using AIShell-Gate.

The goal is not to give the AI a shell. Instead, the AI submits a structured JSON plan that passes through a policy gate before any command is executed. This preserves security while still allowing meaningful automation.

Why SSH The choice of SSH as the transport is not incidental. The forced command pattern used here is over twenty-five years old and already trusted in production environments for exactly this kind of capability-scoped access. Git uses it (git-shell). rsync uses it. rdiff-backup and restic use it. The entire model — restricted account, no login shell, one forced command — is the established Unix answer to the question "how do I expose a single capability over the network without exposing a shell." AIShell slots into that pattern without asking anyone to trust something new.

02 Architecture

The Two-Binary Design

AIShell-Gate consists of two cooperating binaries with strictly separated responsibilities:

This separation is the central security property of the system. The executor cannot approve or deny a command; only the policy engine can, and it does so in a separate process.

Deployment Flow

AI runtime JSON action plan (via stdin) SSH transport Restricted account (ai-agent, /bin/false shell) Forced SSH command reads plan from stdin aishell-gate-exec ──── spawns ────► aishell-gate-policy JSON decision │◄─────────────────────────────── policy verdict (after human confirmation if required) execve() command runs
Note: The AI never interacts with a shell. Every command must pass through aishell-gate-policy before aishell-gate-exec will call execve().
The daemon-free property Most tools that broker remote command execution require a listening daemon on the target machine: a new service, a new port, a new attack surface, a new thing that can crash or be misconfigured. This design has none of that. The only process listening is sshd, which is already running, already audited, already monitored. When the AI is not active there is nothing extra running at all. The process model is fork-on-connection — which is exactly what sysadmins expect from SSH and exactly what makes it easy to reason about.

Security Layers

The design creates multiple independent security boundaries. Even if one layer fails, the others continue to protect the system.

LayerPurpose
AI runtimeDecision engine only — never touches the OS directly
SSH transportEncrypted connection; identity bound to a specific key
Restricted userDedicated account with no login shell and minimal permissions
Forced commandPrevents arbitrary shell access regardless of client request
aishell-gate-policyEvaluates each command against policy; returns allow or deny
execve()Direct kernel exec — no shell interpolation or PATH search

03 Installation

Both binaries must be present on the machine that will execute commands. The executor calls the policy engine as a subprocess, so both must be reachable.

Recommended locations:

/usr/local/bin/aishell-gate-policy
/usr/local/bin/aishell-gate-exec

Ensure both are executable:

chmod 755 /usr/local/bin/aishell-gate-policy
chmod 755 /usr/local/bin/aishell-gate-exec
Important: Neither binary may be setuid or setgid. aishell-gate-exec checks this at startup and will refuse to run if either condition is true, logging the violation to the audit trail. Keep both binaries owned by root and not world-writable.
Per-host policy Because both binaries live on each target machine, policy lives where execution lives. There is no centralized policy server. This means policy changes require touching the config or the forced command on each host — which for a large fleet means doing it through whatever config management you already use (Ansible, Salt, Puppet, a deploy script). Whether that feels like a limitation or a feature tends to depend on your environment. In practice, the absence of a central policy service is also the absence of a central point of failure and a central target for policy tampering. Each host is self-contained and auditable in isolation.

04 SSH Configuration

Create a Dedicated AI User

Create an account whose sole purpose is to receive AI command plans via SSH:

useradd -r -s /bin/false ai-agent

This account has no interactive login shell and no home directory access. It exists only to receive the forced SSH command.

Access control the Unix way Revoking the AI's access is a single operation: remove the key from authorized_keys, or delete the file entirely. No service restart, no API call, no database entry. Key rotation follows the same workflow as any other SSH key. Granting access to a second AI agent is adding another key with its own forced command, which can point at a different preset or a different jail root. All of this lives in flat files under normal Unix permissions, visible to the usual audit tools, manageable by the usual automation.

Configure the Forced Command

Edit ~ai-agent/.ssh/authorized_keys and add the AI's public key with a forced command:

command="/usr/local/bin/aishell-gate-exec \
  --policy /usr/local/bin/aishell-gate-policy \
  --preset ops_safe \
  --audit-log /var/log/aishell_ai.log",\
no-port-forwarding,no-agent-forwarding,no-X11-forwarding \
ssh-rsa AAAA...

With this in place, any SSH connection from the AI — regardless of what command the client requests — will run aishell-gate-exec with the specified options. The AI cannot bypass the gate.

Note: The --policy flag must be an explicit path containing a / character. Bare names are rejected at startup to prevent PATH-based substitution. The path is resolved with realpath(3) before any subprocess is forked.

Commonly Used Flags

FlagPurpose
--policy <path>Absolute path to aishell-gate-policy binary (required)
--preset <name>Named policy preset: read_only, ops_safe, dev_sandbox
--audit-log <file>Write executor audit log to this path
--jail-root <path>Restrict write-class commands to this directory tree
--eval-timeout <n>Policy engine evaluation timeout in seconds (default: 30)
--confirm-tty <path>Single-session interactive use only. Reads confirmation prompts from the given PTY device instead of /dev/tty. Not suitable for multi-session remote deployments — use --confirm-pipe instead. See § 09.
--confirm-pipe <base>Secure pipe-based confirmation relay. aishell-confirm creates two FIFOs (BASE.req, BASE.resp) owned by the operator; aishell-gate-exec exchanges JSON requests and plain-text responses through them. The operator sees full command context; ai-agent never opens any PTY. Use with --confirm-lock for multi-session safety. Default: /run/aishell-gate/confirm. See § 09.
--confirm-lock <path>Serialise concurrent sessions: only one may be in the confirmation phase at a time. Required when using --confirm-pipe with multiple AI agents. Default: /run/aishell-gate/confirm.lock. See § 09.
--verboseEmit diagnostic output to stderr

Less common policy engine flags that aishell-gate-exec does not recognise natively can be passed after a -- separator in the forced command:

command="/usr/local/bin/aishell-gate-exec \
  --policy /usr/local/bin/aishell-gate-policy \
  --preset ops_safe -- --mode batch"

05 JSON Plan Format

aishell-gate-exec reads a JSON plan from standard input. The plan describes the AI's goal and the list of actions to carry out. The only required field is actions.

{
  "goal":    "check disk usage and list recent logs",
  "source":  "ai",
  "actions": [
    {"cmd": "df -h"},
    {"cmd": "ls -lt /var/log"}
  ]
}
FieldDescription
goalHuman-readable description of intent. Included in the audit log. Optional but recommended.
sourceProvenance label for the request. Defaults to "ai" if omitted.
strategyExecution strategy: fail_fast (default) or best_effort. Controls whether a denied action stops the plan.
actionsArray of command objects. Each has a cmd field containing the complete command string. Maximum 32 actions per plan.

Commands are given as a single cmd string — arguments are not supplied separately. aishell-gate-policy tokenizes the command string and produces a validated argument vector. aishell-gate-exec passes that vector to execve() directly, without invoking a shell.

Why no shell injection is possible here The policy engine tokenizes the command string itself and hands the executor a validated argv array. That array goes straight to execve(). There is no shell anywhere in the execution path — not as an interpreter, not as a subprocess, not as an intermediate step. Shell metacharacters in a command string are inert: they reach the policy engine tokenizer as literal characters, and whatever command results from that tokenization is evaluated against policy on its own terms. A sysadmin reviewing this design does not have to reason about quoting edge cases, argument splitting, or whether some combination of inputs might slip a character past a shell. That entire attack surface is absent by construction.

06 Sending Plans

The AI sends its plan over SSH via standard input:

ssh ai-agent@server <<'EOF'
{
  "goal":    "check disk usage and list recent logs",
  "source":  "ai",
  "actions": [
    {"cmd": "df -h"},
    {"cmd": "ls -lt /var/log"}
  ]
}
EOF

The forced command defined in authorized_keys intercepts the connection. The AI's plan arrives on the stdin of aishell-gate-exec. Whatever the SSH client requested is ignored.

Note: Use a single-quoted heredoc delimiter ('EOF') to prevent the local shell from expanding variables or backslash sequences inside the JSON before it is sent.
Stateless by design Each plan submission is a complete, self-contained transaction: connect, deliver stdin, run, disconnect. There is no persistent session, no accumulated context, no long-lived process the AI can park itself in between invocations. If something goes wrong mid-plan the connection dies and nothing lingers. There is no state across connections for the AI to exploit or corrupt — each submission starts from zero. Sysadmins who have dealt with misbehaving long-running daemons will recognize this as a deliberate structural virtue.

07 Policy Decisions

For each action in the plan, aishell-gate-exec submits the command to aishell-gate-policy as a child process and reads back a structured JSON decision. Key fields in that decision:

FieldMeaning
decisionallow or deny
confirmRequired confirmation level: none, plan, action, or typed
layerPolicy layer that produced the decision
reasonHuman-readable explanation
risk.scoreInteger 0–100
risk.blast_radiussingle, tree, system, or unknown
argvValidated argument vector — passed to execve() on ALLOW decisions
suggestionsAllowed alternatives — present on DENY decisions only

If the decision is DENY, the action is refused and the reason is reported. If the decision is ALLOW, the executor proceeds according to the confirmation level.

Everything already in the toolbox Because execution happens as a normal process under the ai-agent account, the full range of standard Unix observability tools applies without adaptation. ps, top, lsof, and strace see honest process trees. Audit logging goes to a file in /var/log, and SSH logs to syslog — both feed existing log aggregation pipelines without requiring adapters or agents. There is nothing hidden behind a proprietary protocol, no opaque daemon to peer into, no special tooling needed to answer the question "what is the AI actually doing right now."

08 Confirmation Levels

Some commands require a human operator to confirm before execution. This is a policy decision — neither the AI nor the executor controls it. Confirmation for all actions is collected in a single pass before any command runs, so a mid-plan refusal cannot leave the system in a partially-executed state.

LevelBehaviour
noneProceed without any confirmation prompt.
planPrompt once before execution begins. Operator types y to proceed.
actionPrompt for each individual command. Operator types yes.
typedOperator must type a short challenge code derived from the exact command text. Used for high-risk operations where muscle memory alone should not suffice.

By default, aishell-gate-exec opens /dev/tty for confirmation prompts — the controlling terminal of the process, separate from stdin. This works correctly in any interactive session.

Non-interactive SSH sessions (v0.21 and earlier): In a forced-command SSH session there is no controlling terminal, so /dev/tty cannot be opened. In v0.21 and earlier, the executor fell back to reading from stdin. Because the AI's JSON plan arrives on stdin and the pipe is still open when the confirmation fires, the process blocked indefinitely — the SSH connection hung with no error. This is fixed in v0.22.0. The executor now exits immediately with code 2 and a clear diagnostic. See § 09 — Remote Human Confirmation for how to keep the human in the loop.
The confirmation system is working when it fires A confirmation requirement surfacing during a remote session is the policy engine doing exactly what it was designed to do — flagging a command that a human should review before it runs. The right response is not to suppress it. Either choose a preset that produces confirm: none for the commands the AI will run, or wire up --confirm-pipe and aishell-confirm so a human can respond. Test the exact plan shapes the AI will submit against the exact preset you have configured before going to production. Any action showing a confirmation level other than none belongs either in a workflow where a human is present, or in a policy rule that explicitly reduces its requirement.

09 Remote Human Confirmation

When the AI operates over SSH and the policy engine requires a human confirmation, the question is: where does the prompt appear, and how does the operator answer it? The --confirm-pipe flag, combined with the aishell-confirm companion tool, provides the secure answer.

How it works

The operator opens a second SSH session to the remote host and runs aishell-confirm. That tool creates two named FIFOs — confirm.req and confirm.resp — owned by the operator with a shared group. When a confirmation fires, aishell-gate-exec writes a JSON request to confirm.req containing the full command context: command text, goal, source, risk score, blast radius, policy reason, and challenge code for typed confirmations. aishell-confirm reads it, displays everything on the operator's terminal, reads the operator's response, and writes it back through confirm.resp. The ai-agent account never opens any PTY device.

AI runtime (remote machine — no terminal) JSON plan via SSH stdin aishell-gate-exec ──── policy check ────► aishell-gate-policy │ confirm: action │◄────────────────────────────────────────────── writes JSON request → confirm.req FIFO aishell-confirm (operator's SSH session — owns the FIFOs) displays: cmd, goal, risk, reason, challenge operator terminal operator types response → confirm.resp FIFO aishell-gate-exec execve() command runs
Security property: ai-agent never opens any PTY device. The operator's Unix credentials — owning the FIFO — are what authorise the confirmation response. Nothing useful reaches the AI's stderr channel: the evaluation summary, challenge code, and operator response all flow through the FIFOs, not through the AI's SSH connection.

Step-by-step setup

Step 1 — One-time: create a shared group and the runtime directory.

The confirmation FIFOs must be accessible by both the operator and the ai-agent account. The cleanest model is a dedicated shared group:

# Create shared group and add both accounts
sudo groupadd aishell-gate
sudo usermod -aG aishell-gate operator
sudo usermod -aG aishell-gate ai-agent

# Create the runtime directory with the shared group and setgid bit
sudo mkdir -p /run/aishell-gate
sudo chown root:aishell-gate /run/aishell-gate
sudo chmod 2770 /run/aishell-gate   # setgid: new files inherit aishell-gate group

With this in place, aishell-confirm (running as the operator) creates FIFOs with mode 0660 and group aishell-gate. aishell-gate-exec (running as ai-agent, a member of aishell-gate) can open them for reading and writing. Neither account needs PTY group membership.

Step 1b — Make the directory survive reboots.

/run is a tmpfs memory filesystem on virtually every modern Linux system — it is created fresh on every boot. Without action, /run/aishell-gate/ disappears at shutdown. On the first AI connection after a reboot, aishell-gate-exec will fail to open the lock file, exit with code 5, and refuse all plans. On systemd systems, a tmpfiles.d drop-in recreates the directory automatically before sshd starts:

sudo tee /etc/tmpfiles.d/aishell-gate.conf <<'EOF'
d /run/aishell-gate 2770 root aishell-gate -
EOF
sudo systemd-tmpfiles --create /etc/tmpfiles.d/aishell-gate.conf
ls -ld /run/aishell-gate   # verify: should show drwxrws--- root aishell-gate
Non-systemd systems: Add the mkdir, chown, and chmod commands from Step 1 to /etc/rc.local (or the equivalent early-boot script for your init system) so the directory is recreated before sshd starts on each boot.

Step 2 — Each session: operator arms the relay with aishell-confirm.

# Operator's own SSH session — keep this window open
ssh operator@remotehost
$ aishell-confirm
[aishell-confirm] Terminal:  /dev/pts/3
[aishell-confirm] Req  FIFO: /run/aishell-gate/confirm.req
[aishell-confirm] Resp FIFO: /run/aishell-gate/confirm.resp
[aishell-confirm] Status:    armed — waiting for confirmation requests
[aishell-confirm] Press Ctrl-C to disarm.

# Full confirmation requests will appear here as the AI submits plans

Step 3 — Add --confirm-pipe and --confirm-lock to the forced command in authorized_keys.

command="/usr/local/bin/aishell-gate-exec \
  --policy /usr/local/bin/aishell-gate-policy \
  --preset ops_safe \
  --confirm-pipe /run/aishell-gate/confirm \
  --confirm-lock /run/aishell-gate/confirm.lock \
  --audit-log /var/log/aishell/audit.log",\
no-port-forwarding,no-agent-forwarding,no-X11-forwarding \
ssh-rsa AAAA...

Step 4 — AI submits a plan that triggers a confirmation. The operator sees the full context on their terminal:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  AISHELL-GATE CONFIRMATION REQUEST
  Session:  a3f8c21d9e4b7012...
  Action:   0   Level: action
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Command:  git push origin main
  Goal:     deploy release v2.4.1
  Source:   ai
  Reason:   modifies remote branch; requires explicit approval
  Risk:     72/100   blast=system
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Approve? [yes/NO]

The operator types yes (or anything else to refuse). The AI's SSH session receives the result immediately.

For confirm: typed (high-risk commands), the challenge code is displayed here — on the operator's terminal only — and the operator must type it back exactly:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  AISHELL-GATE CONFIRMATION REQUEST
  Session:  a3f8c21d9e4b7012...
  Action:   1   Level: typed
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Command:  rm -rf /var/tmp/old-builds
  Goal:     clean build artifacts
  Source:   ai
  Reason:   recursive delete; high blast radius
  Risk:     91/100   blast=system

  ⚠  HIGH-RISK — typed confirmation required.
  Type the challenge code exactly to confirm:
  Challenge: 3k7mw2nx
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Type code: 

The challenge code is derived from the exact command text. It is sent only through the pipe to the operator's terminal — it never appears in the AI's SSH channel. aishell-gate-exec verifies the operator's typed response independently.

What happens when the relay is not armed

If aishell-confirm is not running when a confirmation fires, aishell-gate-exec will find no reader on the request FIFO and fail immediately with exit code 5:

[gate-exec] ERROR: confirmation request pipe '/run/aishell-gate/confirm.req' has no reader.
[gate-exec]   aishell-confirm is not running in an operator session.
[gate-exec]   Start it: ssh operator@host  then run: aishell-confirm
[gate-exec]   No commands have been executed.

The AI receives exit code 5, the audit log records the failure, and no commands run. The operator needs to start aishell-confirm before the AI tries again.

Multiple concurrent AI sessions

Multiple concurrent AI sessions share the same FIFO pair. The --confirm-lock flag serialises the confirmation phase: only one aishell-gate-exec session can write to confirm.req and read from confirm.resp at a time. Other sessions block on the lock until the current session finishes. The lock is released before any execve() call, so command execution across sessions still proceeds in parallel. Without the lock, concurrent sessions would interleave their JSON frames on the shared FIFOs, producing unparseable requests.

Important: When multiple AI agents may connect simultaneously, both --confirm-pipe and --confirm-lock are required together. Neither is needed in a single-session interactive deployment using /dev/tty.

Single-session interactive deployments

For a single operator running a single AI session on a machine they are logged into interactively, no special setup is required. aishell-gate-exec opens /dev/tty by default and prompts directly on the controlling terminal. --confirm-tty is available for the rare case where the operator wants to redirect prompts to a specific device. Neither --confirm-pipe nor --confirm-lock is needed in this scenario.

Fully unattended deployments If the AI genuinely needs to operate with no human in the loop, the right answer is a policy preset — such as read_only or a custom policy file — that produces confirm: none for every command the AI will run. The confirmation system should then never fire. A confirmation requirement appearing in a fully automated session is the policy engine correctly identifying a command that warrants human review. That is its job; the right response is to fix the policy or add a human, not to work around the system.

10 Complete Examples

Read-Only Monitoring

Policy preset read_only. No confirmation required for listing and reading operations.

# Forced command in authorized_keys:
# command="/usr/local/bin/aishell-gate-exec \
#   --policy /usr/local/bin/aishell-gate-policy \
#   --preset read_only \
#   --audit-log /var/log/aishell_ai.log"

# AI sends this plan:
ssh ai-agent@server <<'EOF'
{
  "goal":    "check system health",
  "source":  "monitoring-agent",
  "actions": [
    {"cmd": "uptime"},
    {"cmd": "df -h /"},
    {"cmd": "free -m"}
  ]
}
EOF

Operations Work with a Jail Root

Policy preset ops_safe. The --jail-root flag restricts write-class commands to the specified directory tree.

# Forced command in authorized_keys:
# command="/usr/local/bin/aishell-gate-exec \
#   --policy /usr/local/bin/aishell-gate-policy \
#   --preset ops_safe \
#   --jail-root /srv/deployments \
#   --audit-log /var/log/aishell_ai.log"

# AI sends this plan:
ssh ai-agent@server <<'EOF'
{
  "goal":     "deploy updated configuration",
  "source":   "deploy-agent",
  "strategy": "fail_fast",
  "actions":  [
    {"cmd": "cp /srv/deployments/staging/app.conf /srv/deployments/prod/app.conf"},
    {"cmd": "systemctl reload myapp"}
  ]
}
EOF

Development Sandbox

Policy preset dev_sandbox allows a broader set of operations within a workspace. Using best_effort strategy so that a test failure does not prevent subsequent steps from running.

ssh ai-agent@devserver <<'EOF'
{
  "goal":     "update dependencies and run tests",
  "source":   "ci-agent",
  "strategy": "best_effort",
  "actions":  [
    {"cmd": "git pull"},
    {"cmd": "npm install"},
    {"cmd": "npm test"}
  ]
}
EOF

11 Security Considerations

Never Expose a Shell

The ai-agent account has /bin/false as its shell and the authorized_keys entry forces aishell-gate-exec for every connection. There is no path through which the AI can obtain an interactive shell.

Environment Variables

SSH may pass environment variables into the session. aishell-gate-exec sanitizes its execution environment and does not pass the ambient environment to child processes. Use no-user-rc and no-agent-forwarding in authorized_keys as additional precautions.

Binary Integrity

aishell-gate-exec checks at startup that neither it nor the aishell-gate-policy binary is setuid or setgid. If either check fails, execution halts immediately and the violation is written to the audit trail before exit. Keep both binaries owned by root and not world-writable.

No Shell Execution

aishell-gate-policy tokenizes the command string itself and returns a validated argv array. aishell-gate-exec passes that array to execve() directly. No shell is invoked at any point. Shell metacharacters in a command string are inert — any command that tries to exploit them will be evaluated against policy on its literal terms.

Least Privilege

The ai-agent account should have the minimum permissions required for the AI's work: access to specific paths, no sudo, no writable home directory, no membership in privileged groups.

Audit Logging

Specify --audit-log in the forced command so every plan submission is recorded. The executor log captures the plan source, each action, the policy engine decision, confirmation outcome, and execution result. The policy engine has its own separate audit log (--audit flag) that supports HMAC-SHA256 tamper-evident chaining for cryptographic log integrity when that level of assurance is required.

Each aishell-gate-exec invocation is an independent process with its own HMAC chain state. When multiple AI sessions write to the same audit log file, the file contains one internally consistent HMAC chain per session_id, not a single global sequence. When verifying chains, always operate per session_id; treating the entire log as a single linear sequence will produce false verification failures when sessions are concurrent.

For the HMAC chain to remain verifiable across restarts and sessions, a persistent key must be configured. Without one, each invocation generates a random per-session key that is discarded on exit, making post-hoc chain verification impossible. Configure a persistent key by one of these methods, in priority order:

  1. Set the AISHELL_AUDIT_KEY environment variable to a 64-character hex string (32 raw bytes) in the forced command environment.
  2. Create /etc/aishell/audit.key containing a 64-character hex string on the first line, readable by the ai-agent account. Mode 0640 with group ai-agent is appropriate.

If neither is configured, aishell-gate-exec emits a warning to stderr at startup. In a multi-session deployment this warning appears in the stderr of each AI's SSH session; if those streams are not monitored, the degraded mode may go unnoticed. The warning text includes the word ephemeral to make it grep-friendly in log aggregation.

Concurrent AI Sessions

Allowing multiple AI agents to connect simultaneously requires two mitigations: the --confirm-pipe / --confirm-lock combination described in § 09, and a persistent HMAC key for audit chain integrity as described above. Neither is required in a single-session deployment using /dev/tty, but both are required the moment a second AI agent can reach the host. The pipe design additionally eliminates the PTY access problem: ai-agent never needs tty group membership, and the operator sees full command context before responding to every confirmation request.

Why this earns trust The design earns sysadmin trust not by asking them to trust a new framework, but by not asking them to trust anything new at all. The security primitives — SSH keys, restricted accounts, forced commands, execve(), file permissions — are primitives they have already accepted and already operate. AIShell adds policy enforcement and a structured JSON channel in the middle of a pattern they already know. No new daemons, no new ports, no new firewall rules, no new monitoring agents, no new log formats to parse. The existing SSH logs and the aishell log file feed directly into whatever log aggregation is already running. Operators who need to understand what happened reach for the same tools they always reach for.

12 Exit Codes

aishell-gate-exec returns a precise exit code so the AI can distinguish between types of failure:

CodeMeaning
0All actions allowed, confirmed, and executed successfully
1One or more actions denied by policy
2Human confirmation refused, or no terminal available to present the confirmation prompt (see § 09)
3Policy engine process error (subprocess could not be started, or returned no output)
4JSON parse error in the input plan or in the policy engine response
5Usage or argument error, or startup security check failed
6execve() failure after a confirmed ALLOW decision

13 Conceptual Model

Traditional shells are designed to give a human direct, unmediated access to the operating system:

human  →  shell  →  operating system

AIShell-Gate inserts a deterministic policy gate between AI intent and OS execution:

AI  →  aishell-gate-exec  →  aishell-gate-policy  →  execve()  →  operating system

Instead of granting raw shell access, the system enforces policy, validates intent, and executes only what the policy engine has explicitly approved. The AI never touches a shell. The executor never makes a policy decision. The gate never bypasses itself.

Because each SSH connection is independent — connect, submit plan, disconnect — the system is stateless and predictable. There is no persistent channel for accumulated state or privilege to leak through.

The Unix answer If you sat down and thought carefully about how to give an AI controlled access to a Unix system in a way that would make a experienced sysadmin comfortable, you would probably arrive at something very close to this. You would reach for SSH because it is already there and already trusted. You would use a forced command because that is what forced commands are for. You would create a dedicated account because that is how you scope identity. You would call execve() directly because you know what shells do to arguments. The result is not a clever new thing — it is the application of well-understood primitives to a new problem. That is exactly the right kind of design.