Skip to main content

Documentation Index

Fetch the complete documentation index at: https://agents.craft.do/docs/llms.txt

Use this file to discover all available pages before exploring further.

Remote Server

Craft Agents can run as a remote server, letting you keep long-running sessions alive on a remote machine, access them from multiple clients (desktop app, browser, or CLI), and run compute-heavy tasks on a powerful server.

Prerequisites

Choose the setup path that fits your deployment:
  • Run from source — install Bun (v1.0+)
  • Run the published container — install Docker
If you’re running from source, install Bun with:
curl -fsSL https://bun.sh/install | bash

Quick setup from source

Clone and run the install script:
git clone https://github.com/lukilabs/craft-agents-oss.git
cd craft-agents-oss
./scripts/install-server.sh
The script installs dependencies, generates a token, and prints the run command. Save your token — it cannot be recovered.

Docker container

If you just want a deployable server, use the public GitHub Container Registry package: Use the latest container tag:
export CRAFT_SERVER_TOKEN=$(openssl rand -hex 32)
echo $CRAFT_SERVER_TOKEN  # Save this

docker run -d \
  --name craft-agents-server \
  --restart unless-stopped \
  -p 9100:9100 \
  -e CRAFT_SERVER_TOKEN=$CRAFT_SERVER_TOKEN \
  -e CRAFT_RPC_HOST=0.0.0.0 \
  -v craft-agents-data:/home/craftagents/.craft-agent \
  ghcr.io/lukilabs/craft-agents-server:latest
This image already includes the browser-accessible Web UI, so you can open http://your-server:9100 immediately after the container starts. For any networked deployment, terminate TLS at a reverse proxy or mount certificates and set CRAFT_RPC_TLS_CERT / CRAFT_RPC_TLS_KEY. If you prefer Docker Compose:
services:
  craft-agents:
    image: ghcr.io/lukilabs/craft-agents-server:latest
    restart: unless-stopped
    ports:
      - "9100:9100"
    environment:
      CRAFT_SERVER_TOKEN: ${CRAFT_SERVER_TOKEN}
      CRAFT_RPC_HOST: 0.0.0.0
    volumes:
      - craft-agents-data:/home/craftagents/.craft-agent

volumes:
  craft-agents-data:

Docker troubleshooting

Sessions return empty responses (no errors) When running with --user and a custom HOME directory, the Claude Agent SDK needs $HOME/.claude/ to be writable. If the HOME directory is read-only (e.g., owned by root), the SDK silently returns empty responses with no error logged. Fix: Mount a writable volume at the HOME path. For example, if running as a host user whose home is /Users/alice:
docker run -d \
  --name craft-agents-server \
  --user $(id -u):$(id -g) \
  -e HOME=/Users/alice \
  -e CRAFT_SERVER_TOKEN=$CRAFT_SERVER_TOKEN \
  -p 9100:9100 \
  -v /Users/alice/.craft-agent/docker-home:/Users/alice \
  -v /Users/alice/.craft-agent:/Users/alice/.craft-agent \
  ghcr.io/lukilabs/craft-agents-server:latest
The volume stack works in layers:
  1. docker-home/Users/alice — writable HOME for the SDK (~/.claude/)
  2. .craft-agent/Users/alice/.craft-agent — workspace data (overlays on top)
The default Docker image uses HOME=/home/craftagents with a pre-created writable directory. This issue only occurs when overriding HOME to match a host user path.
Web UI not loading If the server starts but http://your-server:9100 returns a 404 or connection error:
  • CRAFT_WEBUI_DIR not set — The Docker image sets this to /app/apps/webui/dist by default. If your docker-compose.yml or .env file overrides environment variables, make sure CRAFT_WEBUI_DIR is included or not overridden to empty.
  • Volume shadows the app directory — Mounting a volume over /app replaces the built WebUI assets. Only mount volumes to /home/craftagents/.craft-agent (or your custom HOME path), not to /app.
  • Older image tag — Pre-0.8.0 images don’t include the WebUI. Use latest or 0.8.0+.
Verify inside the container:
docker exec craft-agents-server ls /app/apps/webui/dist/index.html
docker exec craft-agents-server echo $CRAFT_WEBUI_DIR

Manual setup from source

git clone https://github.com/lukilabs/craft-agents-oss.git
cd craft-agents-oss
bun install
Generate a token and start:
export CRAFT_SERVER_TOKEN=$(openssl rand -hex 32)
echo $CRAFT_SERVER_TOKEN  # Save this

CRAFT_SERVER_TOKEN=$CRAFT_SERVER_TOKEN \
CRAFT_RPC_HOST=0.0.0.0 \
CRAFT_RPC_TLS_CERT=certs/cert.pem \
CRAFT_RPC_TLS_KEY=certs/key.pem \
bun run packages/server/src/index.ts
For development TLS, generate a self-signed certificate:
./scripts/generate-dev-cert.sh
# Creates certs/cert.pem and certs/key.pem (valid 365 days)
For production, use certificates from a trusted CA (e.g., Let’s Encrypt) or place the server behind a reverse proxy (nginx, Caddy) that terminates TLS. The server prints connection details on startup:
CRAFT_SERVER_URL=wss://0.0.0.0:9100
CRAFT_SERVER_TOKEN=<your-token>

Web UI

The server can serve a browser-accessible web UI on the same port.
  • Docker image: already includes the Web UI
  • Run from source: build and enable it with:
# Build the web UI assets
bun run webui:build

# Start the server with Web UI enabled
CRAFT_SERVER_TOKEN=$CRAFT_SERVER_TOKEN \
CRAFT_WEBUI_DIR=apps/webui/dist \
CRAFT_RPC_HOST=0.0.0.0 \
bun run packages/server/src/index.ts
Or use the convenience script that builds everything:
bun run server:prod

Accessing the Web UI

Open https://your-server:9100 (or http:// without TLS) in any browser. You’ll see a login page.

Authentication

Enter the server token as the password. The server issues a session cookie on successful login.
  • Login attempts are rate-limited to 5 per 60 seconds per IP
  • The session persists until you log out or the cookie expires

What you can do

The web UI provides the same session interface as the desktop app — create sessions, send messages, manage workspaces. OAuth flows for Claude and Copilot work directly in the browser.
Never run without TLS on a network. The server token and all session data are transmitted over the WebSocket connection. Without TLS, anyone on the network can intercept them.

Connecting Clients

Desktop App (Hybrid Mode)

Connect to a remote server while keeping local workspaces:
  1. Click the workspace dropdown in the sidebar
  2. Select Add Workspace…Connect to Remote Server
  3. Enter the server URL (e.g., wss://192.168.1.100:9100) and token
  4. Click Test Connection to verify
  5. Select an existing workspace or create a new one on the server
Once connected, remote workspaces appear in your workspace switcher alongside local ones. A CloudOff icon indicates when a remote workspace is unreachable.

Desktop App (Thin Client)

Launch the app as a pure thin client — all logic runs on the server:
CRAFT_SERVER_URL=wss://your-server:9100 \
CRAFT_SERVER_TOKEN=<token> \
bun run electron:start

Web UI

Open the server URL in any browser and log in with the token. See Web UI above.

CLI Client

Use the terminal client for scripting and automation:
export CRAFT_SERVER_URL=wss://your-server:9100
export CRAFT_SERVER_TOKEN=<token>

craft-cli ping
craft-cli sessions
craft-cli send abc-123 "Run the tests"
See the CLI guide for the full command reference.

Environment Variables

VariableRequiredDefaultDescription
CRAFT_SERVER_TOKENYesBearer token for authentication
CRAFT_SERVER_URLNoServer URL for client connections
CRAFT_RPC_HOSTNo127.0.0.1Bind address (0.0.0.0 for remote)
CRAFT_RPC_PORTNo9100Bind port
CRAFT_RPC_TLS_CERTYes*PEM certificate file (enables wss://)
CRAFT_RPC_TLS_KEYYes*PEM private key file
CRAFT_RPC_TLS_CANoPEM CA chain file (optional)
CRAFT_DEBUGNofalseEnable debug logging
* Required for remote connections. Can be omitted for localhost-only development.

Running at Startup

Linux (systemd)

Create the environment file at /path/to/craft-agents-oss/.env:
CRAFT_SERVER_TOKEN=<your-token>
CRAFT_RPC_HOST=0.0.0.0
CRAFT_RPC_PORT=9100
CRAFT_RPC_TLS_CERT=/path/to/cert.pem
CRAFT_RPC_TLS_KEY=/path/to/key.pem
Create a service file at /etc/systemd/system/craft-agents.service:
[Unit]
Description=Craft Agents Server
After=network.target

[Service]
Type=simple
User=<your-user>
WorkingDirectory=/path/to/craft-agents-oss
EnvironmentFile=/path/to/craft-agents-oss/.env
ExecStart=/home/<your-user>/.bun/bin/bun run packages/server/src/index.ts
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable craft-agents
sudo systemctl start craft-agents

# Check status
sudo systemctl status craft-agents

# View logs
journalctl -u craft-agents -f

macOS (launchd)

Create a plist at ~/Library/LaunchAgents/com.craft.agents-server.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.craft.agents-server</string>
  <key>ProgramArguments</key>
  <array>
    <string>/Users/YOU/.bun/bin/bun</string>
    <string>run</string>
    <string>packages/server/src/index.ts</string>
  </array>
  <key>WorkingDirectory</key>
  <string>/path/to/craft-agents-oss</string>
  <key>EnvironmentVariables</key>
  <dict>
    <key>CRAFT_SERVER_TOKEN</key>
    <string>YOUR_TOKEN</string>
    <key>CRAFT_RPC_HOST</key>
    <string>0.0.0.0</string>
    <key>CRAFT_RPC_PORT</key>
    <string>9100</string>
    <key>CRAFT_RPC_TLS_CERT</key>
    <string>/path/to/cert.pem</string>
    <key>CRAFT_RPC_TLS_KEY</key>
    <string>/path/to/key.pem</string>
  </dict>
  <key>RunAtLoad</key>
  <true/>
  <key>KeepAlive</key>
  <true/>
  <key>StandardOutPath</key>
  <string>/tmp/craft-agents.log</string>
  <key>StandardErrorPath</key>
  <string>/tmp/craft-agents.log</string>
</dict>
</plist>
Load and start:
launchctl load ~/Library/LaunchAgents/com.craft.agents-server.plist

# Check status
launchctl list | grep craft

# View logs
tail -f /tmp/craft-agents.log

# Stop and unload
launchctl unload ~/Library/LaunchAgents/com.craft.agents-server.plist

Secure Access

Exposing the server directly to the internet is not recommended. Instead, use one of these approaches: Tailscale creates a private mesh network between your devices. Install it on both the server and client machines — no port forwarding, no certificates, no firewall rules needed.
# On the server: bind to Tailscale IP only
CRAFT_RPC_HOST=100.x.y.z \
CRAFT_SERVER_TOKEN=$TOKEN \
bun run packages/server/src/index.ts
Traffic is encrypted end-to-end by Tailscale, so you can skip TLS certificate setup entirely. The server is only reachable from your Tailscale network.

Reverse proxy (nginx, Caddy)

Place the server behind a reverse proxy that handles TLS termination and access control. This is the standard approach for production deployments. Caddy (automatic HTTPS):
craft.example.com {
    reverse_proxy localhost:9100
}
nginx:
server {
    listen 443 ssl;
    server_name craft.example.com;

    ssl_certificate /etc/letsencrypt/live/craft.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/craft.example.com/privkey.pem;

    location / {
        proxy_pass http://localhost:9100;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}
When using a reverse proxy, bind the server to localhost only (CRAFT_RPC_HOST=127.0.0.1) and let the proxy handle external access.

Cloudflare Tunnel

Cloudflare Tunnel exposes your server over HTTPS without opening ports or managing certificates. Install cloudflared and run:
# Quick tunnel — instant HTTPS URL, no config needed
cloudflared tunnel --url http://localhost:9100
This prints a https://<random>.trycloudflare.com URL you can open in any browser. For a permanent custom domain:
# One-time setup
cloudflared tunnel login
cloudflared tunnel create craft-agents
cloudflared tunnel route dns craft-agents agents.yourdomain.com

# Run the tunnel
cloudflared tunnel run --url http://localhost:9100 craft-agents
Cloudflare Tunnel handles TLS termination automatically — no need to set CRAFT_RPC_TLS_CERT / CRAFT_RPC_TLS_KEY. Keep CRAFT_RPC_HOST=127.0.0.1 so the server only listens locally.

SSH tunnel

For quick, ad-hoc access without any setup:
# On the client: forward local port 9100 to the remote server
ssh -L 9100:localhost:9100 user@your-server
Then connect to ws://localhost:9100 from the desktop app or browser. The tunnel encrypts all traffic over SSH.

Browser tool

The built-in browser bridges from the remote server to the connected desktop client — pages open on your local machine, using your cookies and signed-in sessions. The browser tool is available when at least one desktop client is connected; web UI and CLI clients cannot host browser windows. See Browser on remote workspaces for the security model and the allowRemoteEvaluate switch.

Version Compatibility

The server includes its version in the connection handshake. When a client connects to an older server (pre-0.8.0), it shows a warning that some features may not be available.