Skip to content

Commit 15c734e

Browse files
harden transport security and finalize v1.2 docs
1 parent b7709f6 commit 15c734e

52 files changed

Lines changed: 1863 additions & 261 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.dockerignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.git
2+
.github
3+
node_modules
4+
npm-debug.log
5+
data
6+
config
7+
.wwebjs_auth
8+
docs
9+
tests
10+
coverage
11+
*.db
12+
*.sqlite
13+
*.sqlite-shm
14+
*.sqlite-wal

.env.docker.example

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# OpenCode server endpoint (usually running on host)
2+
OPENCODE_SERVER_URL=http://host.docker.internal:4096
3+
4+
# Persistent DB path inside container
5+
STORAGE_DB_PATH=/app/data/opencode-remote.db
6+
7+
# Required owner identity
8+
SECURITY_OWNER_NUMBER=+15551234567
9+
10+
# Telegram config
11+
TELEGRAM_ENABLED=true
12+
TELEGRAM_BOT_TOKEN=replace-with-real-token
13+
TELEGRAM_OWNER_USER_ID=123456789
14+
TELEGRAM_POLLING_ENABLED=true
15+
TELEGRAM_WEBHOOK_ENABLED=false
16+
17+
# Optional webhook profile values
18+
# TELEGRAM_WEBHOOK_URL=https://your-domain.example/telegram/webhook
19+
# TELEGRAM_WEBHOOK_SECRET=replace-with-random-secret
20+
21+
# Keep default lightweight image path (disable WhatsApp runtime in container)
22+
WHATSAPP_ENABLED=false

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
node_modules/
22
.wwebjs_auth/
33
data/
4+
config/
5+
.env
6+
.env.*
7+
!.env.docker.example
48
*.log
59
*.db
610
*.sqlite
711
*.sqlite3
812
*.db-shm
913
*.db-wal
14+
*.db-journal
15+
npm-debug.log*
16+
yarn-error.log*
17+
pnpm-debug.log*

CHANGELOG.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,21 @@ All notable changes to this project are documented in this file.
66

77
### Added
88

9-
- Quality hardening follow-up plan: `docs/plans/2026-03-01-quality-hardening-followup-plan.md`.
9+
- Docker runtime support:
10+
- `Dockerfile`
11+
- `docker-compose.yml`
12+
- `docker-compose.webhook.yml`
13+
- `.dockerignore`
14+
- `.env.docker.example`
15+
- Telegram UX + reliability overhaul plan: `docs/plans/2026-03-02-telegram-ux-reliability-overhaul-plan.md`.
16+
- Formatter regression test for prompt event-envelope filtering: `tests/formatter.test.ts`.
17+
- Storage lease coverage for transport single-instance behavior: `tests/storage.test.ts` lease test.
18+
- File-by-file audit checklist: `docs/plans/2026-03-02-file-audit-checklist.md`.
19+
- Security review register: `docs/SECURITY_REVIEW.md`.
20+
- Security/remediation backlog register: `TOFIX.md`.
21+
- Security redaction utility for audit/dead-letter storage: `src/security/redaction.ts`.
22+
- CLI security posture helper: `security rotate-token-check`.
23+
- Release notes for hardened release: `RELEASE_NOTES_v1.2.0.md`.
1024
- Architecture diagram set under `docs/architecture/`:
1125
- system/context/container/component
1226
- interaction sequences
@@ -101,6 +115,24 @@ All notable changes to this project are documented in this file.
101115

102116
### Changed
103117

118+
- Chat command UX now supports slash commands and natural language without requiring `@oc` prefix (legacy prefix still accepted).
119+
- Help output is redesigned into a concise, task-oriented menu with command descriptions.
120+
- Telegram input normalization now maps plain shorthand (status/help/runs/sessions/diff/abort/pwd) to slash commands.
121+
- Telegram polling conflict handling now applies controlled backoff with degraded-state visibility.
122+
- Runtime `/status` output now includes transport health and polling conflict recovery timing.
123+
- SQLite schema now includes transport lease support (`transport_leases`) to protect polling ownership on shared DB deployments.
124+
- Webhook mode now fails fast unless `telegram.webhookSecret` is configured.
125+
- Bridge/TUI status views now surface Telegram polling degraded state, conflict count, and retry timing.
126+
- App ingress now enforces global and per-sender token-bucket throttling with `ingress.throttled` audit events.
127+
- Startup validation now supports env-only secret mode (`security.requireEnvTokens`) and placeholder-token warnings.
128+
- SQLite audit and dead-letter writes now redact token/secret/bearer-like values before persistence.
129+
- Removed dead code: `src/audit/logger.ts` and unused `OpenCodeAdapter.server` field.
130+
- Package versions bumped for release alignment:
131+
- root `opencode-remote` -> `1.2.0`
132+
- workspaces (`daemon`, `cli`, `tui`, `bridge`) -> `0.2.0`
133+
- `/opencode diagnostics` now includes runtime transport and lease status snapshot.
134+
- Documentation updated for prefix-optional command model and webhook-first production profile.
135+
- TSDoc coverage expanded across runtime, transport, bridge, and CLI/TUI entry modules.
104136
- Root TypeScript strict mode is re-enabled with passing project and workspace checks.
105137
- Lint pipeline now targets TypeScript across root and workspace code:
106138
- `src/**/*.ts`, `tests/**/*.ts`, `apps/**/*.ts`, `packages/**/*.ts`

Dockerfile

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
FROM node:20-bookworm-slim
2+
3+
WORKDIR /app
4+
5+
ENV PUPPETEER_SKIP_DOWNLOAD=true
6+
7+
# better-sqlite3 may need native build tooling during install.
8+
RUN apt-get update \
9+
&& apt-get install -y --no-install-recommends python3 make g++ \
10+
&& rm -rf /var/lib/apt/lists/*
11+
12+
COPY package.json package-lock.json ./
13+
COPY apps ./apps
14+
COPY packages ./packages
15+
COPY scripts ./scripts
16+
COPY src ./src
17+
COPY tsconfig.json ./
18+
19+
RUN npm ci
20+
21+
ENV XDG_CONFIG_HOME=/app/config
22+
23+
RUN mkdir -p /app/data /app/config /app/.wwebjs_auth
24+
25+
CMD ["npm", "start"]

README.md

Lines changed: 117 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ OpenCode Remote is a local-first control layer for OpenCode with dual transport
55
- WhatsApp (`whatsapp-web.js`)
66
- Telegram (Bot API)
77

8-
It routes chat input into a deterministic command model (`@oc`) and persists control-plane state in SQLite.
8+
It routes chat input into a deterministic command model (slash commands + natural language) and persists control-plane state in SQLite.
99

1010
Implementation is TypeScript-first (`src/**/*.ts`, `tests/**/*.ts`) with strict mode enabled and zero explicit `any` usage.
1111

@@ -29,7 +29,7 @@ Request flow:
2929
1. Transport receives message/update
3030
2. App builds a composite dedupe key (`channel:sender:transport_message_id`)
3131
3. Access controller validates allowlist/role
32-
4. Router parses `@oc` prompt or slash command
32+
4. Router parses prompt or slash command
3333
5. Safety engine enforces guardrails
3434
6. Executor calls OpenCode adapter
3535
7. Response returns via originating transport
@@ -44,7 +44,60 @@ Request flow:
4444

4545
## Quick Start
4646

47-
Install from curl (fresh machine):
47+
### Local (recommended)
48+
49+
Start OpenCode server first (required):
50+
51+
```bash
52+
opencode serve --hostname 127.0.0.1 --port 4096
53+
```
54+
55+
In a second terminal, install and run setup:
56+
57+
```bash
58+
npm install
59+
npm run cli -- setup
60+
npm start
61+
```
62+
63+
### Docker (lightweight, Telegram-first)
64+
65+
The Docker image is optimized for lightweight operation and disables WhatsApp by default.
66+
67+
1) Start OpenCode server on the host:
68+
69+
```bash
70+
opencode serve --hostname 127.0.0.1 --port 4096
71+
```
72+
73+
2) Configure docker env:
74+
75+
```bash
76+
cp .env.docker.example .env
77+
```
78+
79+
3) Edit `.env` with your owner number and Telegram bot token.
80+
81+
4) Start:
82+
83+
```bash
84+
docker compose up --build -d
85+
docker compose logs -f remote
86+
```
87+
88+
Webhook-first production profile:
89+
90+
```bash
91+
docker compose -f docker-compose.yml -f docker-compose.webhook.yml up -d --build
92+
```
93+
94+
Token hygiene:
95+
96+
- If a token is exposed, rotate it in `@BotFather` and update `.env`.
97+
- Polling with one bot token supports one active consumer.
98+
- Run posture check: `npm run cli -- security rotate-token-check`
99+
100+
Install from curl (fresh local machine):
48101

49102
```bash
50103
curl -fsSL https://raw.githubusercontent.com/Traves-Theberge/opencode-remote/master/scripts/install.sh | bash
@@ -82,6 +135,28 @@ npm start
82135

83136
Then pair WhatsApp from QR (if enabled), and message your Telegram bot.
84137

138+
## Environment Overrides
139+
140+
Config values can be overridden with environment variables using uppercase key names.
141+
142+
Examples:
143+
144+
```bash
145+
OPENCODE_SERVER_URL=http://127.0.0.1:4096
146+
WHATSAPP_ENABLED=false
147+
TELEGRAM_ENABLED=true
148+
TELEGRAM_BOT_TOKEN=replace-with-real-token
149+
TELEGRAM_OWNER_USER_ID=123456789
150+
SECURITY_OWNER_NUMBER=+15551234567
151+
STORAGE_DB_PATH=./data/opencode-remote.db
152+
```
153+
154+
Notes:
155+
156+
- `TELEGRAM_OWNER_USER_ID` auto-binds owner access on startup.
157+
- Telegram polling supports one active consumer per bot token. Use a single running instance for a token.
158+
- Set `SECURITY_REQUIRE_ENV_TOKENS=true` to force env-only secret loading and reject persisted plaintext token config.
159+
85160
## Monorepo Layout
86161

87162
- `apps/daemon/` - workspace-native daemon entrypoint
@@ -149,62 +224,68 @@ npx conf set telegram.webhookSecret "<random-secret>"
149224

150225
If both are enabled, webhook mode takes precedence and polling is skipped with a warning.
151226

227+
When polling is used, OpenCode Remote reports polling conflict backoff in `/status` to make collisions visible.
228+
152229
## Security Defaults
153230

154231
- Telegram group chats blocked by default (`telegram.allowGroupChats=false`)
155232
- Telegram retry controls are transport-specific:
156233
- `telegram.messageMaxRetries`
157234
- `telegram.messageRetryDelayMs`
235+
- Ingress rate limiting enabled with token-bucket controls:
236+
- `security.ingressPerSenderPerMinute`
237+
- `security.ingressGlobalPerMinute`
238+
- `security.ingressBurst`
158239
- Dangerous commands require explicit confirmation
159240

160241
## Command Model
161242

162-
- `@oc <text>`: pass-through prompt
163-
- `@oc /<command>`: control-plane command
243+
- `<text>`: pass-through prompt
244+
- `/<command>`: control-plane command
164245

165246
Telegram normalization:
166247

167-
- Plain text is normalized to `@oc <text>`
168-
- Supported Telegram slash aliases are normalized to shared `@oc` commands
248+
- Plain text shorthand is normalized to slash commands where available (`status`, `help`, `runs`, `sessions`, ...)
249+
- Other plain text is treated as prompt input
169250

170251
Common commands:
171252

172-
- `@oc /status`
173-
- `@oc /session list`
174-
- `@oc /run <command>`
175-
- `@oc /shell <command>`
176-
- `@oc /runs`
177-
- `@oc /get <runId>`
253+
- `/status`
254+
- `/session list`
255+
- `/run <command>`
256+
- `/shell <command>`
257+
- `/runs`
258+
- `/get <runId>`
178259

179260
Advanced control-plane namespaces:
180261

181-
- `@oc /model status`
182-
- `@oc /model list`
183-
- `@oc /model set <providerId> <modelId>`
184-
- `@oc /tools ids`
185-
- `@oc /tools list [providerId] [modelId]`
186-
- `@oc /mcp status`
187-
- `@oc /mcp add <name> <command>`
188-
- `@oc /mcp connect <server>`
189-
- `@oc /mcp disconnect <server>`
190-
- `@oc /skills list`
191-
- `@oc /opencode status`
192-
- `@oc /opencode providers`
193-
- `@oc /opencode commands`
194-
- `@oc /opencode diagnostics`
262+
- `/model status`
263+
- `/model list`
264+
- `/model set <providerId> <modelId>`
265+
- `/tools ids`
266+
- `/tools list [providerId] [modelId]`
267+
- `/mcp status`
268+
- `/mcp add <name> <command>`
269+
- `/mcp connect <server>`
270+
- `/mcp disconnect <server>`
271+
- `/skills list`
272+
- `/opencode status`
273+
- `/opencode providers`
274+
- `/opencode commands`
275+
- `/opencode diagnostics`
195276

196277
Permission/safety policy matrix is documented in `docs/COMMAND_MODEL.md`.
197278

198279
Admin commands:
199280

200-
- `@oc /users list`
201-
- `@oc /users add <+number>`
202-
- `@oc /users remove <+number>`
203-
- `@oc /users bindtg <telegramUserId> <+number> [username]`
204-
- `@oc /users unbindtg <telegramUserId>`
205-
- `@oc /users tglist`
206-
- `@oc /lock`
207-
- `@oc /unlock`
281+
- `/users list`
282+
- `/users add <+number>`
283+
- `/users remove <+number>`
284+
- `/users bindtg <telegramUserId> <+number> [username]`
285+
- `/users unbindtg <telegramUserId>`
286+
- `/users tglist`
287+
- `/lock`
288+
- `/unlock`
208289

209290
## Data and Reliability
210291

@@ -260,3 +341,4 @@ npm run verify
260341
- `docs/OPERATIONS.md`
261342
- `docs/ONBOARDING.md`
262343
- `CHANGELOG.md`
344+
- `TOFIX.md`

RELEASE_NOTES_v1.1.0.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ v1.1.0 upgrades OpenCode Remote from a prototype-grade runtime to a durable loca
3333
npx conf set security.ownerNumber "+15551234567"
3434
```
3535

36-
5. Confirm health with WhatsApp command `@oc /status`.
36+
5. Confirm health with command `/status`.
3737

3838
## Verification Checklist
3939

4040
- [ ] OpenCode server reachable from app host
4141
- [ ] WhatsApp QR pairing succeeds
42-
- [ ] `@oc /status` returns online state
43-
- [ ] `@oc /session list` returns session metadata
44-
- [ ] `@oc /runs` and `@oc /get <id>` work
42+
- [ ] `/status` returns online state
43+
- [ ] `/session list` returns session metadata
44+
- [ ] `/runs` and `/get <id>` work
4545
- [ ] Permission prompt events appear and accept `/allow` or `/deny`
4646

4747
## Known Limitations

0 commit comments

Comments
 (0)