term-server is a small Rust service that keeps native PTYs alive and makes them available through a focused web interface. A private session broker owns the PTYs, while the HTTPS process can restart and reconnect to them. Terminals automatically follow their live working directories, so the sidebar becomes a workspace tree without any manual project setup. Split panes, reconnect history, a lightweight file editor, and a Linux process inspector are available when you need them; the product still feels like a terminal, not a browser IDE.
Sessions remain attached when a browser reloads, disconnects, or the HTTPS process applies a signed update. They intentionally end when the term-server service is explicitly stopped.
Prebuilt Linux artifacts are available for x86-64 and ARM64. The installer detects the current architecture, downloads the rolling main release, verifies the Ed25519 signature on its checksum list, verifies the selected archive's SHA-256 checksum, and installs the binary and browser client for the current user. openssl is required for signature verification.
curl -fsSL https://raw.githubusercontent.com/Agusx1211/term-server/main/install.sh | sh
~/.local/bin/term-serverThe default locations are ~/.local/bin/term-server and ~/.local/lib/term-server/client. Override them with TERM_SERVER_BIN_DIR and TERM_SERVER_INSTALL_DIR. To inspect the installer before running it:
curl -fsSLO https://raw.githubusercontent.com/Agusx1211/term-server/main/install.sh
less install.sh
sh install.shmain is a moving development channel. Pin a versioned release instead when stable releases become available.
Installed releases check their configured channel for signed updates after login and every six hours. When an update is available, the sidebar and Settings → Updates show an Update action. Updating verifies the signed release manifest, target architecture, archive size and SHA-256 checksum, and the new binary's embedded version and source commit before replacing any files. The HTTPS process then restarts itself and reconnects to the private session broker, so active terminals and their replay buffers remain available.
The broker is a hidden mode of the same executable. It listens on session-broker.sock inside the data directory with user-only permissions and accepts no network connections. An explicit service stop also stops the broker and its terminals; an in-process signed update leaves it running. The broker protocol is versioned so future web processes can reject an incompatible handoff instead of silently corrupting a session.
When an update leaves an older compatible broker running, Settings → Updates shows both builds and offers to restart the broker. The action is immediate when no terminals are open; otherwise term-server warns that the open terminals will be closed and requires confirmation.
Automatic installation is intentionally disabled for source builds, containers, and system packages whose binary and client/ directory are not writable siblings. Those builds still expose their version and commit through term-server --version, the status bar, and the authenticated configuration API.
On first boot, open https://127.0.0.1:8090. term-server prints a random password once and stores only its Argon2 hash. The browser will warn about the locally generated certificate; trust it, provide your own certificate, or terminate TLS at a reverse proxy.
- Terminal-first workspace: native PTYs, xterm.js WebGL rendering, truecolor, selection, clipboard shortcuts, search, links, up to eight panes, and as many as 2,000,000 scrollback lines per pane.
- Phone and tablet support: touch-sized navigation, a workspace drawer, focused pane switching, safe-area-aware layouts, and terminal actions that do not depend on hover or hardware-keyboard shortcuts.
- Directory-aware organization: terminals move between collapsible workspaces as their shell changes directory. Workspace colors, names, filters, and sidebar sizing stay stable across reconnects.
- Resilient sessions: bounded server-side replay, slow-client protection, coherent WebSocket reconnects, browser renderer caching, and a separate pane layout in each browser tab. A closed pane detaches the view without killing its process.
- Files when needed: searchable explorer, local image and PDF previews, direct downloads, and a lazy-loaded CodeMirror editor with syntax highlighting, atomic saves, and stale-file conflict detection.
- Agent-connected artifacts: multiline handoffs stay attached to the terminal and agent that created them, with inline text, image, and PDF previews plus an optional full editor.
- Process visibility and control: a lightweight Linux
/procsampler shows the complete live descendant process tree, foreground job, CPU and memory usage, and lets you send SIGTERM to a selected process. Command lines are secret-aware and redacted; input, output, and exited processes are not retained. - Agent awareness: Codex, Claude, and Pi sessions show working, idle, and closed states. An unseen return to idle gets a distinct bell until you focus that terminal. Completion alerts can appear in-app, as desktop notifications, in both places, or remain off. In-app cards inherit their terminal color and can be placed in any corner with a configurable dismissal time.
- Secure defaults: loopback binding, HTTPS, Argon2 password hashing, signed HTTP-only SameSite cookies, origin enforcement, CSP, HSTS, login throttling, and bounded memory use.
- Deployment choices: one native executable plus static browser assets, with Docker Compose and a systemd user service included.
![]() |
![]() |
| Explorer and conflict-safe editor | Live process tree |
The unframed workspace capture used for the hero is also available at docs/screenshots/workspaces.png.
Click the main + to open a shell in your home directory, or use a workspace row’s + to start in that directory. A terminal’s name follows its foreground process until you pin a custom name. Click a terminal to open it in the active pane; use its row actions to rename, split, or kill it directly, or drag it onto the left, right, top, or bottom of another pane to build a nested layout. Kill actions ask for confirmation by default; turn off Settings → Terminal behavior → Confirm before killing terminals to make them immediate.
On a phone, term-server keeps that pane layout but shows one terminal at a time. Use the arrows in the mobile toolbar to move between visible panes, the workspace drawer to open another session, and the terminal action menu for search, clipboard, process inspection, clone, kill, and close controls. The touch keybar starts with terminal zoom controls; the percentage resets the text to its default size, and the selected size is remembered on that device.
Closing a pane keeps the PTY running. The trash action kills it. A normal shell exit removes the terminal automatically.
Serve term-server from a trusted HTTPS origin, then open it in your phone's browser:
- On iPhone or iPad, use Safari's Share → Add to Home Screen action.
- On Android, use the browser menu's Install app or Add to Home screen action.
The installed app launches as <hostname> Term Server in its own standalone window and respects the device safe areas. It loads the current interface from the daemon instead of keeping an offline app shell, so a server update cannot strand the installed app on an incompatible client. Upgrading from an older release clears that legacy app-shell cache automatically. Terminals, files, and authentication require a connection to the term-server daemon. A browser warning bypass for the generated self-signed certificate may not qualify as a trusted origin; use a trusted certificate or TLS-terminating reverse proxy when installing from another device.
Useful shortcuts:
| Action | Shortcut |
|---|---|
| Copy selection | Ctrl+Shift+C / Cmd+Shift+C |
| Paste | Ctrl+Shift+V / Cmd+Shift+V |
| Search terminal history | Ctrl+F / Cmd+F |
| Save an edited file | Ctrl+S / Cmd+S |
| Open a local file link | Ctrl+click / Cmd+click |
Filesystem access has the same operating-system permissions as the daemon. Anyone who can sign in can also open a shell, so treat access as equivalent to SSH access for that user.
Term-server ships the canonical term-server-artifacts skill with every release. The installer
links it into ${CODEX_HOME:-~/.codex}/skills, and Settings → Artifact skill reports whether
Codex, Claude Code, and Pi use that bundled version, another matching copy, an outdated copy, or a
broken link. Explicit install and repair actions link an agent to the bundled skill; term-server
never replaces a standalone directory. Managed links automatically follow term-server updates.
When an agent uses the skill from a term-server terminal, its helper creates a private file under
/tmp/artifacts/<session>/<artifact-id>/ and prints both the full file:// URI and absolute path.
Term-server discovers the completed file atomically and places it in that session's artifact
sidebar. Workspace rows and terminal headers show how many artifacts belong to each agent, and the
same path remains usable with normal tools such as cat.
The sidebar shows the selected artifact's contents next to the live terminal, including inline
text, image, and PDF previews. Open an artifact when the full conflict-safe editor, syntax
highlighting, line wrapping, or a larger canvas is useful. The editor links back to the originating
agent, and closing its tab leaves the artifact available in the sidebar without reopening it.
Edits remain visible to the agent at the same path on later turns. Delete actions in the sidebar and
full editor permanently remove the artifact and close any open tab after confirmation. Artifacts are
temporary: the operating system may clear /tmp, and they are not added to a project or committed
automatically.
For a source checkout, install the skill by linking it into Codex:
mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills"
ln -s "$PWD/skills/term-server-artifacts" \
"${CODEX_HOME:-$HOME/.codex}/skills/term-server-artifacts"Term-server continues to infer Codex, Claude Code, and Pi state from their process trees, terminal signals, output, and CPU activity. Settings → Live agent activity can additionally install a small provider-native plugin or extension for more immediate lifecycle updates such as thinking, running a command, waiting for approval, and compacting context. These updates appear in the existing terminal subtitle; working, idle, ready, closed, and completion notifications keep using the existing state machine.
The managed packages are additive: they do not edit or replace existing hook files, and uninstalling
them removes only term-server's package and dedicated local marketplace. They do nothing outside a
term-server terminal. Events are reduced to a fixed activity category before they reach the private
session broker; prompts, command text, tool arguments, and tool output are not forwarded. Fallback
inference remains enabled whether a package is installed, unavailable, broken, or removed, and
automatically takes over if a native update goes stale. Codex requires a one-time review of newly
installed hooks through /hooks; start a new agent session after any package change.
Run term-server --help for generated CLI help. CLI flags take precedence over environment variables.
| CLI | Environment | Default | Purpose |
|---|---|---|---|
--host |
TERM_SERVER_HOST |
127.0.0.1 |
Bind address |
--port |
TERM_SERVER_PORT |
8090 |
Listen port |
--no-https |
TERM_SERVER_NO_HTTPS |
off | Disable built-in TLS |
--secure-cookie |
TERM_SERVER_SECURE_COOKIE |
off | Mark cookies secure behind a TLS proxy |
--cert, --cert-key |
TERM_SERVER_CERT, TERM_SERVER_CERT_KEY |
generated | Custom PEM certificate and key |
--tls-hostname |
TERM_SERVER_TLS_HOSTNAMES |
local and bind hosts | Extra names for the generated certificate |
--password-file |
TERM_SERVER_PASSWORD_FILE |
generated password | Read the password from a secret file |
| — | TERM_SERVER_PASSWORD |
— | Password; takes precedence over the file |
--data-dir |
TERM_SERVER_DATA_DIR |
$XDG_DATA_HOME/term-server |
Credentials, TLS files, and settings |
--shell |
TERM_SERVER_SHELL |
$SHELL |
Default shell executable |
--allowed-origin |
TERM_SERVER_ALLOWED_ORIGINS |
same origin | Extra reverse-proxy origins |
--replay-mb |
TERM_SERVER_REPLAY_MB |
16 |
Reconnect buffer per terminal |
--scrollback-lines |
TERM_SERVER_SCROLLBACK_LINES |
200000 |
Browser scrollback per pane |
--max-panes |
TERM_SERVER_MAX_PANES |
4 |
Visible pane limit, 1–8 |
--client-dir |
TERM_SERVER_CLIENT_DIR |
auto-detected | Compiled browser application |
--disable-updates |
TERM_SERVER_DISABLE_UPDATES |
off | Disable signed update checks and installation |
--update-channel |
TERM_SERVER_UPDATE_CHANNEL |
main |
Signed release channel to follow |
| — | TERM_SERVER_RELEASE_BASE_URL |
GitHub releases | Alternate HTTPS release base URL |
--log |
TERM_SERVER_LOG |
term_server=info,tower_http=info |
Rust tracing filter |
For an unattended deployment, provide the password through the environment or a protected file:
TERM_SERVER_PASSWORD='use-a-long-random-secret' term-server
# or
term-server --password-file /run/secrets/term-server-passwordPasswords stored in credentials.json can be changed from Settings → Security. Changing
the password signs out other browser sessions. Passwords supplied through the environment or a
secret file remain externally managed and must be changed at their source before restarting the
server. A successful login creates a revocable 400-day browser session and offers the credential
to supported browser password managers; explicit logout or a password change still invalidates it.
Use --no-https only for local development or behind a trusted TLS-terminating proxy. When proxying, forward WebSocket upgrades and declare the public origin:
TERM_SERVER_ALLOWED_ORIGINS=https://terminal.example.com \
TERM_SERVER_SECURE_COOKIE=true \
term-server --no-https --host 127.0.0.1term-server does not trust forwarded client-IP headers. Keep the loopback listener private and apply public-network controls at the proxy or VPN.
export TERM_SERVER_PASSWORD='use-a-long-random-secret'
export TERM_SERVER_BUILD_COMMIT="$(git rev-parse HEAD)"
docker compose up --buildThe image runs as UID/GID 10001. Bind-mounted projects must be readable by that user, and the data volume must be writable.
Container self-updates are disabled by its split, read-only image layout; rebuild the image to update it.
After running the installer, install the supplied unit and create its protected environment file:
mkdir -p ~/.config/systemd/user ~/.config/term-server
curl -fsSL https://raw.githubusercontent.com/Agusx1211/term-server/main/deploy/term-server.service \
-o ~/.config/systemd/user/term-server.service
printf 'TERM_SERVER_PASSWORD=%s\n' 'use-a-long-random-secret' > ~/.config/term-server/environment
chmod 600 ~/.config/term-server/environment
systemctl --user daemon-reload
systemctl --user enable --now term-serverThe daemon finds Pi on its inherited PATH and in common per-user install locations, including npm, pnpm, Volta, Bun, asdf, mise, and installed NVM Node versions. Restart the service after installing or upgrading Pi.
Pi-generated titles and notification summaries have independent settings. A title is generated from the first task submitted to an idle agent and remains stable for that agent session. Follow-up tasks, approvals, and other later input do not replace it.
Prerequisites are Rust 1.88+, Node.js 22+, npm, and a C toolchain for the PTY dependency.
npm ci
npm run build
./target/release/term-serverFor development, run the Rust API and Vite client together:
npm ci
npm run devVite listens on http://127.0.0.1:5173, proxies the API on port 8090, uses the password development, and disables HTTPS. Do not expose the development server.
Before submitting a change:
npm run checkGitHub Actions formats, lints, type-checks, tests, and builds the project on every pull request, push to main, and v* tag. Native Ubuntu runners embed the Cargo version and exact source commit, then produce self-contained archives for:
term-server-linux-x86_64.tar.gzterm-server-linux-aarch64.tar.gz
Each archive contains the native binary, compiled client/, README, license, and systemd unit. Successful main pushes update the rolling main prerelease, while a tag matching the Cargo version (for example v0.1.0) publishes a versioned release. Releases contain:
- the x86-64 and ARM64 archives
SHA256SUMSand its raw Ed25519 signaturerelease-manifest.jsonwith the version, commit, channel, publication time, target, size, and checksum of each archive- the manifest's raw Ed25519 signature
The signing private key lives only in the RELEASE_SIGNING_KEY GitHub Actions secret. Its public half is committed at release/public-key.txt and embedded in the daemon and installer. Publishing fails closed when the secret is absent or does not match that public key. Build the current machine’s archive locally with npm run package.
Each terminal owns a native PTY, a bounded raw-output ring, and a Tokio broadcast channel. Dedicated blocking-reader threads keep PTY I/O away from the async Axum runtime. WebSocket subscribers receive a coherent replay before live output; lagging clients reconnect instead of allowing unbounded queues.
On Linux, one sampler reads /proc for all terminals every 1.5 seconds. It follows parent PID relationships across the complete process table, tracks the PTY foreground process group and per-process CPU and resident memory, and recognizes supported agent process trees without parsing or delaying terminal bytes. Process termination revalidates the terminal ancestry and process start time before sending SIGTERM. Other operating systems retain normal terminal behavior but do not expose process and agent metadata.
The browser delegates terminal parsing and rendering to xterm.js. Recently viewed renderers remain mounted in a bounded cache so switching panes preserves the screen and scroll position without keeping every historical renderer alive.
Read SECURITY.md before exposing term-server beyond a trusted machine or network. Pi titles and completion summaries are independently disabled by default; enabling either sends a bounded, ANSI-sanitized slice of the relevant prompt or terminal output to the selected Pi model provider.
Please report vulnerabilities privately as described in the security policy. Contributions are welcome—see CONTRIBUTING.md.
MIT © 2026 term-server contributors.



