Make the remote shell enjoyable in agentic AI time
See the code
Make the remote shell enjoyable in agentic AI time.
Your real shell, in a browser tab. Suwu gives agents and humans alike a tiling terminal that keeps running when you don't — refresh, disconnect, come back tomorrow, pick up where you left off.
A tile-based window manager, by keyboard or mouse — Split, focus, drag tiles into place, and swap them with fast key bindings or a click and drag — whichever fits the moment. Your sessions live in the layout: refresh the page, drop the connection, even restart the server, and every tile comes back exactly as you left it.
Full-featured terminals, perfect for agents — Real shells with selection,
copy/paste, scrollback, and notifications — comfortable for you, and a solid
target for agent orchestrators like opencode, pi, and herdr that drive
many terminals at once.
Convenient apps for everyday dev work — A file browser with download, a
file viewer with auto-refresh, a TCP/UDP port forwarder, a database browser
for SQLite, MySQL, and PostgreSQL, and a Postman-style REST helper that
reaches localhost and your LAN from the server side — the small tools
you'd otherwise reach for a second terminal or a GUI client, right in the
grid next to your shells.
From terminal to web, with suwu commands — suwu send posts a
notification to your screen from any script — pipe stdin in.
suwu forward opens a port-forwarding tile, suwu open, suwu code, and
suwu diff resolve files and actions on the remote machine. Long builds
finish? You'll know.
Reachable, but only by you — Access works out of the box on localhost, your LAN, or over the internet behind a reverse proxy — with password authentication, per-run tokens, and one-command HTTPS.
Yours to tune — Pick your font family and size, theme the colors, dial in a glassy background alpha, and dock the header to any edge. Choose a full-viewport background — an ambient blob, a looping video clip, or a WebGPU shader scene — and drop your own into the data directory. The interface speaks English and Chinese out of the box.
Install once, stay current — Onboarding sets up your dev environment step
by step, and suwu upgrade keeps the binary fresh. No config files to
babysit.
Build output lands on your laptop — Folder Sync mirrors a folder on the server into a folder on your own computer, one way and re-checked every few seconds, straight from the browser.
Your own apps, in a sandbox — An extension is a small JavaScript program
that returns an HTML page. It runs in a QuickJS isolate, in its own process,
with its own API routes, static assets, and an extension-scoped token.
suwu gq runs the same sandbox from the command line.
curl -fsSL https://raw.githubusercontent.com/liyu1981/suwu/refs/heads/master/install.sh | sh
This downloads the latest release binary to ~/.local/bin/suwu and runs
an interactive suwu onboard wizard. Onboarding requires an attached
terminal; it collects the complete setup plan, shows a review, and only then
writes configuration or installs selected tools.
To change one part of an existing setup without re-running the whole wizard, pass a section flag:
suwu onboard --password # reset the connection password
suwu onboard --server # bind host, ports, session timeout
suwu onboard --tls # TLS certificate
suwu onboard --runtime # data directory and systemd service
suwu onboard --tools # development tools
suwu onboard --shell # shell integration
Section runs prompt only for that part, show the resulting configuration, and leave every other setting as it is on disk. Flags can be combined.
pnpm install && pnpm --dir frontend install
pnpm start # build web assets + server, then run on HTTPS :8181
Or build and run manually:
pnpm build:web # vite build -> pkg/assets/web (embedded via go:embed)
pnpm build # go build -o suwu ./cmd/Suwu
./suwu serve # production: https://127.0.0.1:8181
The web assets are always embedded in the binary — a single suwu binary is
all you need to deploy.
Copy .env.example to .env and adjust. Shell environment variables take
precedence over .env.
| Variable | Default | Description |
|---|---|---|
SUWU_DEV | false | Dev banner and hot-reload mode. |
SERVER_MODE | https | https, http, or https+http. |
HTTPS_PORT | 8181 | HTTPS listener port. |
HTTP_PORT | 8180 | HTTP listener port. |
HOST | 127.0.0.1 | Bind address. 0.0.0.0 exposes on all interfaces. |
EXTRA_HOSTS | — | Comma-separated additional allowed hosts, e.g. example.com,*.lan.example. |
AUTH_PASS | — | SHA-256 hash of the required web password; set by suwu onboard. |
DEMO_PORT | 8000 | Port the Vite dev server proxies /api and /ws to. |
TLS_CERT_FILE | — | TLS certificate for HTTPS modes. suwu gencerts writes both paths into ~/.config/suwu/.env. |
TLS_KEY_FILE | — | TLS private key; must be set together with TLS_CERT_FILE. |
Browser-visible hostnames need no configuration: loopback names plus the
machine's own hostname and interface addresses are always accepted, so the
terminal works via localhost, the LAN IP, or the hostname out of the box.
suwu serve loads, in order of precedence: shell environment → ./.env
(project) → ~/.config/suwu/.env (user-global, created by suwu gencerts).
Browsers only expose clipboard APIs (terminal paste) on secure contexts, so non-localhost HTTP access cannot paste into the terminal. To enable HTTPS:
suwu gencerts # interactive: pick hosts (auto-detected) and output dir
suwu serve # now serves https://, certs picked up automatically
gencerts creates a persistent local CA under ~/.config/suwu/CA/ and signs
a server certificate for the hosts you select (default
~/.config/suwu/tls-cert.pem + tls-key.pem). Client devices only need to
trust the CA once (~/.config/suwu/CA/rootCA.pem). Flags allow non-interactive
use: --hosts a.com,192.168.0.5 --out <dir> --no-env --force.
./serve-dev.sh start # hot-reload dev server (air): web + go rebuild on change
./serve-dev.sh status # resolved URL mirrors what the server actually binds
./serve-dev.sh logs # tail the log
./serve-dev.sh stop
serve-dev.sh wraps pnpm dev (air), which rebuilds the web tree, rebuilds
the server into tmp/, and restarts on any source change. Logs and the PID
file live in var/.
For frontend-only iteration you can also run the Vite dev server directly — it proxies API and WebSocket traffic to the Go server:
pnpm dev:web # Vite on :5173, proxying /api and /ws to DEMO_PORT
pnpm test # go test ./...
pnpm vet # go vet ./...
pnpm typecheck # tsc --noEmit (frontend)
cmd/Suwu/ entry point: flags, .env loading, graceful shutdown, banner
pkg/
assets/ go:embed of the built frontend (pkg/assets/web)
auth/ per-run same-origin token + host/origin validation
envfile/ minimal .env loader (first occurrence of a key wins)
pty/ shell PTY sessions (creack/pty)
session/ keyed PTY sessions with libghostty-vt screen state
server/ HTTP + WebSocket endpoints
assets/web/ built frontend (vite output, embedded into the binary)
frontend/ Vite + React + TypeScript + Tailwind v4
src/routes/ AppShell (header + content), TermPage (pane iframe)
src/wm/ tiling window manager: layout tree, shortcuts, tools
src/components/ FullTerminal, PTY session bridge, dialogs, hooks
src/store/ Jotai atoms (font, appearance, connection), shared
with pane iframes via localStorage storage events
/api/token before each connection attempt; WebSocket
upgrades require the token plus a same-origin Host/Origin check, so
cross-origin WebSockets are rejected. This server provides shell access —
bind to loopback unless you understand the exposure.session key (the
tiling pane id, persisted across reloads). The server keeps the shell and a
libghostty-vt screen model alive for a TTL (10 min) after the last client
detaches; reconnecting replays DumpVTFull before live output. Scrollback
is intentionally not restored — only the visible screen.localStorage. Panes render as same-origin
iframes loading /term?pane=<id>, positioned absolutely from pixel rects
computed by a pure computeTiling() function. Shortcuts work both on the
parent window and inside a focused pane (relayed via postMessage).TypeScript
46.1%
Go
29.6%
HTML
17.5%
JavaScript
3.2%
CSS
1.6%
Make the remote shell enjoyable in agentic AI time
See the code
Make the remote shell enjoyable in agentic AI time.
Your real shell, in a browser tab. Suwu gives agents and humans alike a tiling terminal that keeps running when you don't — refresh, disconnect, come back tomorrow, pick up where you left off.
A tile-based window manager, by keyboard or mouse — Split, focus, drag tiles into place, and swap them with fast key bindings or a click and drag — whichever fits the moment. Your sessions live in the layout: refresh the page, drop the connection, even restart the server, and every tile comes back exactly as you left it.
Full-featured terminals, perfect for agents — Real shells with selection,
copy/paste, scrollback, and notifications — comfortable for you, and a solid
target for agent orchestrators like opencode, pi, and herdr that drive
many terminals at once.
Convenient apps for everyday dev work — A file browser with download, a
file viewer with auto-refresh, a TCP/UDP port forwarder, a database browser
for SQLite, MySQL, and PostgreSQL, and a Postman-style REST helper that
reaches localhost and your LAN from the server side — the small tools
you'd otherwise reach for a second terminal or a GUI client, right in the
grid next to your shells.
From terminal to web, with suwu commands — suwu send posts a
notification to your screen from any script — pipe stdin in.
suwu forward opens a port-forwarding tile, suwu open, suwu code, and
suwu diff resolve files and actions on the remote machine. Long builds
finish? You'll know.
Reachable, but only by you — Access works out of the box on localhost, your LAN, or over the internet behind a reverse proxy — with password authentication, per-run tokens, and one-command HTTPS.
Yours to tune — Pick your font family and size, theme the colors, dial in a glassy background alpha, and dock the header to any edge. Choose a full-viewport background — an ambient blob, a looping video clip, or a WebGPU shader scene — and drop your own into the data directory. The interface speaks English and Chinese out of the box.
Install once, stay current — Onboarding sets up your dev environment step
by step, and suwu upgrade keeps the binary fresh. No config files to
babysit.
Build output lands on your laptop — Folder Sync mirrors a folder on the server into a folder on your own computer, one way and re-checked every few seconds, straight from the browser.
Your own apps, in a sandbox — An extension is a small JavaScript program
that returns an HTML page. It runs in a QuickJS isolate, in its own process,
with its own API routes, static assets, and an extension-scoped token.
suwu gq runs the same sandbox from the command line.
curl -fsSL https://raw.githubusercontent.com/liyu1981/suwu/refs/heads/master/install.sh | sh
This downloads the latest release binary to ~/.local/bin/suwu and runs
an interactive suwu onboard wizard. Onboarding requires an attached
terminal; it collects the complete setup plan, shows a review, and only then
writes configuration or installs selected tools.
To change one part of an existing setup without re-running the whole wizard, pass a section flag:
suwu onboard --password # reset the connection password
suwu onboard --server # bind host, ports, session timeout
suwu onboard --tls # TLS certificate
suwu onboard --runtime # data directory and systemd service
suwu onboard --tools # development tools
suwu onboard --shell # shell integration
Section runs prompt only for that part, show the resulting configuration, and leave every other setting as it is on disk. Flags can be combined.
pnpm install && pnpm --dir frontend install
pnpm start # build web assets + server, then run on HTTPS :8181
Or build and run manually:
pnpm build:web # vite build -> pkg/assets/web (embedded via go:embed)
pnpm build # go build -o suwu ./cmd/Suwu
./suwu serve # production: https://127.0.0.1:8181
The web assets are always embedded in the binary — a single suwu binary is
all you need to deploy.
Copy .env.example to .env and adjust. Shell environment variables take
precedence over .env.
| Variable | Default | Description |
|---|---|---|
SUWU_DEV | false | Dev banner and hot-reload mode. |
SERVER_MODE | https | https, http, or https+http. |
HTTPS_PORT | 8181 | HTTPS listener port. |
HTTP_PORT | 8180 | HTTP listener port. |
HOST | 127.0.0.1 | Bind address. 0.0.0.0 exposes on all interfaces. |
EXTRA_HOSTS | — | Comma-separated additional allowed hosts, e.g. example.com,*.lan.example. |
AUTH_PASS | — | SHA-256 hash of the required web password; set by suwu onboard. |
DEMO_PORT | 8000 | Port the Vite dev server proxies /api and /ws to. |
TLS_CERT_FILE | — | TLS certificate for HTTPS modes. suwu gencerts writes both paths into ~/.config/suwu/.env. |
TLS_KEY_FILE | — | TLS private key; must be set together with TLS_CERT_FILE. |
Browser-visible hostnames need no configuration: loopback names plus the
machine's own hostname and interface addresses are always accepted, so the
terminal works via localhost, the LAN IP, or the hostname out of the box.
suwu serve loads, in order of precedence: shell environment → ./.env
(project) → ~/.config/suwu/.env (user-global, created by suwu gencerts).
Browsers only expose clipboard APIs (terminal paste) on secure contexts, so non-localhost HTTP access cannot paste into the terminal. To enable HTTPS:
suwu gencerts # interactive: pick hosts (auto-detected) and output dir
suwu serve # now serves https://, certs picked up automatically
gencerts creates a persistent local CA under ~/.config/suwu/CA/ and signs
a server certificate for the hosts you select (default
~/.config/suwu/tls-cert.pem + tls-key.pem). Client devices only need to
trust the CA once (~/.config/suwu/CA/rootCA.pem). Flags allow non-interactive
use: --hosts a.com,192.168.0.5 --out <dir> --no-env --force.
./serve-dev.sh start # hot-reload dev server (air): web + go rebuild on change
./serve-dev.sh status # resolved URL mirrors what the server actually binds
./serve-dev.sh logs # tail the log
./serve-dev.sh stop
serve-dev.sh wraps pnpm dev (air), which rebuilds the web tree, rebuilds
the server into tmp/, and restarts on any source change. Logs and the PID
file live in var/.
For frontend-only iteration you can also run the Vite dev server directly — it proxies API and WebSocket traffic to the Go server:
pnpm dev:web # Vite on :5173, proxying /api and /ws to DEMO_PORT
pnpm test # go test ./...
pnpm vet # go vet ./...
pnpm typecheck # tsc --noEmit (frontend)
cmd/Suwu/ entry point: flags, .env loading, graceful shutdown, banner
pkg/
assets/ go:embed of the built frontend (pkg/assets/web)
auth/ per-run same-origin token + host/origin validation
envfile/ minimal .env loader (first occurrence of a key wins)
pty/ shell PTY sessions (creack/pty)
session/ keyed PTY sessions with libghostty-vt screen state
server/ HTTP + WebSocket endpoints
assets/web/ built frontend (vite output, embedded into the binary)
frontend/ Vite + React + TypeScript + Tailwind v4
src/routes/ AppShell (header + content), TermPage (pane iframe)
src/wm/ tiling window manager: layout tree, shortcuts, tools
src/components/ FullTerminal, PTY session bridge, dialogs, hooks
src/store/ Jotai atoms (font, appearance, connection), shared
with pane iframes via localStorage storage events
/api/token before each connection attempt; WebSocket
upgrades require the token plus a same-origin Host/Origin check, so
cross-origin WebSockets are rejected. This server provides shell access —
bind to loopback unless you understand the exposure.session key (the
tiling pane id, persisted across reloads). The server keeps the shell and a
libghostty-vt screen model alive for a TTL (10 min) after the last client
detaches; reconnecting replays DumpVTFull before live output. Scrollback
is intentionally not restored — only the visible screen.localStorage. Panes render as same-origin
iframes loading /term?pane=<id>, positioned absolutely from pixel rects
computed by a pure computeTiling() function. Shortcuts work both on the
parent window and inside a focused pane (relayed via postMessage).TypeScript
46.1%
Go
29.6%
HTML
17.5%
JavaScript
3.2%
CSS
1.6%