Remote instances & headless server
Run Qalatra Server on a Linux box and connect desktop clients over HTTPS.
Qalatra can run as a server/client system. The desktop app still feels local, but the data path can go through Qalatra Server: an authenticated HTTP API that owns SQLite, MCP, background workers, agent jobs, attachments, backups, tokens, and encryption key access.
What changes
- Local desktop: Electron starts a local Qalatra Server and connects to it over the same API path remote clients use.
- Remote server: a Linux user service runs Qalatra without the Electron UI.
- Instances: Settings → Instances stores server URLs and bearer tokens so the desktop app can switch between local and remote data sources.
- MCP: the server starts MCP locally, but MCP is not exposed publicly.
Trust model
Use separate channels for operator access and the Qalatra API.
| Channel | Example | Cloudflare Access | Auth owner |
|---|---|---|---|
| Operator access | SSH, noVNC, admin desktop | On | Cloudflare Access + SSH keys |
| Qalatra API | api.example.com | Off | Qalatra bearer tokens |
Linux headless install
Run the installer as the normal Linux user that should own Qalatra. Do not run it with sudo.
export QALATRA_TUNNEL_HOSTNAME=api.example.com curl -fsSL https://raw.githubusercontent.com/pirateandfox/qalatra/develop/scripts/bootstrap-linux-server.sh | bash
The bootstrap script:
- clones or updates Qalatra in
~/qalatra - installs dependencies without Electron postinstall rebuilds
- rebuilds native modules for system Node
- installs and starts
qalatra-server.service - creates or reuses a Cloudflare Tunnel named
qalatra-api - routes the API hostname to
127.0.0.1:3456 - installs and starts
qalatra-cloudflared.service
Reboot survival
loginctl enable-linger "$USER" systemctl --user status qalatra-server.service --no-pager -l systemctl --user status qalatra-cloudflared.service --no-pager -l
Smoke tests
On the Linux server:
curl -fsS http://127.0.0.1:3456/health TOKEN=$(cat ~/.local/share/qalatra/db/admin-token.txt) curl -fsS \ -H "Authorization: Bearer $TOKEN" \ http://127.0.0.1:3456/api/instance
From another machine, test the public API hostname:
curl -fsS \ -H "Authorization: Bearer $TOKEN" \ https://api.example.com/api/instance
Connect desktop to a remote server
Settings -> Instances -> Add Server URL: https://api.example.com Token: contents of ~/.local/share/qalatra/db/admin-token.txt from the Linux server
Prefer creating a dedicated expiring token for each client, then revoking the bootstrap token when it is no longer needed.
Embed private box web tools
Qalatra 1.9.8+ can add one sidebar item per remote instance for a private web app running on that box. This is useful for a tools root page, dashboard, email console, or any small web surface your agents maintain beside their work.
- The server proxies only
http://127.0.0.1:8080on the active remote box. - The desktop app creates a short-lived Box Web session through the existing authenticated API connection and embeds the proxied
/api/box-web/proxy/<ticket>/...path for that session. - Root-relative HTML links/assets and CSS
url(/...)references are rewritten for the proxy. - Qalatra 1.9.9+ also rewrites common runtime API calls from inside the iframe, including
fetch('/api/...'),XMLHttpRequest,EventSource, andnavigator.sendBeacon. - Qalatra 1.9.20+ uses a single reconnect action that creates a fresh session and remaps the current proxied path, avoiding stale-ticket 401s after expired sessions or server restarts.
- Qalatra 1.9.21+ hardens unattended server runtime paths: terminal WebSocket drops, agent completion write failures, and auto-attachment errors are logged without taking down the server.
Settings -> Instances -> Box Web Apps Check "Show for <box>" Label: Tools
Serve the tools page on the remote box:
cd ~/www python3 -m http.server 8080 --bind 127.0.0.1
A public tools hostname remains optional for access when you are away from Qalatra Desktop. It is not required for the in-app sidebar because the proxy uses the existing Qalatra API connection and bearer token.
MCP stays local
Qalatra Server starts MCP locally at http://127.0.0.1:3457/mcp. If Claude Code is running on the Linux machine, configure that local URL. Do not expose port 3457 through Cloudflare Tunnel.
Troubleshooting
API is not healthy
systemctl --user status qalatra-server.service --no-pager -l journalctl --user -u qalatra-server.service -n 160 --no-pager
Server exited while unattended
Qalatra 1.9.21+ writes uncaught server errors to ~/.local/share/qalatra/db/server-crash.log.
tail -n 120 ~/.local/share/qalatra/db/server-crash.log journalctl --user -u qalatra-server.service -n 160 --no-pager
Tunnel routes to the wrong service
cat ~/.config/qalatra/cloudflared/config.yml cloudflared tunnel info qalatra-api dig +short CNAME api.example.com
Native module ABI errors
cd ~/qalatra npm run rebuild:node systemctl --user restart qalatra-server.service