OpenCode Remote is a local-first control layer for OpenCode with dual transport support:
- WhatsApp (
whatsapp-web.js) - Telegram (Bot API)
It routes chat input into a deterministic command model (slash commands + natural language) and persists control-plane state in SQLite.
Implementation is TypeScript-first (src/**/*.ts, tests/**/*.ts) with strict mode enabled and zero explicit any usage.
- Natural language pass-through to OpenCode
- Slash-command control plane for sessions, paths, execution, and permissions
- Owner/allowlist access control
- Confirmation flow for dangerous actions
- Durable run retrieval (
/runs,/get <id>) - Retry + dead-letter capture for inbound transport failures
- OpenCode is the execution and session source of truth
- Transports: WhatsApp and Telegram
- Local state: SQLite (
users,bindings,confirmations,runs,messages,audit,dead_letters)
Request flow:
- Transport receives message/update
- App builds a composite dedupe key (
channel:sender:transport_message_id) - Access controller validates allowlist/role
- Router parses prompt or slash command
- Safety engine enforces guardrails
- Executor calls OpenCode adapter
- Response returns via originating transport
- Node.js
>= 20 - Bun
>= 1.3(for TUI app) - Local OpenCode server (
http://localhost:4096by default) - WhatsApp account for pairing (if WhatsApp transport enabled)
- Telegram bot token (if Telegram transport enabled)
Start OpenCode server first (required):
opencode serve --hostname 127.0.0.1 --port 4096In a second terminal, install and run setup:
npm install
npm run cli -- setup
npm startThe Docker image is optimized for lightweight operation and disables WhatsApp by default.
- Start OpenCode server on the host:
opencode serve --hostname 127.0.0.1 --port 4096- Configure docker env:
cp .env.docker.example .env-
Edit
.envwith your owner number and Telegram bot token. -
Start:
npm run docker:redeploy
docker compose logs -f remoteThis redeploy command forces a no-cache image build and container recreate so Docker always runs the latest source changes.
It also stamps each build with OPENCODE_REMOTE_BUILD_ID (git short SHA + timestamp by default) for runtime fingerprint verification.
Webhook-first production profile:
docker compose -f docker-compose.yml -f docker-compose.webhook.yml up -d --buildToken hygiene:
- If a token is exposed, rotate it in
@BotFatherand update.env. - Polling with one bot token supports one active consumer.
- Run posture check:
npm run cli -- security rotate-token-check - Polling conflict alerts are cooldown-limited to avoid repeated spam during recovery windows.
Install from curl (fresh local machine):
curl -fsSL https://raw.githubusercontent.com/Traves-Theberge/opencode-remote/master/scripts/install.sh | bashThen:
cd ~/opencode-remote
npm run cli -- setup
npm startManual local setup:
Install and set owner:
npm install
npx conf set security.ownerNumber "+15551234567"Optional Telegram basics:
npx conf set telegram.botToken "<your-bot-token>"
npx conf set telegram.ownerUserId "123456789"Start:
npm startThen pair WhatsApp from QR (if enabled), and message your Telegram bot.
Config values can be overridden with environment variables using uppercase key names.
Examples:
OPENCODE_SERVER_URL=http://127.0.0.1:4096
WHATSAPP_ENABLED=false
TELEGRAM_ENABLED=true
TELEGRAM_BOT_TOKEN=replace-with-real-token
TELEGRAM_OWNER_USER_ID=123456789
SECURITY_OWNER_NUMBER=+15551234567
STORAGE_DB_PATH=./data/opencode-remote.db
OPENCODE_REMOTE_BUILD_ID=local-devNotes:
TELEGRAM_OWNER_USER_IDauto-binds owner access on startup.- Telegram polling supports one active consumer per bot token. Use a single running instance for a token.
- Set
SECURITY_REQUIRE_ENV_TOKENS=trueto force env-only secret loading and reject persisted plaintext token config.
apps/daemon/- workspace-native daemon entrypointsrc/- core daemon modules (router/executor/adapter/transports)apps/cli/- onboarding and maintenance CLIapps/tui/- visual operator TUI (OpenTUI)packages/bridge/- shared management bridge for config/db/log operations
Run:
npm run tuiCurrent TUI includes:
- runtime summary (owner, transport mode, db counters)
- flow visualizer for message stages from recent audit events
- transition tracking (
incoming -> executed,incoming -> blocked, etc.) - latest timeline for recent message/command events
- pane-based navigation (Overview, Flow, Tasks, Output)
- keyboard task execution and output/timeline paging
CLI wizard:
npm run cli -- setupValidate without persisting:
npm run cli -- setup --dry-runTUI manager:
npm run tuiThe TUI currently provides an onboarding state view and management dashboard shell, while the CLI handles interactive setup and maintenance commands.
Development (polling):
npx conf set telegram.pollingEnabled true
npx conf set telegram.webhookEnabled falseProduction (webhook):
npx conf set telegram.webhookEnabled true
npx conf set telegram.pollingEnabled false
npx conf set telegram.webhookUrl "https://your-domain.example/telegram/webhook"
npx conf set telegram.webhookSecret "<random-secret>"If both are enabled, webhook mode takes precedence and polling is skipped with a warning.
When polling is used, OpenCode Remote reports polling conflict backoff in /status to make collisions visible.
- Telegram group chats blocked by default (
telegram.allowGroupChats=false) - Telegram retry controls are transport-specific:
telegram.messageMaxRetriestelegram.messageRetryDelayMs
- Ingress rate limiting enabled with token-bucket controls:
security.ingressPerSenderPerMinutesecurity.ingressGlobalPerMinutesecurity.ingressBurst
- Dangerous commands require explicit confirmation
<text>: pass-through prompt/<command>: control-plane command
Telegram normalization:
- Plain text shorthand is normalized to slash commands where available (
status,help,runs,sessions, ...) - Other plain text is treated as prompt input
Common commands:
/status/session list/run <command>/shell <command>/runs/get <runId>
Advanced control-plane namespaces:
/model status/model list/model set <providerId> <modelId>/tools ids/tools list [providerId] [modelId]/mcp status/mcp add <name> <command>/mcp connect <server>/mcp disconnect <server>/skills list/opencode status/opencode providers/opencode commands/opencode diagnostics
Permission/safety policy matrix is documented in docs/COMMAND_MODEL.md.
Admin commands:
/users list/users add <+number>/users remove <+number>/users bindtg <telegramUserId> <+number> [username]/users unbindtg <telegramUserId>/users tglist/lock/unlock
- DB path:
./data/opencode-remote.db(default) - WhatsApp auth path:
./.wwebjs_auth(default) - Audit events:
audittable - Failed inbound payloads:
dead_letterstable - Message idempotency: composite key in
messagestable
Persistence safety:
- local database files are ignored by git (
data/,*.db,*.sqlite*) - repo stays code-only; operators bootstrap runtime state locally
npm startnpm run devnpm run cli -- <command>npm run tuinpm run lintnpm run typechecknpm testnpm run typecheck:workspacesnpm run test:workspacesnpm run verify
Recommended pre-release gate:
npm run verifyverify now includes:
- TypeScript lint on
src/,tests/,apps/, andpackages/ - docs index/link path check (
npm run docs:check) - root typecheck
- workspace typecheck
- full tests
- workspace smoke command
docs/README.mddocs/architecture/(diagram set)docs/wiki/Home.mddocs/ARCHITECTURE.mddocs/COMMAND_MODEL.mddocs/DATA_MODELS.mddocs/DATABASE_SCHEMA.mddocs/ERD.mddocs/OPERATIONS.mddocs/ONBOARDING.mdCHANGELOG.mdTOFIX.md