v0.1.0 · open-source release soon

termelix

Self-hosted SSH and tmux management, for humans and their agents.

One supervised Elixir app on the BEAM: a browser terminal, your host fleet, and a live tmux monitor. Agents drive the same tmux sessions you can attach to — over a tiny CLI or MCP, with keys that are deliberately narrower than you.

claude-deploy — tmx
$ pane=$(tmx open 3 claude-deploy /srv/app | jq -r .paneId)
$ tmx watch 3 "$pane" './deploy.sh v2.4.1'
==> Building release v2.4.1
==> Rolling restart: web-01 ok web-02 ok web-03 ok
awaiting_input Proceed with db migration? (y/N)
$ tmx keys 3 "$pane" y
finished Deploy complete in 2m 14s
# you can `tmux attach -t claude-deploy` on web-01 the whole time
1: product

Your fleet, in panes.

Hosts with folders, tags, and credentials on the left. Real terminals and a live tmux tree on the right. Everything runs on your server, in one container.

Termelix dashboard: host fleet grouped by Production, Staging, and Infrastructure, with sessions, quick actions, and per-host online status
The fleet: grouped hosts, sessions, credentials, and status at a glance.

Browser SSH terminal

OTP-native :ssh with PTY, resize, and key or password auth. Sessions detach instead of dying: close the tab, come back, replay 512 KB of scrollback.

Host fleet

Folders, tags, reusable credentials, reachability probes, and Wake-on-LAN. Secrets are write-only: the read APIs answer with hasPassword, never the password.

tmux monitor

Sessions → windows → panes across every host, with per-pane CPU and memory and an activity classifier. It recognizes claude, codex, aider, and friends by name — your agent fleet at a glance.

SFTP file manager

Browse, edit, upload, and download over :ssh_sftp on the same connection machinery — with pinned paths and shortcuts per host.

SSH tunnels

Supervised local forwards with auto-reconnect — each tunnel its own OTP process. Jump-host chains and a SOCKS5 dialer are built and tested; wiring them into the connect path is next.

Serious auth

OIDC/SSO, LDAP, TOTP 2FA, RBAC with per-host grants, trusted devices, session revocation — and AES-256-GCM encryption at rest for every secret you save.

2: agents

Agents get a terminal you can watch.

An agent opens a named tmux session on a real host, runs something in it, and is told when that something finishes or starts asking questions. The work happens in a visible tmux session: tmux attach at any moment to see — and take over — exactly what the agent is doing.

door 1

Browser

The SPA for humans: fleet, terminals, monitor, admin.

door 2

tmx CLI

One bash script, one dependency: curl. Every command is a single HTTP call to /agent.

door 3

MCP

POST /mcp for Claude Code, Codex, and any MCP client speaking streamable HTTP.

All three doors end in the same Termelix.Tmux modules, so the verbs cannot drift apart.

Keys narrower than you

  • tmux:read list panes, capture a screen
  • tmux:write open a session, run a command, send keys
  • tmux:wait block until a pane changes state
  • Hosts are named explicitly on the key — an empty list means no hosts
  • A key cannot read credentials, edit hosts, or mint another key
  • 60 acting calls/min per user and host, 8 concurrent waits — every session open, command, and keystroke batch audited per key

The loop that works

  1. open_session — idempotent, stable name, returns the pane
  2. run_command — types it, presses Enter, returns at once
  3. wait_for_pane — sleeps until the pane needs attention
  4. capture_pane — read the screen, with an activity verdict
  5. send_keys — answer the prompt, loop again
idle running working awaiting_inputthe state the product exists for finished gone

Wire up your agent

mint-key.sh
# log in, list host ids, mint a scoped key (shown exactly once)
curl -s -c jar.txt -X POST "$BASE/users/login" \
  -H 'Content-Type: application/json' \
  -d '{"username":"you","password":"…"}'
curl -s -b jar.txt "$BASE/host/db/host" | jq -c '[.[] | {id, name}]'
curl -s -b jar.txt -X POST "$BASE/api-keys" \
  -H 'Content-Type: application/json' \
  -d '{"name":"claude-code",
       "scopes":["tmux:read","tmux:write","tmux:wait"],
       "hostIds":[3],"expiresAt":"2026-12-31T00:00:00Z"}'
The seven MCP tools
ToolScopeWhat it does
list_hostsThe hosts this key may act on, plus its scopes. Start here.
list_panesreadEvery pane on a host, with its state and the evidence for it.
open_sessionwriteCreate or attach a named session; returns the pane to work in.
capture_panereadThe last lines of a pane's screen, plus an activity verdict.
run_commandwriteType a single-line command, press Enter, return at once.
send_keyswriteType without Enter — for answering prompts.
wait_for_panewaitBlock until the pane needs attention or the command finishes.

tools/list is filtered by the key's scopes, so an agent is never offered a tool it will be refused.

3: origin

Termix, ported to the BEAM.

Termelix began as an Elixir/Phoenix port of Termix, the self-hosted SSH platform. Think of it as Termix with less features and deeper tmux and agent support.

12 → 1 micro-services became one supervised Phoenix app
~103K lines of TypeScript, reimagined on OTP
1 : 1 byte-compatible crypto — an existing Termix database opens in place
1155 tests, including property tests over the tmux parsers

The trade was deliberate. RDP, VNC, Docker panels, and the widget dashboard stayed behind; what remained got the depth treatment: terminals, tmux, and the agent interface. The React frontend carried over, because the Phoenix app preserves the HTTP and WebSocket contract it depends on.

The BEAM earns its keep: OTP ships a native SSH client, so connections, pools, and tunnels are supervised processes with restart semantics instead of singletons with retry loops. When a terminal dies, its supervisor knows. When you deploy, sessions drain gracefully.

4: selfhost

One container. One port. Your data stays yours.

Embedded SQLite, auto-migrating at boot. Root secrets generate themselves on first start. The first account becomes the admin, and one toggle switches registration off. There is no cloud half.

docker compose up -d One service, port 8080, a single data volume. Health checks are liveness-only, so a database hiccup never kills your live terminals.
/app/data/.env Encryption key, JWT secret, database key — generated on first boot, chmod 0600. A backup that skips this file is not a backup.
DATABASE_PATH=termix.db Coming from Termix? Point Termelix at your existing database and keys, and it opens in place.
docker-compose.yml
services:
  termelix:
    build:
      context: ..
      dockerfile: docker/Dockerfile
    container_name: termelix-beam
    restart: unless-stopped
    stop_grace_period: 30s   # live terminals drain gracefully
    ports:
      - "8080:8080"
    environment:
      PHX_HOST: termelix.example.com
    volumes:
      - termix-data:/app/data

volumes:
  termix-data:   # pre-rename key kept — existing Termix data stays attached
5: source

Going open source, on GitHub.

Termelix goes fully open source with its public release. The repository is being prepared now; the frontend and mobile companions carry their Apache-2.0 lineage from Termix. The list below is how you hear the moment the release ships.

Release news by email.

A handful of messages: the GitHub release, then major versions. You also join sysinit-news, the sysinit newsletter with occasional product and book updates.

The signup uses double opt-in and covers both lists. Every message includes an unsubscribe link.