Mobile-first dashboard for monitoring and controlling OpenCode and Claude Code agents.
Self-host for free, forever.
- Real-Time Sessions — Watch AI agents work via SSE streaming
- Remote Config — Edit agent configuration from anywhere
- Multi-Agent — Monitor both OpenCode and Claude Code from one place
- Metrics & Analytics — Track token usage, session duration, tool calls
- Mobile-First — Designed for your phone. Check on agents from anywhere.
- 12 Themes — Midnight, Phosphor, Frost, Aurora, Ember, Ocean, Rose, Cobalt, Sakura, Copper, Slate, Neon
git clone https://github.com/NecturaLabs/Conduit.git
cd Conduit
# 1. Generate TLS certificates and data directory
./scripts/setup.sh
# 2. Generate secure secrets — written to .env
./scripts/generate-secrets.sh
# 3. Edit docker-compose.yml — set APP_URL, API_URL, and optionally RESEND_API_KEY
# 4. Build and start (server + nginx + frontend)
docker compose up -dOpen https://localhost in your browser (accept the self-signed TLS cert on first visit).
No email provider? Leave
RESEND_API_KEYblank. Magic links will be printed to the server logs instead (docker logs conduit-server-1), so you can still sign in locally.
Secrets live in .env (generated by generate-secrets.sh). All other configuration lives in docker-compose.yml. The key fields to set after cloning:
| Variable | Description |
|---|---|
JWT_SECRET |
HMAC secret for access tokens — loaded from .env, set by generate-secrets.sh |
JWT_REFRESH_SECRET |
HMAC secret for refresh tokens — loaded from .env, set by generate-secrets.sh |
CONDUIT_HOOK_TOKEN |
Shared secret for agent webhook hooks — loaded from .env, set by generate-secrets.sh |
APP_URL |
Public URL of your Conduit instance (used in magic link emails) |
API_URL |
Public URL of the API — must match VITE_API_URL in the web build args |
RESEND_API_KEY |
Resend email API key — leave blank to log magic links to stdout |
RESEND_FROM_EMAIL |
Sender address shown in magic link emails |
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET |
GitHub OAuth app credentials (optional) |
GITLAB_CLIENT_ID / GITLAB_CLIENT_SECRET |
GitLab OAuth app credentials (optional) |
GITLAB_BASE_URL |
GitLab instance URL — defaults to https://gitlab.com (optional) |
VITE_API_URL |
Public API URL baked into the frontend at build time — injected by Netlify env vars or CI, never committed |
CONDUIT_API_HOST |
Hostname for Android App Links (e.g. conduit-api.example.com) — set as a GitHub Actions repo variable |
Android App Links: If you build and distribute the Android APK, set
ASSETLINKS_FINGERPRINTin your server's environment variables (Dokploy or docker-compose) to your APK signing certificate's SHA-256 fingerprint (colon-separated hex pairs, e.g.AB:CD:EF:...). The API server serves it atGET /.well-known/assetlinks.json— returns 404 if not set. See Android signing below for key generation instructions.
To rotate secrets at any time, delete .env and re-run the script:
rm .env
./scripts/generate-secrets.sh
# Restart the stack to apply
docker compose up -d --force-recreate serverWarning: Rotating
JWT_SECRETorJWT_REFRESH_SECRETinvalidates all active sessions.
git clone https://github.com/NecturaLabs/Conduit.git
cd Conduit
# Install dependencies
bun install
# Build shared types
bun run --filter @conduit/shared build
# Start development servers
bun run devThis starts:
- Landing page at
http://localhost:3000 - Dashboard at
http://localhost:5173/app/ - API server at
http://localhost:3443
Conduit/
├── apps/
│ ├── web/ # React 19 + Vite — Dashboard SPA
│ ├── server/ # Fastify v5 + SQLite — API backend
│ ├── landing/ # Next.js 15 — Landing page
│ └── mobile/ # Capacitor v8 — Android shell
├── packages/
│ ├── shared/ # Shared TypeScript types
│ └── mcp-server/ # Published MCP server package (@conduit-ai/mcp-server)
├── cli/ # legacy hook installer
├── nginx/ # Nginx config (self-hosted)
├── scripts/ # Setup & build scripts
│ ├── setup.sh # Generate TLS certs and data directory
│ └── generate-secrets.sh # Generate secrets and write them to .env
└── docker-compose.yml # Self-hosted stack config
| Command | Description |
|---|---|
bun run dev |
Start all apps in development mode |
bun run build |
Build all packages |
bun run test |
Run all tests |
bun run typecheck |
Type-check all packages |
bun run lint |
Lint all packages |
# Run all tests
bun run test
# Run tests for a specific package
bun run --filter @conduit/server test
# Run tests in watch mode
bun run --filter @conduit/server test:watch
# Run with coverage
bun run --filter @conduit/server test:coverageConduit tracks OpenCode sessions via the MCP server. From the Settings → Setup tab in the dashboard, copy the one-liner install command and run it in your terminal. This adds the Conduit MCP server to your OpenCode config non-destructively.
Same as above — from the Settings → Setup tab, copy the one-liner and run it. This adds the Conduit MCP server to your .claude/settings.json non-destructively.
The MCP server handles all communication with Conduit — no additional configuration required.
| Layer | Technology |
|---|---|
| Frontend | React 19, Vite, Tailwind CSS v4, Zustand, TanStack Query |
| Backend | Fastify v5, SQLite (bun:sqlite), Zod |
| Auth | Magic link email (Resend), JWT (httpOnly cookies), OAuth 2.0 PKCE (GitHub, GitLab) |
| Landing | Next.js 16 (static export) |
| Infra | Docker, Nginx |
| Runtime | Bun |
- Magic link tokens: 256-bit entropy, SHA-256 hashed, 15-min TTL, single-use
- JWT: HMAC-SHA256, httpOnly/Secure cookies with SameSite derived (lax same-site, none cross-origin; Capacitor forced lax), 2-hour access + 30-day refresh with rotation
- Webhook receiver: HMAC-SHA256 signature + timestamp replay protection
- All SQL: prepared statements only
- CSRF:
X-Requested-With: XMLHttpRequestheader + Origin allowlist - TLS 1.2+ with ECDSA P-256 certs (self-hosted)
./scripts/setup.sh
./scripts/generate-secrets.sh
# edit docker-compose.yml — set APP_URL, API_URL
docker compose up -dWhen deploying the backend via Dokploy (source-deploy mode), you must configure a persistent volume so the SQLite database survives redeployments:
- In Dokploy, navigate to your
conduit-apiservice → Advanced tab → Volumes - Add a bind mount: Host path
/var/lib/conduit/data→ Container path/app/data - Redeploy the service
Without this, all user sessions and refresh tokens are wiped on every deploy.
- Frontend: Static files — deploy to any CDN (Netlify, Vercel, Cloudflare Pages)
- Backend: Docker image built from
apps/server/Dockerfile
The Android APK is signed with a PKCS12 keystore (never committed to the repo).
If the keystore is ever lost, generate a new one:
keytool -genkeypair \
-keystore conduit.keystore \
-storetype PKCS12 \
-alias conduit \
-keyalg RSA \
-keysize 2048 \
-validity 10000 \
-storepass <password> \
-keypass <password> \
-dname "CN=Conduit, OU=Mobile, O=YourName, L=Unknown, ST=Unknown, C=US"Then extract the SHA-256 fingerprint:
keytool -list -v \
-keystore conduit.keystore \
-alias conduit \
-storepass <password> \
| grep 'SHA256:'-
Dokploy — update
ASSETLINKS_FINGERPRINTin theconduit-apiservice environment variables to the new SHA-256 fingerprint, then redeploy the server soGET /.well-known/assetlinks.jsonreturns the updated value. -
GitHub secrets — update all four secrets in the repository:
ANDROID_KEYSTORE_BASE64—base64 -w0 conduit.keystoreANDROID_KEY_ALIAS—conduitANDROID_STORE_PASSWORD— keystore passwordANDROID_KEY_PASSWORD— key password
-
Re-run the
Build Android APKworkflow to produce a newly signed APK.
Warning: Changing the signing key means existing installs cannot be updated in-place via APK sideload — users must uninstall and reinstall.
MIT — NecturaLabs