Web Terminal for Kiro
A minimal browser terminal for the Kiro CLI: run kiro-cli in a browser tab, on your desktop or your phone.
Web Terminal for Kiro gives each browser tab its own kiro-cli session over a live PTY stream and renders kiro-cli's real terminal UI verbatim, the way an SSH session would, with no chat layer, history store, or translation in between.
It differs from a typical browser terminal in three ways. The screen is real browser text, so scrolling and text selection are native. It is touch-first with multiple tabs, as usable on a phone as on a laptop. Sessions survive sleep and network drops: the screen and scrollback are replayed on reconnect.
Published as a multi-arch (amd64 + arm64) container image on GHCR (ghcr.io/cplieger/web-terminal-kiro) and Docker Hub (cplieger/web-terminal-kiro).
⚠️ It is a remote shell
A browser tab here is an interactive shell with access to your files under /workspace and to kiro-cli's stored credentials. Anyone who can reach the port can use it, and Web Terminal for Kiro has no built-in authentication. Before exposing it beyond your own machine, do one (ideally both) of:
- put it behind an authenticating reverse proxy (Caddy forward-auth, oauth2-proxy, Authentik, …), and/or
- keep the published port on loopback or a private network.
Neither of those covers DNS rebinding: a malicious page in your own browser can point its own hostname at 127.0.0.1 (or your LAN IP) and drive even a loopback-bound terminal, because the request then arrives from your own machine with a matching Origin. Also set ALLOWEDHOSTS to the exact hostnames you reach it by; the Host allowlist is the check that rejects a rebound request.
The server logs a warning at startup when it binds a non-loopback address, and another when ALLOWEDHOSTS is unset.
Stored scrollback
Each tab's newest 200 lines are kept in your browser's localStorage, so returning to a page your phone discarded does not pull every tab's history back over the wire. Terminal output is not always something you want on disk, so know what that keeps:
- It is readable from that browser without reaching this server, and it outlives the tab. An entry is deleted when you close its terminal, and otherwise after seven days.
- Nothing is sent anywhere. The server neither receives nor reads these snapshots.
- Restored output is cleared when it came from a previous run of the container, so a restart never leaves last run's history on screen.
- On a shared or borrowed device, use a private window, which keeps no storage at all.
No setting turns this off. <!-- hub-overview END --
Run
# compose.yaml
services:
web-terminal-kiro:
image: ghcr.io/cplieger/web-terminal-kiro:latest
container_name: web-terminal-kiro
init: true # required: reaps orphaned processes, see below
ports:
- "9848:9848"
volumes:
- ./config:/config # kiro-cli auth, tools, settings
- ./workspace:/workspace # your repos
restart: unless-stoppedOpen <http://localhost:9848. On first launch, kiro-cli signs you in with a device-code flow: it prints a URL and a one-time code, so you open the URL in any browser (your phone works) and enter the code. Every browser tab is a fresh session.
init: true is required. An agent session forks language servers, git processes and node runtimes whose own parent exits, which re-parents them onto PID 1, and the server waits only for the children it started itself. Without an init the server is PID 1 and those orphans accumulate as zombies for the container's life. The server logs a warning at startup when it finds itself running as PID 1, so a deployment that omits this does not fail silently.