AISHELL-GATE-CONFIRM(1)       AIShell-Gate Manual      AISHELL-GATE-CONFIRM(1)


"AIShell-Gate" Copyright (c) 2026 AIShell Labs LLC Winston-Salem NC USA.
All Rights Reserved. Author: Sean T. Gilley
Do not remove this notice.
Use of this Software requires a valid license.

THIS SOFTWARE IS PROVIDED BY AISHELL LABS LLC "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL AISHELL LABS LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

For full license terms see the LICENSE file or: www.aishellgate.com
                                                  



NAME
       aishell-gate-confirm  -  operator confirmation relay for remote AIShell-Gate
       deployments

SYNOPSIS
       aishell-gate-confirm [--pipe basepath] [--timeout secs]

       aishell-gate-confirm --help

EDITIONS
       aishell-gate-confirm is available in both the Standard and Enterprise
       editions. It implements the operator side of relay confirmation mode
       -- the --confirm-pipe and --confirm-lock path in aishell-gate-exec(1)
       -- which is available in both editions. Standard edition also
       supports single-session interactive confirmation via --confirm-tty,
       which does not require aishell-gate-confirm. See aishell-gate-exec(1)
       EDITIONS for the full Standard/Enterprise split.

DESCRIPTION
       aishell-gate-confirm is the operator-side relay for the AIShell-Gate confir-
       mation system. It creates a pair of named FIFOs, waits for confirmation
       requests from aishell-gate-exec(1), displays the full  command  context
       on  the  operator's terminal, reads the operator's response, and relays
       it back. It is required only in remote multi-session deployments; it is
       not used when an operator runs  aishell-gate-exec  interactively  at  a
       single terminal. Available in both editions (see EDITIONS above).

   The confirmation system
       When aishell-gate-policy(1) allows a command, it also assigns a confir-
       mation level that controls how much human review is required before the
       command runs. There are four levels:
       none     Proceed immediately -- no human review needed.
       plan     Show the full plan; operator types y to proceed.
       action   Require explicit per-command approval; operator types yes.
       typed    Operator must type an 8-character challenge code derived from the exact command text.

       Confirmation levels are set by policy rules and raised automatically by
       risk  score.  They can only be raised, never lowered. A command scoring
       40 or above is raised to at least plan; 70 or above to  action;  90  or
       above to typed.

       All  confirmations for a plan are collected in a single pass before any
       command runs. A mid-plan refusal cannot leave  the  system  in  a  par-
       tially-executed state.

   The remote confirmation problem
       In a remote deployment, the AI agent connects to the target machine via
       SSH  under  a restricted account with no interactive shell. When a con-
       firmation fires, aishell-gate-exec needs to present a prompt and read a
       response from a human operator. There is no controlling terminal on the
       AI's SSH connection. The question is: where does the prompt appear, and
       how does the operator respond?

       Earlier designs attempted to open the operator's  PTY  device  directly
       from the ai-agent account. This approach has three fatal flaws:

       o PTY  slave  devices  on  Linux  are mode 0620 (owner rw, group write-
         only). The tty group does not grant read  access,  so  fopen(3)  with
         "r+" fails with EACCES in any correctly configured deployment.

       o Granting  ai-agent  membership in the tty group gives it write access
         to every PTY on the machine, enabling keystroke  injection  into  any
         user's terminal session.

       o The operator would see only a bare prompt with no context -- the full
         command  description,  risk score, and challenge code were written to
         the AI's stderr channel, not to the operator's terminal.

   The FIFO relay design
       aishell-gate-confirm solves the remote confirmation problem by inverting who
       owns the channel.  The operator runs aishell-gate-confirm in their  own  SSH
       session. That tool creates two named FIFOs owned by the operator with a
       shared aishell-gate group:

       BASEPATH.req
              aishell-gate-exec   writes   JSON  confirmation  requests  here;
              aishell-gate-confirm reads them.

       BASEPATH.resp
              aishell-gate-confirm    writes     plain-text     responses     here;
              aishell-gate-exec reads them.

       The FIFOs are owned by the operator and accessible to the  ai-agent ac-
       count via the shared aishell-gate group. Neither account needs PTY mem-
       bership.  aishell-gate-exec never opens any PTY device. Every piece  of
       information the operator needs -- command text, goal, source, risk
       score, blast radius, policy reason, and the typed-level challenge code
       -- travels through the request FIFO to the operator's terminal. Nothing
       useful reaches the AI's stderr channel.

       aishell-gate-confirm  opens  both  FIFOs with O_RDWR rather than O_RDONLY or
       O_WRONLY. A FIFO opened O_RDONLY blocks until a writer appears;  O_RDWR
       returns immediately, keeping aishell-gate-confirm responsive from the moment
       it starts. This is documented POSIX behaviour for FIFOs on Linux.

   What the operator sees
       When a confirmation fires, aishell-gate-confirm displays the full context on
       the operator's 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]

       For  typed-level  confirmations,  the  challenge code is shown here and
       only here. It never appears in the AI's SSH channel:

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

       For plan-level confirmations, all planned actions are displayed  before
       the single y/N prompt, giving the operator a complete view of scope be-
       fore approving.

       The  typed  challenge  is an 8-character alphanumeric code derived from
       the command text using FNV-1a hashing.  Visually  ambiguous  characters
       (0, 1, i, l, o) are excluded so the operator can read and type the code
       without misreading a character. The code changes with every unique com-
       mand,  proving  the operator read the specific command being authorised
       rather than muscle-memorying a fixed response. 31^8 ~ 852 billion  com-
       binations.

OPTIONS
       --pipe basepath
              Base  path for the FIFO pair. BASEPATH.req and BASEPATH.resp are
              created here with mode 0660. The directory  must  exist  and  be
              writable by the operator.  Default: /run/aishell-gate/confirm

       --timeout secs
              Per-request timeout. If the operator does  not respond within
              this many seconds, aishell-gate-confirm  forwards an empty response
              to aishell-gate-exec, which treats it as a refusal and skips
              the action. The relay itself remains armed and continues
              waiting for further requests; only the individual unanswered
              request is treated as refused.  Default: 120.  Set to 0 to
              disable (legacy behaviour; not recommended).

              Raise this while first setting up a deployment.  120 seconds is
              sized  for  an  operator already watching the relay window, not
              for one who is still switching terminals, reading  the  request
              for  the first time, and deciding what to type.  --timeout 600
              is a reasonable value during setup and testing.

              An  expired  request  is  reported  on  the  operator's terminal
              ("read timed out ... treating as refusal") and on the AI side as
              a  closed or unreadable response pipe.  The AI-side wording de-
              scribes the pipe rather than the timeout, so a slow  confirmation
              can  look like a plumbing failure.  Check the operator's terminal
              before investigating the FIFOs.

       --help | -h
              Print a usage summary and exit.

ONE-TIME SETUP
       The following setup is performed once on the target machine as root. It
       installs the relay for the operator, creates the shared aishell-gate
       Unix group, and creates a runtime  directory  with  the setgid bit and
       a systemd-tmpfiles(8) drop-in so the directory survives reboots.

   Who is "operator"
       Two accounts are involved and only one of them is new.  ai-agent is the
       restricted account the AI connects to over SSH under a forced  command.
       The  operator  is  whichever ordinary human account approves confirma-
       tions -- if you log in as jsmith, then jsmith is the operator.  Substi-
       tute your own login name wherever this page writes "operator".  There
       is no need to create an account by that name.

       The split is the security property.  The ai-agent account is  deliber-
       ately unable to reach a terminal, so it cannot approve its own commands;
       the FIFO pair is the only channel between the two, and the human's pro-
       cess owns it.

   Install the relay
       aishell-gate-confirm is run by the operator, not by ai-agent, so it must
       be on the operator's PATH.  It ships in the package bin/ directory as
       aishell-gate-confirm.sh; install it without the extension to match this
       page and the deployment guides:

              install -m 755 <package>/bin/aishell-gate-confirm.sh \
                  /usr/local/bin/aishell-gate-confirm
              command -v aishell-gate-confirm

       Do  not  run it out of the ai-agent account's home directory.  That
       directory is writable by the AI's account, and the relay is the tool the
       human uses to approve the AI's commands.

   Shared group and runtime directory
       Create the aishell-gate group and add both accounts:
              groupadd aishell-gate
              usermod -aG aishell-gate operator
              usermod -aG aishell-gate ai-agent

       Create  the runtime directory with the aishell-gate group and the set-
       gid bit. New files inherit the aishell-gate group automatically:
              mkdir -p /run/aishell-gate
              chown root:aishell-gate /run/aishell-gate
              chmod 2770 /run/aishell-gate

       Make the directory survive reboots (/run is a tmpfs  on  virtually  all
       modern Linux systems and is recreated fresh on every boot):
              echo 'd /run/aishell-gate 2770 root aishell-gate -' \
                > /etc/tmpfiles.d/aishell-gate.conf
              systemd-tmpfiles --create /etc/tmpfiles.d/aishell-gate.conf

       On  non-systemd  systems,  add  the mkdir, chown, and chmod commands to
       /etc/rc.local or the equivalent early-boot script so the  directory  is
       recreated before sshd starts on each boot.

       Verify the result:
              ls -ld /run/aishell-gate
              # drwxrws--- root aishell-gate /run/aishell-gate

   New group membership requires a fresh login
       A  process  receives  its group list at login and never revises it.  An
       operator session that predates the usermod above still carries the  old
       list,  and  aishell-gate-confirm  will fail to create the FIFOs with a
       permission error that reads like a software fault.  Distinguish the two
       cases:

              getent group aishell-gate    # the database: lists operator, ai-agent
              id                           # this session: shows aishell-gate?

       If getent lists the account but bare id does not, the change  succeeded
       and  the  session  is stale.  Log out fully and back in.  On a desktop,
       closing terminal windows is not enough: every terminal inherits its
       groups from the graphical login session, so the desktop session itself
       must be restarted.  Over SSH, close the connection and remove any
       ControlMaster socket under ~/.ssh, which can silently reuse the old
       session.

       To  proceed  in  one  terminal without logging out, run  newgrp
       aishell-gate  -- it starts a subshell with the group applied.  This is
       per-terminal and does not persist.

PER-SESSION OPERATOR WORKFLOW
       1.  SSH  to  the  target  machine  in a dedicated window or tab and run
           aishell-gate-confirm:
           ssh operator@remotehost
           $ aishell-gate-confirm
           [aishell-gate-confirm] Terminal: /dev/pts/3
           [aishell-gate-confirm] Req FIFO: /run/aishell-gate/confirm.req
           [aishell-gate-confirm] Resp FIFO: /run/aishell-gate/confirm.resp
           [aishell-gate-confirm] Status: armed -- waiting for confirmation requests
           [aishell-gate-confirm] Press Ctrl-C to disarm.

       2.  Keep this window open for the duration of the AI session.

       3.  When the AI submits a plan that triggers a confirmation,  the  full
           request appears here. Type the appropriate response (y, yes, or the
           challenge code). The AI's session receives the result immediately.

       4.  Press Ctrl-C to disarm when done. Both FIFOs are removed cleanly on
           exit.

CONFIGURING AISHELL-GATE-EXEC
       Add  --confirm-pipe  and  --confirm-lock  to  the  forced  command  in
       authorized_keys.  Invoke aishell-gate(1) rather than aishell-gate-exec
       directly: the wrapper performs pre-flight checks and locates the policy
       engine  itself,  so --policy-binary is not needed, and it is the stable
       entry point for forced commands as the system evolves.

              command="/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-pty,no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA...

       THE ENTRY MUST BE ONE PHYSICAL LINE.  It is shown unwrapped above for
       that reason.  authorized_keys has no line-continuation syntax: a trail-
       ing backslash is not a continuation, it is part of the option text, and
       sshd  parses  each  physical line as a separate entry.  Splitting the
       entry across lines does not produce an error.  It fails open: the frag-
       ment  holding  the  key material is parsed as a valid entry with no
       command= option, the key authenticates normally, and the AI receives an
       ordinary interactive shell with the gate bypassed entirely.  Verify
       after every edit:

              awk '{print NR": "substr($0,1,60)}' ~ai-agent/.ssh/authorized_keys

       One numbered line per authorised key.  More than that means the entry
       is broken.  Then confirm the gate is actually in the path:

              ssh ai-agent@host      # must hang, not return a shell prompt
                                     # Ctrl-D -> "input is not a JSON object"

       A shell prompt here means the forced command is not being applied.

       Verify  the  path in command= against the installed location: packaged
       installs commonly land in /usr/local/bin, not /usr/bin.  Check with
       `command -v aishell-gate`.

       Note also that command= binds to the key, so it is only applied when
       that key authenticates.  If the ai-agent account also accepts a pass-
       word, password login bypasses the forced command and the gate with it.
       Lock it:  passwd -l ai-agent

       The --confirm-lock flag is required alongside  --confirm-pipe  whenever
       multiple  AI  agents may connect simultaneously. It serialises the con-
       firmation phase across concurrent sessions: only one  aishell-gate-exec
       session  may  write to the request FIFO and read from the response FIFO
       at a time. Other sessions block on an exclusive flock(2) until the cur-
       rent session finishes. The lock is released before any execve(2)  call,
       so command execution across sessions still proceeds in parallel.  With-
       out the lock, concurrent sessions would interleave their JSON frames on
       the shared FIFOs, producing unparseable requests.

TESTING THE RELAY
       Verifying  a  new  relay  installation  requires a command that is both
       ALLOWED and carries a confirmation level above none.  This is a  narrow
       target and the obvious candidates are the wrong ones.

       A denied command never reaches the relay.  Confirmations are  collected
       only  for  actions the policy engine has already allowed, so reaching for
       something dangerous -- rm -rf, chmod 777 -- produces a denial and an ap-
       parently  dead  relay.   Equally, a harmless command such as ls sits at
       confirm: none and is executed without ever  consulting  the  operator.
       Neither outcome indicates a fault.

       Preset matters more than the command.  Under read_only, git is  allowed
       only  as  the  three  read-only  subcommands git status, git diff, and
       git log; a bare git push or git commit has no matching allow  rule  and
       is  denied  by  default.  ops_safe allows git at confirm: action.  Under
       dev_sandbox, curl, wget, rsync, ssh, zip, unzip, and git are all allowed
       at confirm: action, which makes it the most convenient preset for a first
       relay test.

       A reliable probe under dev_sandbox, with the relay armed in another win-
       dow:

              echo '{"actions":[{"cmd":"curl"}]}' | ssh ai-agent@host

       curl with no arguments prints its own usage text, so approving it is
       harmless.  The request should appear on the operator's terminal with the
       command, goal, risk score, blast radius, and policy reason, followed by
       the Approve? [yes/NO] prompt.

       To  determine  the  confirmation level of any command without executing
       anything, use --dry-run-json on the executor: it reports  decision  and
       confirm level per action.  See aishell-gate-exec(1).

WHEN THE RELAY IS NOT ARMED
       If   aishell-gate-confirm   is   not  running  when  a  confirmation  fires,
       aishell-gate-exec will find no reader on the request FIFO and exit  im-
       mediately with code 5:

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

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

SINGLE-SESSION INTERACTIVE DEPLOYMENTS
       aishell-gate-confirm   is   not   needed   when   a   human   operator  runs
       aishell-gate-exec --interactive at a single  terminal.  In  interactive
       mode,  confirmations are collected directly from /dev/tty by the execu-
       tor. Neither --confirm-pipe nor --confirm-lock is required.

       If the AI genuinely needs to operate with no human  in  the  loop,  the
       correct  approach  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 confirma-
       tion requirement appearing in a fully automated session is  the  policy
       engine  correctly identifying a command that warrants human review. The
       right response is to adjust the policy or add a human,  not  to  bypass
       the system.

FIFO LIFECYCLE
       aishell-gate-confirm  creates  both FIFOs with mkfifo(1) at mode 0660 in the
       /run/aishell-gate/ directory (setgid, group shared with  ai-agent).  On
       exit  -- whether by Ctrl-C, SIGTERM, or error -- both FIFOs are removed
       if they were created by this invocation. A stale FIFO from  a  previous
       crashed session is replaced atomically on startup.

       The  FIFOs  are opened O_RDWR rather than O_RDONLY to avoid blocking on
       open: a FIFO opened O_RDONLY blocks until a writer appears. O_RDWR  re-
       turns  immediately.  This  is  documented  POSIX behaviour for FIFOs on
       Linux.

DIAGNOSTICS
       The relay disarms the instant a request arrives, printing nothing
              Fixed in 0.15.0.  Versions up to and including 0.14.0 terminated
              silently  on  every  action-level and typed-level confirmation.
              display_request() captured the parsed field list  with  command
              substitution,  which  strips  all trailing newlines; for a re-
              quest with no plan_actions array the final field is empty,  its
              line  was  removed on capture, and the last read reached end-of-
              input.  Under set -e that non-zero return  terminated  the  script
              through  the  EXIT trap, removing the FIFOs.  Nothing had failed,
              so nothing was printed.  Only plan-level requests, which populate
              that field, worked.  Upgrade to 0.15.0.

       Cannot create request FIFO / permission denied
              The  operator's session predates its aishell-gate group member-
              ship.  See ONE-TIME SETUP above.

       Relay armed but nothing arrives when the AI submits a plan
              The action was denied rather than allowed, or it sits at
              confirm: none.  See TESTING THE RELAY above.

       Requests parse but the operator sees a half-empty block
              From 0.15.0 this  is  reported  explicitly  rather  than  dis-
              played:  a  truncated field stream (python3 killed, or a format
              mismatch between exec and the relay) refuses that single request
              and leaves the relay armed.

       Any  shell script failing silently under set -e can be traced with
       `bash -x /usr/local/bin/aishell-gate-confirm 2>&1 | tee /tmp/trace.log`.
       The last command shown before the cleanup trap fires is the one that
       returned non-zero.

EXIT CODES
       0   Operator pressed Ctrl-C or SIGTERM; FIFOs cleaned up normally.
       1   Not running in a terminal, or could not determine PTY device.
       2   Could not create FIFOs or open them for read/write.
       3   Usage error (unknown flag or missing argument).
       4   python3 not available (required for JSON parsing of requests).

       Note: an unanswered confirmation request that hits --timeout does not
       cause aishell-gate-confirm to exit.  An empty response is forwarded for
       that single request and the relay continues waiting.

REQUIREMENTS
       bash 4+
              The script uses bash-specific features  including  read  -t  for
              timeout support. A POSIX /bin/sh is not sufficient.

       python3
              Used  for JSON parsing of the confirmation request. The standard
              library module json is used; no  third-party  packages  are  re-
              quired.

       mkfifo Used to create the named FIFOs. Available on all Linux systems.

       tty(1) Used at startup to identify the operator's terminal device.

       Tested  on  Linux.  Not tested on macOS or BSD (these platforms are not
       supported in the v1.0 release).

SECURITY NOTES
       The operator's Unix credentials -- owning the FIFO -- are  what  autho-
       rise  the confirmation response.  aishell-gate-exec never opens any PTY
       device in the pipe path.

       The typed challenge code is sent only through the FIFO  to  the  opera-
       tor's terminal. It never appears in the AI's SSH channel or on the AI's
       stderr.  The  executor  verifies the operator's typed response indepen-
       dently; aishell-gate-confirm does not pre-verify it before relaying.

       The request JSON parsed by aishell-gate-confirm uses safe  line-by-line  ex-
       traction  via  python3(1) with no eval(1) call. Shell metacharacter in-
       jection through command content is not possible regardless of what  the
       command contains.

USAGE EXAMPLES
       Standard remote deployment (operator armed, waiting for requests):
              ssh operator@remotehost
              $ aishell-gate-confirm

       Custom FIFO base path and extended timeout:
              aishell-gate-confirm --pipe /var/run/mygate/confirm --timeout 300

       Verify the runtime directory is correctly configured:
              ls -ld /run/aishell-gate
              stat /run/aishell-gate/confirm.req   # after arming

SEE ALSO
       aishell-gate-exec(1),   aishell-gate-policy(1),   mkfifo(1),  flock(2),
       ssh(1), sshd(8)

AUTHOR
       Sean T. Gilley

VERSION
       aishell-gate-confirm 0.15.0
      

AIShell-Gate                  August 2026              AISHELL-GATE-CONFIRM(1)
