opendray
Self-hosted gateway for Claude Code · Codex · Gemini · shell, with one shared local-first memory layer across them.
Run sessions on your own infra. Drive from web, mobile, or chat. Open REST + WebSocket API for integrations.
🌐 English · 简体中文 · فارسی · Español · Português · 日本語 · 한국어 · Français · Deutsch · Русский
Why opendray exists#
Three frictions in day-to-day work with AI coding CLIs that opendray is built to fix.
Sessions die when your laptop sleeps. Running Claude Code or Codex over SSH means the agent dies the moment your machine closes the lid or drops Wi-Fi. Context, in-flight tool calls, the partial diff you were about to review. Gone. opendray runs the agent on a host that doesn't sleep (a Mac mini under your desk, a NAS, a VPS) and lets you reattach from a web admin, a Flutter mobile app, or a chat message. The session keeps executing whether or not anyone's connected.
Hitting a rate limit shouldn't kill what you were doing. If you have multiple Anthropic accounts (work + personal, family plan + Pro), opendray treats them as a pool. It surfaces tier, quota and active-session count per account, balances new sessions across them, and lets you swap a live session to a different account without losing the conversation. The transcript moves with you. Same model for Codex and Gemini accounts.
Memory is a first-class layer, not an afterthought. Most AI CLIs re-index project context from scratch every session, burning tokens on repeated retrieval. opendray ships a local-first vector store (ONNX / Ollama / LM Studio embeddings) with three-domain retrieval across user, project, and session, plus drift detection across layers. Every byte stays on your network.
What is opendray?#
opendray wraps the AI coding CLIs you already use (Claude Code, Codex, Gemini, plus any shell) and turns them into something you can drive from anywhere. Run sessions on your home server / NAS / VPS, get notified on Telegram when one goes idle, reply from your phone to feed the next prompt back in, all over a self-hosted gateway you control end to end.
- 🛰 One backend, three surfaces. Single Go binary serving a React web admin and a Flutter mobile app, with every action also exposed over a REST + WebSocket API for third-party integrations.
- 💬 Six bidirectional channels, no walled gardens. Telegram, Slack, Discord, Feishu (飞书), DingTalk (钉钉), WeCom (企业微信), plus a Bridge adapter for anything custom. Replies on any channel get routed back into the right session.
- 🧠 Local-first memory. ONNX / Ollama / LM Studio embeddings with three-scope retrieval (user · project · session), smart ranking, and cross-layer conflict detection. No vector data leaves your network.
- 🔌 Integration-grade API. Scoped API keys, per-call audit log, reverse-proxy mounts. Treat opendray as the gateway behind your own product or just as a personal command centre.
- 🔑 Multi-Claude-account fleet. Drop multiple
claude loginaccounts into the gateway; the panel auto-discovers them via a filesystem watcher, balances new sessions across enabled accounts, and lets you switch a live session between accounts without losing the conversation (transcript is migrated under the hood). Each account row shows live capacity (subscription tier, rate-limit tier, active sessions, last-used, current Anthropic email) so you can pick the right one at a glance. - 🔒 Self-hosted, license-clear. Apache 2.0, one static binary, cosign-signed releases with SPDX SBOM. No telemetry, no cloud account, no subscription.
Architecture at a glance#
A single Go binary on your host runs the show. Clients drive sessions through HTTP/WebSocket, the session manager spawns each AI CLI in its own PTY, and the memory layer keeps shared state in Postgres with vector embeddings from your own provider.
[object Promise]Everything in the diagram runs on your network. No cloud dependencies, no inference outside your control.
Status#
v2.7.2 (latest). The v2 generation continues to iterate. See
VERSIONING.md for the major-as-generation policy
(major = product generation, not strict SemVer "breaking change") and
CHANGELOG.md for the full release history.
This generation ships:
- One-line installer + uninstaller wizards (Linux + macOS; Windows funnels through WSL2). Walks the operator through Postgres bootstrap, AI-CLI install, admin credentials, listen address, binary install, schema migration, and service registration.
- Self-managing binary.
opendray update / start / stop / restart / status / providers list / providers updateso operators don't touchsystemctl/launchctlfor routine ops. - Goreleaser release pipeline. Cross-compiled binaries (linux/darwin × amd64/arm64), cosign keyless signing (Sigstore), SPDX SBOM, atomically verified self-update.
Install#
One-line installer#
Linux / macOS / WSL2
[object Promise]Windows sets up WSL2 first, then runs the Linux installer inside it. details →
[object Promise]Walks through Postgres setup, AI-CLI install, admin credentials, and service registration, landing a running gateway in ~5 to 10 minutes. See scripts/README.md for what the wizard does, the file layout it creates, options, and troubleshooting.
Want the manual walkthrough? Read docs/getting-started.md, a 15-minute end-to-end guide that mirrors what the wizard does so you can verify each step yourself.
npm / npx (Node ≥ 18)#
Install globally and put opendray on PATH:
Or run on demand without installing:
[object Promise]This installs just the binary, no wizard, no service, no Postgres. The package pulls the matching opendray-{linux,darwin}-{x64,arm64} platform binary via optionalDependencies (the esbuild / Biome pattern, no postinstall, no network call at install time). Good for scripted environments, ephemeral runners, or when you already run your own Postgres and process supervisor.
You still bring a database and start the gateway yourself:
[object Promise]Full walkthrough (pgvector setup, config.toml, running as a systemd / launchd service, and updating) in docs/install-binary.md.
Uninstall (Linux / macOS)#
Default. Stops the gateway and removes the binary, but keeps your config.toml, data directory (bcrypt keyfile, sessions, notes, vault), logs, and the PostgreSQL database so a re-install resumes where you left off:
Full purge. Also drops the PG database + role, deletes config / data / logs, removes the service user. Includes a post-delete verification step that bails loudly if anything survived:
[object Promise]Day-to-day commands#
After install, the opendray binary handles its own lifecycle, with no need to remember systemctl / launchctl incantations:
opendray --help lists the full subcommand set.
Deploy path picker#
Every supported path includes session spawn, AI-CLI access, encrypted backups, and the full integration API. opendray is a host-resident gateway; it spawns AI CLIs via PTYs and shares process state (~/.claude, ssh-agent, project files) with them. That model is incompatible with the container isolation that production Docker would impose, so Docker is not a supported deployment path for v2.x.
| Path | Best for | Jump to |
|---|---|---|
| 📦 Pre-built binary | "Just run it", Linux / macOS, any supervisor | Releases page → see Production deploy |
| 🐧 systemd unit | Bare-metal / VM / LXC Linux box | Production deploy §A |
| 🍎 macOS LaunchDaemon | Mac mini / Mac Studio as home server | Production deploy §C |
| 🛠 Build from source | Dev / contributing / custom builds | Quickstart below |
Quickstart (5-minute dev path)#
For a full walkthrough with prereqs and troubleshooting, see docs/quickstart.md. The condensed dev path:
This runs OpenDray in the foreground; Ctrl-C kills it. For a long-running daemon, see Production deploy below.
Production deploy#
Four supported deploy paths, pick whichever fits your environment. Each one gives you auto-restart on crash, persistent state, and separation of secrets from config.
Option A: systemd (bare-metal / VM / LXC)#
The recommended Linux deploy path. Ships a hardened unit at
deploy/systemd/opendray.service
with sandboxing (ProtectSystem=strict, NoNewPrivileges,
MemoryDenyWriteExecute, capability scrub), migrate-then-serve
boot, and a 20s graceful-stop window.
Get a binary first. Either grab a pre-built archive from the
Releases page
(opendray_*_linux_<arch>.tar.gz, which unpacks to a single opendray
binary), or build from source via the Quickstart
above (go build ./cmd/opendray).
The unit runs opendray migrate as ExecStartPre, so the first boot
applies all migrations before serve ever starts. Restarts are
on-failure with a 5s back-off and a 5-burst limit per minute.
Option B: Direct binary + your own process supervisor#
For LXC without systemd, FreeBSD rc.d, OpenRC, or anything else.
Build once, run with whatever supervisor you already use:
Then point your supervisor (s6, runit, supervisord, runwhen) at:
[object Promise]Pre-flight: run opendray migrate -config /etc/opendray/config.toml
once before the first serve, or as a pre-start hook in your
supervisor of choice.
Option C: macOS launchd (Mac mini / Studio as home server)#
For Apple Silicon Mac mini / Mac Studio running 24/7. Ships a
LaunchDaemon at
deploy/launchd/com.opendray.opendray.plist
that starts at boot before any user login, restarts on crash with
a 5s throttle, and logs to /usr/local/var/log/opendray/.
Restart with sudo launchctl kickstart -k system/com.opendray.opendray;
unload entirely with sudo launchctl bootout system/com.opendray.opendray.
Postgres on macOS: install via Homebrew (brew install postgresql@17 && brew services start postgresql@17) and point [database].url at
postgres://[email protected]:5432/opendray. Add pgvector with
brew install pgvector and CREATE EXTENSION vector inside the
opendray database.
For Proxmox-specific LXC notes (PTY in unprivileged containers,
networking, cgroup tweaks), see deploy/lxc/proxmox-pty-notes.md.
For reverse-proxy / TLS termination (nginx, Caddy, Traefik, Cloudflare
Tunnel), see docs/operator-guide.md §Topology.
Optional: enable encrypted DB backups + data exports#
[object Promise]Restart opendray; the sidebar grows a Backups page (/backups)
for encrypted PostgreSQL dumps + restore, and /export for
zip-bundle data exports + import. See docs/operator-guide.md §Backup for the full lifecycle.
A single Go binary carries the whole web bundle, so no Node runtime
is required at runtime, no separate static-file server, no Caddy/nginx
needed. Cloudflare Tunnel terminates TLS in front of :8770.
Layout#
[object Promise]Web frontend#
app/web/ builds a single SPA into internal/web/dist/, which the Go
binary embeds and serves at /admin/*. The Vite dev server at :5173
proxies /api to :8770 for HMR-driven development.
See app/web/README.md for the frontend stack
(React + Vite + Tailwind v4 + shadcn/ui + TanStack Router/Query +
Zustand + xterm.js) and per-W milestone notes.
Mobile app#
app/mobile/ is a Flutter app for iOS and Android with feature
parity with the web admin. It attaches to a running gateway over HTTPS —
enter the Gateway URL + admin login on first launch and you get the
same Sessions / Channels / Integrations / Memory / Git surfaces. There's
no App Store / Play Store build by design (self-hosted, single-tenant):
you build it yourself and sign it with your own identity.
→ Build & install guide — make the gateway reachable from the phone, then sideload an Android APK or install on iPhone via Xcode. (all 10 languages — switch at the top of the guide.)
Documentation#
docs/getting-started.md: start here if you're new. Zero to first session in 15 minutes, including installing the wrapped CLIs and bootstrapping Postgres.docs/install-binary.md: install from the npm package or a release binary (bring your own Postgres) and run it as a systemd / launchd service.docs/quickstart.md: 5-minute dev environment (assumes you already know the moving parts).docs/mobile-app.md: build & install the Flutter mobile app — sideload an Android APK or install on iPhone via Xcode, then point it at your gateway.docs/operator-guide.md: deploy + ops reference for production-ish setups.docs/integration-guide.md: how to write an external integration in any language.VERSIONING.md: versioning strategy (major-as-generation).CHANGELOG.md: release history.
Tests#
[object Promise]End-to-end smoke flows are tracked in commit messages per milestone. A Playwright harness is a planned follow-up.
Relationship to v1#
v1 (Opendray/opendray) is the legacy codebase, now archived. v2 is
the current and active generation, feature-complete and the only
branch receiving development. Of the 16 v1 builtins, four migrated
into the v2 backend; the rest became client-side features, channel
adaptors, or integration-API consumers.
License#
Apache 2.0. See LICENSE. (v1 was MIT; v2 is licensed
independently.)
