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:
- 01ticketthe browser requests a 60-second, single-use JWT ticket scoped to terminal
- 02socketSocket.IO /terminal with the ticket + column/row size
- 03sshthe API opens an SSH session to the target using the platform key
- 04shellinput/output is forwarded; resize follows the window
| Mode | When | Shell you get |
|---|---|---|
| SSH to the host | TERMINAL_SSH_HOST is set (default host.docker.internal) | The SSH user's shell on the host — usually what you want. |
| Local | TERMINAL_SSH_HOST is empty | A shell inside the API container itself (bash as the node user) — for debugging the API only. |
Setting up SSH to the host
Enable an SSH server on the host
Linuxsudo apt install openssh-server && sudo systemctl enable --now sshWindows (dev): the optional OpenSSH Server feature. The distribution compose file already maps
host.docker.internalto the host gateway on Linux.Fill in the terminal env in .env.dist
.env.distTERMINAL_SSH_HOST=host.docker.internal TERMINAL_SSH_PORT=22 TERMINAL_SSH_USER=deploy # the host user the terminal will useRun
up -dagain after changing the env. WhenTERMINAL_SSH_USERis empty, the terminal logs in asroot(the default of older installs) — the Terminal card marks it as the default user. A new install throughsudo aoox installor the one-line script (curl … | sudo sh) fills it with the name of the user who ransudo(when it is a plain login name and notroot; otherwiseroot; an explicit--terminal-ssh-userflag orTERMINAL_SSH_USERenv 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.Authorize the platform key
A new install does this itself:
sudo aoox installand the one-line script create the key insecrets/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'sauthorized_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
./secretsthe first time it's needed. Settings → Terminal shows the public key and the command to run once on the host asTERMINAL_SSH_USER:mkdir -p ~/.ssh && chmod 700 ~/.ssh touch ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys echo 'ssh-ed25519 AAAA… aoox' >> ~/.ssh/authorized_keysThe same key is used for remote servers — one authorization per host.
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.
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.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_keysof the user running it, so don't run it withsudo.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 anAuthorizedKeysFileelsewhere, 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 ~/.sshon the host. - If
~/.sshorauthorized_keysis 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.internalor172.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) orAOOX_NO_TERMINAL_AUTHORIZE=1(one-line script).
Alternative credentials
| Env | When to use it | Notes |
|---|---|---|
TERMINAL_SSH_PRIVATE_KEY_FILE | Bring your own key | A path inside the API container (mount it to /run/secrets/…); add TERMINAL_SSH_PASSPHRASE if it's encrypted. |
TERMINAL_SSH_PRIVATE_KEY | An inline key in env | Less practical; avoid it in a committed file. |
TERMINAL_SSH_PASSWORD | An SSH password | Less 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) orCmd+V(macOS);Ctrl+Cis forwarded to the shell, it doesn't copy.
If it fails
| Message | Cause & fix |
|---|---|
| auth failed + an authorization command | The 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.internal | The 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 allowed | WEB_ORIGIN doesn't exactly match the browser's URL (scheme/host/port). Fix it, then up -d again. |
| connect ECONNREFUSED / timeout | sshd 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 terminal | A member role. Owner/admin only. |
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 ↗