agentserver
Run your coding agent on any machine anywhere and access it in the browser.
agentserver is to opencode what code-server is to VS Code — a self-hosted web interface that lets your team use a coding agent from a browser, no local installation required.
Highlights
- Browser-based coding agent — Each sandbox runs opencode serve, accessible via a per-sandbox subdomain
- Local agent tunneling — Connect a locally-running opencode instance to agentserver via a WebSocket reverse tunnel, no public IP needed
- Workspaces & multi-tenancy — Organize work into workspaces with role-based membership (owner / maintainer / developer / guest); each workspace has a shared persistent disk
- Sandboxes — Create multiple sandboxes per workspace; pause, resume, and auto-pause on idle
- Two backends — Run sandbox containers via Docker (single node) or Kubernetes with Agent Sandbox + gVisor isolation
- SSO / OIDC — Built-in GitHub OAuth and generic OIDC support; accounts are linked by email
- Anthropic API proxy — Sandboxes never see the real API key; agentserver injects it server-side via a per-sandbox proxy token
- Rich dev environment — Sandbox image ships with Go, Rust, C/C++, Node.js, Python 3, and common tools out of the box
- Cross-platform binary — Pre-built binaries for Linux, macOS, and Windows (amd64 / arm64)
- Helm one-liner — Deploy to any Kubernetes cluster in minutes
Architecture
Browser ──▶ agentserver (Go) ──▶ sandbox pod / container
│ └─ opencode serve (:4096)
│
├─ PostgreSQL (users, workspaces, sandboxes)
├─ Anthropic API proxy (injects real API key)
│
│ WebSocket tunnel
Local machine ─┼──▶ agentserver agent connect ──────────▶ agentserver
└─ opencode serve (:4096) │
Browser access via
subdomain proxy
| Component |
Description |
| agentserver |
Go HTTP server — auth, workspace & sandbox management, opencode subdomain proxy, WebSocket tunnel, Anthropic API proxy, static frontend |
| sandbox |
Container running opencode serve — one per sandbox, isolated via Docker or K8s Agent Sandbox |
| local agent |
agentserver agent connect — connects a local opencode instance to the server via a WebSocket reverse tunnel |
Quick Start
Prerequisites
- Kubernetes cluster (or Docker for local dev)
- PostgreSQL database
- An Anthropic API key
Helm Install
helm install agentserver oci://ghcr.io/agentserver/charts/agentserver \
--namespace agentserver --create-namespace \
--set database.url="postgres://user:pass@postgres:5432/agentserver?sslmode=disable" \
--set anthropicApiKey="sk-ant-..." \
--set ingress.enabled=true \
--set ingress.host="cli.example.com" \
--set baseDomain="cli.example.com"
Open https://cli.example.com, register an account, create a workspace, and launch a sandbox.
Docker Compose (Local Development)
git clone https://github.com/agentserver/agentserver.git
cd agentserver
# Build the opencode agent image
docker build -f Dockerfile.opencode -t agentserver-agent:latest .
# Set your API key
export ANTHROPIC_API_KEY="sk-ant-..."
# Start everything
docker compose up -d
Open http://localhost:8080 in your browser.
Local Agent Tunneling
You can connect a locally-running opencode instance to agentserver without a public IP or any third-party tunnel tool. The server manages it like any other sandbox — accessible via subdomain proxy in the Web UI.
How it works
- In the Web UI, click the laptop icon next to "Sandboxes" to generate a one-time registration code
- On your local machine, download
agentserver from the latest release and run:
# First time: register with the code
agentserver agent connect \
--server https://cli.example.com \
--code <registration-code> \
--name "My MacBook" \
--opencode-url http://localhost:4096
# Subsequent runs: auto-reconnects using saved credentials (~/.agentserver/agent.json)
agentserver agent connect --opencode-url http://localhost:4096
- A new sandbox labeled local appears in the Web UI. Click "Open" to access your local opencode through the browser.
Features
- Zero configuration networking — WebSocket tunnel punches through NATs and firewalls
- Auto-reconnect — Exponential backoff reconnection on disconnect (1s → 2s → 4s → ... → 60s)
- Binary protocol — Raw binary WebSocket frames with chunked streaming, no base64 overhead
- SSE streaming — Agent execution updates stream in real-time through the tunnel
- Offline detection — Web UI shows
offline status when the agent disconnects; automatically recovers to running on reconnect
Tunnel protocol
The tunnel uses a binary WebSocket protocol. Each message is a binary frame:
[4 bytes: JSON header length] [JSON header] [raw binary payload]
- Server → Agent: request header (method, path, HTTP headers) + request body
- Agent → Server: stream header (status, HTTP headers, done flag) + response body chunk (16KB max)
All responses are chunked, keeping each WebSocket message well under the default 32KB limit.
Concepts
Workspaces
A workspace is a collaborative unit. It has members with roles and owns a shared persistent disk (PVC in K8s, named volume in Docker). All sandboxes in a workspace share this disk at /home/agent/projects.
| Role |
Permissions |
| owner |
Full control — manage members, delete workspace, create/manage sandboxes |
| maintainer |
Add members, create/manage sandboxes |
| developer |
Create and manage sandboxes |
| guest |
View sandboxes (read-only access) |
Sandbox types
agentserver supports multiple sandbox types:
| Type |
Image |
Port |
Subdomain |
Description |
| opencode |
opencode-agent |
4096 |
oc-{id}.{baseDomain} |
opencode coding agent with password auth |
| openclaw |
custom |
18789 |
claw-{id}.{baseDomain} |
OpenClaw gateway with token-based WebSocket auth; optional Telegram bot integration |
| local |
— |
— |
oc-{id}.{baseDomain} |
Local opencode instance connected via WebSocket tunnel |
A sandbox is an isolated container running a coding agent, or a local agent connected via WebSocket tunnel. Each sandbox:
- Has its own opencode instance accessible via
oc-{sandboxID}.{baseDomain}
- Cloud sandboxes can be paused (scales to 0 replicas / stops container) and resumed
- Cloud sandboxes are automatically paused after a configurable idle timeout
- Local sandboxes show
offline when the agent disconnects and recover on reconnect
- Gets a unique proxy token for Anthropic API access
Sandbox statuses
| Status |
Description |
creating |
Container is being provisioned |
running |
Sandbox is active and accessible |
pausing |
Container is being paused |
paused |
Container is stopped, can be resumed |
resuming |
Container is being restarted |
offline |
Local agent disconnected (will recover on reconnect) |
Configuration
Helm Values
| Parameter |
Description |
Default |
image.repository |
Server image |
ghcr.io/agentserver/agentserver |
image.tag |
Server image tag |
latest |
opencode.image |
Opencode agent image for sandbox pods |
ghcr.io/agentserver/opencode-agent:latest |
opencode.runtimeClassName |
RuntimeClass for sandbox pods (e.g. gvisor) |
"" |
openclaw.image |
OpenClaw gateway image |
"" |
openclaw.port |
OpenClaw gateway port |
18789 |
database.url |
PostgreSQL connection string |
(required) |
anthropicApiKey |
Anthropic API key |
(required) |
anthropicBaseUrl |
Custom Anthropic API base URL |
"" |
anthropicAuthToken |
Anthropic auth token (alternative to API key) |
"" |
backend |
Sandbox backend: docker or k8s |
docker |
baseDomain |
Base domain for subdomain routing (e.g. cli.example.com) |
"" |
baseScheme |
URL scheme for generated URLs |
https |
idleTimeout |
Auto-pause idle sandboxes after |
30m |
persistence.sessionStorageSize |
Per-sandbox ephemeral storage |
5Gi |
persistence.userDriveSize |
Per-workspace shared disk size |
10Gi |
persistence.storageClassName |
Storage class for PVCs |
"" (cluster default) |
workspace.resources |
Resource limits/requests for sandbox pods |
1Gi/1cpu limits |
agentSandbox.install |
Install Agent Sandbox controller |
true |
ingress.enabled |
Enable Nginx Ingress |
false |
ingress.host |
Ingress hostname |
agentserver.example.com |
ingress.tls |
Enable TLS (cert-manager) |
false |
gateway.enabled |
Enable Gateway API HTTPRoute |
false |
OIDC Authentication
agentserver supports GitHub OAuth and generic OIDC providers alongside username/password auth. Accounts with the same email are automatically linked.
GitHub OAuth:
helm upgrade agentserver oci://ghcr.io/agentserver/charts/agentserver \
--reuse-values \
--set oidc.redirectBaseUrl="https://cli.example.com" \
--set oidc.github.enabled=true \
--set oidc.github.clientId="your-client-id" \
--set oidc.github.clientSecret="your-client-secret"
Set the callback URL in your GitHub OAuth App to: https://cli.example.com/api/auth/oidc/github/callback
Generic OIDC (Keycloak, Authentik, etc.):
helm upgrade agentserver oci://ghcr.io/agentserver/charts/agentserver \
--reuse-values \
--set oidc.redirectBaseUrl="https://cli.example.com" \
--set oidc.generic.enabled=true \
--set oidc.generic.issuerUrl="https://idp.example.com/realms/main" \
--set oidc.generic.clientId="agentserver" \
--set oidc.generic.clientSecret="your-secret"
Kubernetes Backend
For production multi-tenant deployments, use the Kubernetes backend with gVisor sandbox isolation:
helm upgrade agentserver oci://ghcr.io/agentserver/charts/agentserver \
--reuse-values \
--set backend=k8s \
--set opencode.runtimeClassName=gvisor \
--set sandbox.namespace=agentserver
This uses the Kubernetes Agent Sandbox controller to manage isolated pods per sandbox.
Environment Variables
| Variable |
Description |
DATABASE_URL |
PostgreSQL connection string |
ANTHROPIC_API_KEY |
Anthropic API key |
ANTHROPIC_BASE_URL |
Custom API base URL |
ANTHROPIC_AUTH_TOKEN |
Anthropic auth token (alternative to API key) |
OPENCODE_CONFIG_CONTENT |
JSON opencode config for sandbox pods (includes LLM proxy baseURL) |
BASE_DOMAIN |
Base domain for subdomain routing |
BASE_SCHEME |
URL scheme (http or https) |
IDLE_TIMEOUT |
Auto-pause timeout (e.g. 30m) |
AGENT_IMAGE |
Container image for sandbox agents |
OIDC_REDIRECT_BASE_URL |
External URL for OIDC callbacks |
GITHUB_CLIENT_ID |
GitHub OAuth client ID |
GITHUB_CLIENT_SECRET |
GitHub OAuth client secret |
OIDC_ISSUER_URL |
Generic OIDC issuer URL |
OIDC_CLIENT_ID |
Generic OIDC client ID |
OIDC_CLIENT_SECRET |
Generic OIDC client secret |
API
See docs/api-reference.md for the full API reference.
Contributing
# Backend
go run . serve --db-url "postgres://..." --backend docker
# Frontend (separate terminal)
cd web && pnpm install && pnpm dev
License
MIT