Skip to content
This repository was archived by the owner on Mar 19, 2026. It is now read-only.
NecturaLabsPublic archive

About

Self-hosted mobile-first dashboard for monitoring and controlling OpenCode & Claude Code agents

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Conduit

Mobile-first dashboard for monitoring and controlling OpenCode and Claude Code agents.

Self-host for free, forever.


Features

  • 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

Quick Start (Self-Hosted)

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 -d

Open https://localhost in your browser (accept the self-signed TLS cert on first visit).

No email provider? Leave RESEND_API_KEY blank. Magic links will be printed to the server logs instead (docker logs conduit-server-1), so you can still sign in locally.

Configuration

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_FINGERPRINT in 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 at GET /.well-known/assetlinks.json — returns 404 if not set. See Android signing below for key generation instructions.

Regenerating Secrets

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 server

Warning: Rotating JWT_SECRET or JWT_REFRESH_SECRET invalidates all active sessions.

Development

Prerequisites

Setup

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 dev

This starts:

  • Landing page at http://localhost:3000
  • Dashboard at http://localhost:5173/app/
  • API server at http://localhost:3443

Project Structure

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

Commands

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

Testing

# 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:coverage

Connecting Agents

OpenCode

Conduit 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.

Claude Code

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.

Tech Stack

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

Security

  • 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: XMLHttpRequest header + Origin allowlist
  • TLS 1.2+ with ECDSA P-256 certs (self-hosted)

Deployment

Self-Hosted

./scripts/setup.sh
./scripts/generate-secrets.sh
# edit docker-compose.yml — set APP_URL, API_URL
docker compose up -d

Dokploy

When deploying the backend via Dokploy (source-deploy mode), you must configure a persistent volume so the SQLite database survives redeployments:

  1. In Dokploy, navigate to your conduit-api service → Advanced tab → Volumes
  2. Add a bind mount: Host path /var/lib/conduit/data → Container path /app/data
  3. Redeploy the service

Without this, all user sessions and refresh tokens are wiped on every deploy.

Manual Deployment

  • Frontend: Static files — deploy to any CDN (Netlify, Vercel, Cloudflare Pages)
  • Backend: Docker image built from apps/server/Dockerfile

Android signing

The Android APK is signed with a PKCS12 keystore (never committed to the repo).

Regenerating the keystore

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:'

After generating a new keystore

  1. Dokploy — update ASSETLINKS_FINGERPRINT in the conduit-api service environment variables to the new SHA-256 fingerprint, then redeploy the server so GET /.well-known/assetlinks.json returns the updated value.

  2. GitHub secrets — update all four secrets in the repository:

    • ANDROID_KEYSTORE_BASE64 — base64 -w0 conduit.keystore
    • ANDROID_KEY_ALIAS — conduit
    • ANDROID_STORE_PASSWORD — keystore password
    • ANDROID_KEY_PASSWORD — key password
  3. Re-run the Build Android APK workflow 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.

License

MIT — NecturaLabs

About

Self-hosted mobile-first dashboard for monitoring and controlling OpenCode & Claude Code agents

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages