Your dev environment, from any device.
A self-hosted browser workspace that keeps your terminals, editors, AI agents, and projects running — so you can pick up from your iPhone, iPad, or desktop without losing a thing.
Workspace gives you a persistent, browser-based dev environment hosted on your own server. Close the tab, switch devices, go mobile — your sessions keep running and reconnect automatically.
- Persistent terminals — tmux-backed, survive browser close and reconnect from any device
- Project groups — organise terminals, editor, git, and tools by project directory
- Embedded VS Code — full editor in the browser with AI chat panel
- Agent Manager — kanban board for AI-powered planning and ticket-based agents
- Works with any terminal tool — Claude Code, Gemini CLI, Aider, or plain shell sessions
- MCP Manager — install and configure MCP servers per project
- Image paste — paste screenshots directly into the terminal
- Private mesh friendly — works with Tailscale, NetBird, ZeroTier, or a reverse proxy
The one-line installer bootstraps everything below on a bare box — you only need
curl and sudo:
- Ubuntu/Debian or macOS (also Amazon Linux/RHEL/Fedora/Arch/Alpine)
- Node.js 22+ and git — installed for you if missing (Node via nvm, no sudo)
- Postgres 14+ — the installer can run it in Docker
- tmux — installed for you if missing
- Claude Code (required for now) —
npm install -g @anthropic-ai/claude-code, thenclaudeto log in. The installer installs it; other agent CLIs work in terminals but the agent board targets Claude Code today.
One line — clones, installs dependencies, sets up Postgres in Docker, writes .env,
and walks you through the prompts (works on Ubuntu and macOS):
curl -fsSL https://raw.githubusercontent.com/backvco/workspace/master/install.sh | bashOr set it up manually
git clone https://github.com/backvco/workspace
cd workspace && npm install
# Install missing system tools (tmux, git). Add --docker-postgres to also install
# Docker (if needed) and run a Postgres container with a persistent named volume:
./bin/install-deps --docker-postgres
cp .env.example .env
# Edit .env — at minimum set WORKSPACE_DATABASE_URL and WORKSPACE_PROJECT_ROOTS.
# (Required values have no defaults; the server refuses to start without them.)
# WORKSPACE_DATABASE_URL=postgres://postgres:workspace@127.0.0.1:5432/workspace
# Terminal 1 — the API on :5301 (creates the DB schema on first start)
node server/index.js
# Terminal 2 — the UI on :5300 (proxies /api + /ws to the API)
npm run dev
# Open http://localhost:5300On macOS the installer uses Homebrew (install it first) and Docker Desktop (launch it before creating the database). On first start the API prints a pass/fail check of its dependencies (tmux, git, agent CLI, Postgres) and stops if a required one is missing.
For production, build the UI (npm run build) and serve it with node build,
and run the API with node server/index.js. Put a reverse proxy in front that
routes /api and /ws to the API (:5301) and everything else to the UI — the
production node build serves the UI only and does not proxy the API itself
(that's what the dev server does). See the Caddy example below.
# Required — no defaults
WORKSPACE_DATABASE_URL=postgresql://user:password@localhost:5432/workspace
WORKSPACE_PROJECT_ROOTS=/home/user/projects # comma-separated roots to work under
# Optional
WORKSPACE_HOST=127.0.0.1
WORKSPACE_PORT=5301
WORKSPACE_TERM_CWD=/home/user # defaults to the first project root
WORKSPACE_DATA_DIR=/home/user/.workspace/data # defaults to <app>/data
WORKSPACE_CLAUDE_BIN=claude # the agent CLI to drive
WORKSPACE_SESSION_KEY= # required only if you enable auth
# Optional embedded VS Code (code-server) — the "VS Code" tab is hidden until set.
# Point it at a running code-server instance you host.
VITE_CODE_SERVER_URL=.env.example documents every variable.
Auth is optional and off by default — if you reach the server over a private
mesh or trusted network, you may not need it. To require a login, set
WORKSPACE_SESSION_KEY in .env, then turn auth on in Settings. The first
visit offers a one-time signup for the admin account; add more users from Settings.
Keep the server bound to localhost (the default) and front it with your own mesh,
proxy, or firewall.
HTTPS is required for anything other than
localhost. Image paste, clipboard sync, and installing the app to your home screen (PWA) only work in a browser secure context — that means HTTPS, orhttp://localhoston the same machine. Plainhttp://<server-ip>will load but those features silently fail. Use a private mesh that provides TLS, or a reverse proxy that terminates HTTPS (both below). Keep auth on if the server is reachable by anyone you don't trust.
The installer walks you through all of this (mesh + TLS); the pieces are also available as standalone commands:
Private mesh — reach the server from your devices with no public ports. The installer can set up Tailscale or NetBird (incl. a self-hosted NetBird management URL + setup key + device name):
./bin/setup-mesh tailscale # or: ./bin/setup-mesh netbirdTailscale HTTPS (simplest, no domain, nothing public) — Tailscale issues the cert through its own infra:
tailscale serve --bg 5300 # → https://<machine>.<tailnet>.ts.netA domain, fully private — DNS-01 (recommended for a mesh). Let's Encrypt validates via a DNS TXT record, so no public ports are needed and the domain's A record can point straight at your mesh IP. Supported providers: Cloudflare, Route53, Google Cloud DNS, DigitalOcean.
./bin/dns-credentials cloudflare your.domain # get a least-privilege API token
./bin/setup-tls your.domain --dns cloudflare # Caddy w/ DNS-01, auto-renewingbin/dns-credentials auto-creates a scoped credential when the aws/gcloud CLI
is logged in, otherwise prints the exact least-privilege steps.
A public domain — HTTP-01 (WARNING: exposes the server). Only if the box has a public IP with ports 80/443 open. This puts the server on the public internet, so keep auth on and firewall :443 to your mesh CIDR:
./bin/setup-tls your.domain # Caddy + Let's Encrypt over HTTP-01Or a public domain with automatic HTTPS (Caddy + Let's Encrypt)
If you have a real domain pointed at the server with ports 80/443 open, one command installs Caddy and serves HTTPS with auto-provisioned, auto-renewing Let's Encrypt certificates:
./bin/setup-tls workspace.yourdomain.comThis runs the production build, not the vite dev server: serve the UI with
node build and the API with node server/index.js, and Caddy terminates TLS and
routes /api + /ws → :5301, everything else → the UI (the split vite does in dev):
node server/index.js # API on :5301
npm run build && PORT=3000 node build # UI on :3000To keep both running across reboots, install them as systemd services (Linux):
./bin/setup-service # creates + enables workspace-api and workspace-uiThe installer (install.sh) offers both the Caddy/HTTPS step and the boot
services. Under the hood the Caddy config is a tiny Caddyfile:
workspace.yourdomain.com {
@api path /api/* /ws/*
reverse_proxy @api localhost:5301
reverse_proxy localhost:3000
}
Already running nginx (or another proxy)? Don't use setup-tls — it won't
fight your proxy for ports 80/443. Print a ready-to-paste config instead and use
your existing TLS (e.g. certbot):
./bin/print-proxy nginx workspace.yourdomain.com # then: sudo certbot --nginx -d ...The only requirement is routing /api + /ws (with WebSocket upgrade headers) to
:5301 and everything else to the production UI on :3000.
browser
└─ SvelteKit UI (:5300)
├─ proxies /api + /ws → workspace-api (:5301)
│ ├─ WS /ws/term/<tab> → node-pty → tmux session
│ ├─ /api/tabs → Postgres (tab state)
│ ├─ /api/sessions → list/kill tmux sessions
│ └─ /api/paste → save image + type path into terminal
└─ "VS Code" tab → iframes an external code-server (optional,
VITE_CODE_SERVER_URL)
Browser close kills only the WebSocket client. The tmux session (and anything running in it) keeps going.