$ aoox

DocsInfrastructure

Web terminal

A shell into the aoox host (or a remote server) straight from the browser, for owners and admins.

Who
Owner & admin only
To where
The aoox host (SSH) or a remote server
Client
xterm.js in the browser → Socket.IO → SSH

How it works

A container can't open a shell on its own host, so the API connects to the host over SSH. The browser never holds SSH credentials:

  1. 01ticketthe browser requests a 60-second, single-use JWT ticket scoped to terminal
  2. 02socketSocket.IO /terminal with the ticket + column/row size
  3. 03sshthe API opens an SSH session to the target using the platform key
  4. 04shellinput/output is forwarded; resize follows the window
ModeWhenShell you get
SSH to the hostTERMINAL_SSH_HOST is set (default host.docker.internal)The SSH user's shell on the host — usually what you want.
LocalTERMINAL_SSH_HOST is emptyA shell inside the API container itself (bash as the node user) — for debugging the API only.

Setting up SSH to the host

  1. Enable an SSH server on the host

    Linux
    sudo apt install openssh-server && sudo systemctl enable --now ssh

    Windows (dev): the optional OpenSSH Server feature. The distribution compose file already maps host.docker.internal to the host gateway on Linux.

  2. Fill in the terminal env in .env.dist

    .env.dist
    TERMINAL_SSH_HOST=host.docker.internal
    TERMINAL_SSH_PORT=22
    TERMINAL_SSH_USER=deploy        # the host user the terminal will use

    Run up -d again after changing the env. When TERMINAL_SSH_USER is empty, the terminal logs in as root (the default of older installs) — the Terminal card marks it as the default user. A new install through sudo aoox install or the one-line script (curl … | sudo sh) fills it with the name of the user who ran sudo (when it is a plain login name and not root; otherwise root; an explicit --terminal-ssh-user flag or TERMINAL_SSH_USER env always wins); existing installs don't change (change it in Environment, see SSH user on a cloud VPS). If the connection fails, the terminal shows a link to Infrastructure → Environment to fix the settings.

  3. Authorize the platform key

    A new install does this itself: sudo aoox install and the one-line script create the key in secrets/ and add it to the terminal user's ~/.ssh/authorized_keys. On an existing install, the owner just clicks Authorize automaticallyin Infrastructure → Terminal access (or in the error notice on the Terminal page); aoox adds the public key to that user's authorized_keys, tests the login and reconnects. No password or private key is written. The manual command below stays visible as a fallback.

    The API generates an ed25519 keypair in ./secrets the first time it's needed. Settings → Terminal shows the public key and the command to run once on the host as TERMINAL_SSH_USER:

    mkdir -p ~/.ssh && chmod 700 ~/.ssh
    touch ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys
    echo 'ssh-ed25519 AAAA… aoox' >> ~/.ssh/authorized_keys

    The same key is used for remote servers — one authorization per host.

  4. Open the Terminal menu

    Pick a target (aoox host / a remote server) and start typing. The terminal size follows the browser window; the session closes when the tab is closed or the connection drops.

SSH user on a cloud VPS

The terminal logs in to the host as the user in the User field (TERMINAL_SSH_USER), and the key authorization command has to be run on the host as that same user. On a cloud VPS (Ubuntu, Tencent and the like) root login is usually turned off in sshd (PermitRootLogin no, or only through the provider's key), so the root default is often refused.

  1. Set User to the VPS login user

    Open Infrastructure → Environment (owner), set User to the user you SSH into the VPS with — on Ubuntu usually ubuntu — and save. The panel restarts the API container for a few seconds.

  2. Authorize the key as that user

    On the VPS, log in as the same user and run the authorization command from Infrastructure → Terminal access (the three lines above). It writes to the ~/.ssh/authorized_keys of the user running it, so don't run it with sudo.

  3. Reconnect

    Click Reconnect in the Terminal menu.

Limits of automatic authorization

  • Only for the aoox host with the platform key. Remote servers stay manual (the authorization command on the Remote servers page), and so does using your own key or password.
  • Only the owner sees the button (an admin can still use the manual command).
  • The key goes into the terminal user's ~/.ssh/authorized_keys. If sshd uses an AuthorizedKeysFile elsewhere, the key won't be read: aoox reports that the connection test failed and you need to install the key in that location.
  • SELinux (e.g. RHEL/Rocky) can stop sshd from reading a freshly written file; fix the context with restorecon -R ~/.ssh on the host.
  • If ~/.ssh or authorized_keys is a symlink, aoox refuses to follow it and asks for a manual authorization.
  • If Docker uses a custom bridge/gateway (not a default address such as host.docker.internal or 172.17.0.1), the button does not appear; use the manual command.
  • A new install can skip the automatic step with --no-terminal-authorize (CLI) or AOOX_NO_TERMINAL_AUTHORIZE=1 (one-line script).

Alternative credentials

EnvWhen to use itNotes
TERMINAL_SSH_PRIVATE_KEY_FILEBring your own keyA path inside the API container (mount it to /run/secrets/…); add TERMINAL_SSH_PASSPHRASE if it's encrypted.
TERMINAL_SSH_PRIVATE_KEYAn inline key in envLess practical; avoid it in a committed file.
TERMINAL_SSH_PASSWORDAn SSH passwordLess secure — for quick testing only.

Priority: an explicit key → password → the platform key (default).

Usage tips

  • Every aoox container is visible from the host: docker ps --filter label=com.docker.compose.project=aoox.
  • To get into an application container: docker exec -it aoox-app-<slug> sh. For routine commands, a job is cleaner.
  • Proxy logs when a certificate misbehaves: docker logs -f aoox-proxy.
  • Paste with Ctrl+Shift+V (Linux/Windows) or Cmd+V (macOS); Ctrl+C is forwarded to the shell, it doesn't copy.

If it fails

MessageCause & fix
auth failed + an authorization commandThe key isn't in that user's authorized_keys yet. Run the command shown in the terminal/Settings.
SSH authentication failed for root@host.docker.internalThe terminal tried to log in as root while your VPS login is another user and sshd usually refuses root. The owner can try the Authorize automatically button once User in Environment is right. Check: (1) the User field in Infrastructure → Environment equals the VPS login user (e.g. ubuntu); (2) the platform key is in ~/.ssh/authorized_keys of that user (not root); (3) if you really want root, PermitRootLogin in /etc/ssh/sshd_config allows key login (prohibit-password or yes), then sudo systemctl reload ssh.
Automatic authorization: "…but the connection test failed"The key was written, but the test login is still refused: sshd uses an AuthorizedKeysFile elsewhere, SELinux blocks it, or key login is off (PubkeyAuthentication no). Check /etc/ssh/sshd_config, then restorecon -R ~/.ssh if SELinux is on; the manual command is still available.
origin not allowedWEB_ORIGIN doesn't exactly match the browser's URL (scheme/host/port). Fix it, then up -d again.
connect ECONNREFUSED / timeoutsshd isn't running, the port is wrong, or host.docker.internal doesn't resolve (Linux without extra_hosts — use the docker0 gateway IP, e.g. 172.17.0.1).
Key directory not writable (uid 1000)Fix ownership of the ./secrets folder: sudo chown -R 1000:1000 ./secrets.
403 when opening the terminalA member role. Owner/admin only.
This is equivalent to a shell on the host
Only owner and admin can create a terminal ticket; it's checked again at the gateway. Tickets live for 60 seconds and are single-use; the gateway rejects any Origin other than WEB_ORIGIN. This feature is meant for a trusted dev/self-hosted environment — the SSH host key isn't verified. Opening a session (creating a ticket) is recorded in the audit log; the commands typed are not.

Next steps

Found a mistake? Edit this page on GitHub ↗