hermes-kanban-miniapp/docs/architecture-adr.md

666 lines
27 KiB
Markdown
Raw Normal View History

# ADR-001: Community architecture for Hermes Kanban Telegram Mini App
Status: Proposed for implementation
Date: 2026-07-10
Decision owner: project maintainer
Scope: open-source community release, before rewriting the current local FastAPI prototype
## 1. Context
The current repository contains an uncommitted prototype with a Vite/React frontend and a Python/FastAPI backend that shells out to `hermes kanban`. The target changed: the public release should be best-practice, secure, easy to install, documented in English and Russian, pleasant as a Telegram Mini App and normal browser/mobile app, and work well on Vercel.
Hard constraint: a static Vercel frontend cannot safely execute a user's local `hermes` CLI or read local `~/.hermes/kanban.db`. Any architecture that claims otherwise is untrustworthy. A remote browser UI must talk to a trusted server-side component that is already authorized to reach the user's Hermes instance.
## 2. Verified facts used for this ADR
### Hermes Agent
From the official Hermes docs and local checked source at `/home/kisskin/.hermes/hermes-agent`:
- Hermes API Server exposes Hermes as an OpenAI-compatible HTTP endpoint for external frontends (`POST /v1/chat/completions`, `POST /v1/responses`, `POST /v1/runs`, sessions, jobs, skills/toolsets discovery, health). Source: `/home/kisskin/.hermes/hermes-agent/website/docs/user-guide/features/api-server.md`.
- API Server is enabled with env vars including `API_SERVER_ENABLED=true`, `API_SERVER_KEY`, `API_SERVER_HOST`, `API_SERVER_PORT`, and optional `API_SERVER_CORS_ORIGINS`. Default bind host is `127.0.0.1`; bearer auth is required. Source: same file, Configuration and Authentication sections.
- Hermes API Server is powerful: it gives access to the agent's toolset including terminal commands, so the key must never be exposed to a browser. Source: same file, Security warning.
- Current API Server capabilities do not advertise first-class Kanban CRUD endpoints. The capabilities list includes chat/responses/runs, sessions, jobs, skills, and toolsets; searching `gateway/platforms/api_server.py` found no `/api/kanban` route.
- Hermes Kanban is a durable SQLite-backed board. Tasks, comments, links, workspaces, logs, dispatcher behavior, boards, and board isolation are documented in `/home/kisskin/.hermes/hermes-agent/website/docs/user-guide/features/kanban.md`.
- Hermes Kanban has two official surfaces today: agents use `kanban_*` tools, while humans/scripts use `hermes kanban ...` CLI, slash command, or the Hermes dashboard. Source: Kanban docs section "Two surfaces".
### Telegram Mini Apps
From official Telegram Mini Apps docs and Telegram core docs:
- Mini App init data is intended to be used as an authentication/authorization factor.
- Server validation requires excluding `hash`, sorting key-value pairs, joining them with `\n`, deriving a secret with HMAC-SHA256 over the bot token using `WebAppData`, then HMAC-SHA256 over the data-check string and comparing hex digest to `hash`.
- The bot token must remain server-side. A frontend may forward raw init data to its backend, but must not validate with the bot token in the browser.
- The official React ecosystem template for Telegram Mini Apps uses React, TypeScript, Vite, `@tma.js/sdk-react` / Telegram Apps SDK packages, React Router HashRouter, and Telegram UI components.
### Vercel / browser deployment
- Vercel is a good fit for Vite static frontend hosting and for lightweight Node.js serverless API routes.
- Vercel serverless functions can call HTTPS APIs, but they cannot be trusted to execute a user's private local `hermes` CLI or access a local SQLite board unless the user separately exposes a backend/bridge over HTTPS.
- Browser-side direct calls to Hermes API Server are unsafe for a community default because they would require exposing `API_SERVER_KEY` to JavaScript. If direct browser mode is documented, it must be explicitly marked local/dev only.
### Comparable open-source Telegram Mini Apps
GitHub search verified active public projects/templates:
- `Telegram-Mini-Apps/reactjs-template`: React, TypeScript, Vite, Telegram Mini Apps SDK, Telegram UI; suitable baseline for project structure.
- `telegram-mini-apps-dev/TelegramUI`: React component library inspired by Telegram interface; suitable UI dependency.
- Other public Telegram Mini App examples commonly combine React/TypeScript with Next.js or Vite and place secrets in a backend/server component, not in static browser code.
## 3. Decision
Use this stack decisively:
1. Frontend: React + TypeScript + Vite, hosted as static assets on Vercel or any static host.
2. Telegram SDK/UI: `@telegram-apps/sdk-react` (or current successor package from Telegram Mini Apps docs) + `@telegram-apps/telegram-ui` for native-feeling Telegram UX.
3. API layer for Vercel/community default: TypeScript serverless BFF (Backend-for-Frontend) API routes deployed on Vercel.
4. Local integration: optional self-hosted Hermes Kanban Bridge, implemented in TypeScript/Node.js, running on the same trusted machine as Hermes.
5. Data source: the bridge uses stable `hermes kanban ... --json` CLI commands first, with fixed argv and `shell: false`. It may later switch to official Hermes Kanban HTTP endpoints if Hermes adds them.
6. Authentication: Vercel BFF validates Telegram `initData` server-side and issues a short-lived app session. The BFF then calls the self-hosted bridge using a separate bridge token. The bridge never trusts the browser directly.
7. Documentation: docs must include English and Russian install paths for Vercel + bridge, Docker Compose, and local dev.
Short version: `Vercel static React Mini App + Vercel TS BFF + optional self-hosted TS bridge near Hermes`.
## 4. Why not keep the prototype stack as-is?
The Python/FastAPI prototype proved the shape, but it is not the best public target because:
- The user prefers TypeScript/JS and Vercel.
- A Vercel-centered release is simpler if frontend and BFF share TypeScript types and validation schemas.
- A Python backend on Vercel is possible but less ergonomic for this use case than a small Node BFF plus a separate self-hosted bridge.
- The current prototype mixes concerns: Telegram auth, browser CORS, Hermes CLI subprocess, and static serving in one backend. For community release, the trust boundary must be explicit.
Python/FastAPI is not rejected forever; it becomes a migration source and optional legacy adapter, not the recommended architecture.
## 5. Target architecture
```text
Telegram client / mobile browser / desktop browser
|
| HTTPS, static assets, no secrets
v
Vercel-hosted React + TypeScript + Vite app
|
| Authorization: tma <Telegram initData>
| or app session cookie/JWT after validation
v
Vercel TypeScript BFF API routes
| - validates Telegram initData with TELEGRAM_BOT_TOKEN
| - enforces allowed Telegram user IDs / roles
| - rate limits and normalizes errors
| - holds no Hermes CLI access
|
| HTTPS + Bridge token, server-to-server only
v
Optional self-hosted Hermes Kanban Bridge (Node.js)
| - runs on trusted host near Hermes
| - validates bridge token/mTLS or reverse-proxy auth
| - maps narrow REST endpoints to fixed Hermes Kanban operations
| - uses child_process.spawn/execFile with shell=false
v
Hermes install / profile
|
v
Hermes Kanban board SQLite + dispatcher + logs/workspaces
```
### Local-only shortcut
For local development only:
```text
Vite dev server -> local bridge on 127.0.0.1 -> hermes kanban CLI
```
This mode may use `AUTH_MODE=dev` and a fake Telegram user fixture, but it must be impossible to enable in production without an explicit env var.
## 6. Trust boundaries
### Public browser / Mini App
Trusted for display only. Not trusted for identity, permissions, board slug, task IDs, or action payloads. It must not receive:
- Telegram bot token.
- Hermes API Server key.
- Bridge token.
- Local filesystem paths beyond what the bridge explicitly exposes as display-safe metadata.
- Raw command output containing secrets.
### Vercel BFF
Trusted to authenticate Telegram users and authorize UI actions. It has:
- `TELEGRAM_BOT_TOKEN`.
- `SESSION_SECRET` or JWT signing secret.
- `BRIDGE_BASE_URL` and `BRIDGE_TOKEN` if a bridge is configured.
It does not have direct local CLI access.
### Self-hosted bridge
Trusted local integration process. It has:
- Access to `hermes` binary and `HERMES_HOME`.
- Optional `API_SERVER_KEY` only if a future bridge mode calls Hermes API Server for non-Kanban agent actions.
- A bridge token accepted only from the BFF/reverse proxy.
### Hermes API Server
Powerful agent API. Do not expose directly to the Mini App browser for community default. If users expose it, they must keep bearer auth server-side and set narrow CORS only when absolutely needed.
## 7. Authentication model
### Telegram initData validation
Recommended request from frontend to BFF:
```http
Authorization: tma <raw initData>
```
Server validation steps:
1. Parse query-string-like init data.
2. Remove and store `hash`.
3. Sort remaining key-value pairs alphabetically.
4. Join as `key=value` lines separated by `\n`.
5. Derive secret key: HMAC-SHA256 with key `WebAppData` and message `TELEGRAM_BOT_TOKEN`.
6. Compute HMAC-SHA256 of the data-check string using the derived key.
7. Compare hex digest to received `hash` using constant-time comparison.
8. Reject missing/invalid `auth_date`, expired data, missing user, or malformed JSON.
9. Apply allowlist/RBAC: `TELEGRAM_ALLOWED_USER_IDS`, optional `TELEGRAM_ADMIN_USER_IDS`.
10. Issue a short-lived app session cookie/JWT or validate `initData` on every API request.
Recommended TTL: 1 hour for write actions, configurable up to 24 hours for read-only dashboards.
### App roles
Minimum roles:
- `viewer`: boards/tasks read-only, logs redacted/truncated.
- `operator`: create/comment/assign/promote/block/unblock/archive/dispatch.
- `admin`: configure bridge target, manage board allowlist, view diagnostics.
Initial open-source release may implement allowlist-only authorization with `operator` for all allowed users and document RBAC as planned, but the API contract should reserve `role` fields now.
### Bridge authentication
Recommended:
- `Authorization: Bearer <bridge-token>` between BFF and bridge.
- HTTPS at the public edge if bridge is reachable from Vercel.
- Optional reverse-proxy IP allowlist, Cloudflare Access, Tailscale, or mTLS for advanced users.
- Bridge should reject requests without `X-Request-Id` and should log audit metadata without storing Telegram raw initData.
## 8. Threat model
| Threat | Risk | Mitigation |
|---|---|---|
| Browser steals Hermes API key | Full agent/tool compromise | Never put `API_SERVER_KEY` or bridge token in frontend env. Use BFF. |
| Forged Telegram user | Unauthorized board control | Validate initData server-side using official HMAC flow and max age. |
| Replay of old initData | Unauthorized action reuse | Enforce `auth_date` TTL; short-lived session; optional nonce/idempotency for writes. |
| CLI injection | Arbitrary local commands | Bridge maps endpoints to fixed argv; use `execFile`/`spawn` with `shell:false`; validate task IDs/board slugs. |
| Path traversal via board/workspace | Filesystem escape | Accept only Hermes-valid board slugs; never pass raw paths from browser except documented workspace kind with strict validation. |
| Leaking secrets in logs/errors | Token disclosure | Redact `token`, `secret`, `password`, `api_key`; truncate logs; avoid returning raw stderr. |
| CSRF against BFF | Actions from malicious site | Use SameSite cookies or Authorization header; verify Origin; require Telegram/session auth for writes. |
| CORS overexposure | Browser exfiltration | BFF CORS only for app origin; bridge CORS disabled by default. |
| SSRF from BFF to arbitrary bridge URL | Internal network probing | `BRIDGE_BASE_URL` env only; no user-provided target URLs. |
| Bridge public exposure | Local Hermes takeover | Strong token, HTTPS, rate limiting, reverse-proxy auth, optional local-only mode. |
| Denial of service by refresh/log tail | Resource exhaustion | Pagination, `limit` caps, debounce refresh, server-side rate limits, timeouts. |
| Confused deputy across boards | User controls wrong board | Server-side board allowlist; all write endpoints include normalized board slug and audit actor. |
| Supply-chain compromise | Public package risk | Pin lockfile, Dependabot/Renovate, CI tests, minimal dependencies. |
## 9. Deploy matrix
| Mode | Frontend | BFF | Bridge | Hermes access | Recommended for | Notes |
|---|---|---|---|---|---|---|
| Vercel + self-hosted bridge | Vercel static Vite | Vercel Node serverless routes | Node bridge on user's server/Pi/VPS | Bridge runs local `hermes kanban` | Community default | Best UX for Telegram HTTPS and simple updates. Requires exposing bridge securely. |
| Docker Compose single host | Containerized static frontend served by BFF or Nginx | Node BFF container | Node bridge container or same process | Bind-mount Hermes home / host `hermes` | Self-hosters | Strong docs needed for volumes and user permissions. Avoid mounting Docker socket. |
| Local dev | Vite dev server | Local Node BFF | Local bridge | Local `hermes kanban` | Contributors | Supports Telegram mock/dev auth. No public exposure. |
| Fully static Vercel only | Vercel static | None | None | None | Demo/readme only | Cannot control real Hermes. Can show mock data only. |
| Browser -> Hermes API direct | Static or local | None | None | Hermes API Server over HTTPS | Not recommended | Exposes bearer token to browser unless using a trusted private network. Document as unsafe for public/community default. |
| Future native Hermes Kanban API | Vercel static | Vercel BFF | Optional/none | BFF calls official `/api/kanban` | Future | Adopt only when official Hermes exposes least-privilege Kanban CRUD separate from full agent API. |
## 10. API contract for v1
All public frontend calls go to the BFF under `/api/*`. The BFF calls the bridge under `/bridge/v1/*` or equivalent. The frontend should not know whether data came from a bridge or future native Hermes API.
### Common headers
Frontend -> BFF:
```http
Authorization: tma <raw initData>
Content-Type: application/json
X-Request-Id: <uuid>
```
BFF -> Bridge:
```http
Authorization: Bearer <bridge-token>
Content-Type: application/json
X-Request-Id: <uuid>
X-Actor-Telegram-Id: <id>
X-Actor-Role: viewer|operator|admin
```
### Common response envelope
```ts
type ApiOk<T> = {
ok: true;
data: T;
requestId: string;
};
type ApiError = {
ok: false;
error: {
code: string;
message: string;
details?: unknown;
};
requestId: string;
};
```
### Core models
```ts
type Board = {
slug: string;
name?: string;
description?: string;
icon?: string;
current?: boolean;
};
type TaskStatus = 'triage' | 'todo' | 'ready' | 'running' | 'blocked' | 'done' | 'archived';
type TaskSummary = {
id: string;
title: string;
status: TaskStatus;
assignee?: string | null;
priority?: number | null;
createdAt?: string;
updatedAt?: string;
blockedReason?: string | null;
parentIds?: string[];
childIds?: string[];
};
type TaskDetail = TaskSummary & {
body?: string | null;
comments?: TaskComment[];
runs?: TaskRun[];
events?: TaskEvent[];
attachments?: TaskAttachment[];
};
type TaskComment = {
id?: string;
author?: string;
body: string;
createdAt?: string;
};
type TaskRun = {
id?: string;
status?: string;
startedAt?: string;
finishedAt?: string;
logAvailable?: boolean;
};
type TaskEvent = {
id?: string;
type: string;
message?: string;
createdAt?: string;
};
type TaskAttachment = {
id: string;
filename: string;
sizeBytes?: number;
mimeType?: string;
};
```
### BFF endpoints
Read:
- `GET /api/health` -> `{ status, version, bridge: { configured, reachable } }`
- `GET /api/me` -> current Telegram user + role + auth expiry
- `GET /api/boards` -> `Board[]`
- `GET /api/boards/:board/tasks?status=&assignee=&q=&limit=&cursor=` -> paginated `TaskSummary[]` grouped client-side or server-side
- `GET /api/boards/:board/tasks/:taskId` -> `TaskDetail`
- `GET /api/boards/:board/tasks/:taskId/log?lines=200` -> redacted log tail
- `GET /api/boards/:board/events/stream` -> SSE for future live updates; polling fallback required
Write:
- `POST /api/boards/:board/tasks`
- `POST /api/boards/:board/tasks/:taskId/comment`
- `POST /api/boards/:board/tasks/:taskId/assign`
- `POST /api/boards/:board/tasks/:taskId/promote`
- `POST /api/boards/:board/tasks/:taskId/block`
- `POST /api/boards/:board/tasks/:taskId/unblock`
- `POST /api/boards/:board/tasks/:taskId/archive`
- `POST /api/boards/:board/dispatch`
Write payload examples:
```json
{
"title": "Implement mobile task drawer",
"body": "Acceptance criteria...",
"assignee": "developer",
"priority": 3,
"workspaceKind": "worktree",
"workspacePath": null,
"goalMode": false,
"maxRuntimeSeconds": 3600,
"idempotencyKey": "optional-client-generated-key"
}
```
```json
{ "body": "Looks good, unblock after config is set." }
```
```json
{ "assignee": "reviewer" }
```
```json
{ "reason": "Needs maintainer decision on API shape." }
```
### Bridge endpoint behavior
Bridge can mirror BFF paths under `/bridge/v1/boards/...`, but it must be internal-only. It should return the same envelope. It must:
- Validate all slugs and IDs before spawning Hermes CLI.
- Use a command allowlist, not arbitrary pass-through.
- Set `HERMES_HOME` from env, not request payload.
- Set `HERMES_KANBAN_BOARD` or `--board` from validated board slug.
- Enforce timeouts per command.
- Redact and normalize errors.
## 11. UX requirements
Telegram Mini App:
- Respect Telegram theme variables and safe areas.
- Use Telegram-style components for lists, section headers, buttons, badges, bottom sheets/drawers, destructive confirmations.
- Use haptic feedback only for meaningful actions if SDK supports it.
- `MainButton`/bottom action area for primary task action on mobile.
- HashRouter or equivalent router compatible with Telegram webview and static hosting.
Browser/mobile:
- Responsive board: columns on desktop, status tabs/list on mobile.
- Fast read path: skeleton loading, optimistic comment append only after server accepts writes or clearly mark pending.
- Polling fallback; SSE enhancement later.
- Accessible color contrast and keyboard navigation in browser.
Community docs/quality:
- `.env.example` only placeholders.
- No real bot tokens, Hermes keys, domains, local IPs, or passwords.
- README in English plus Russian quickstart or `README.ru.md`.
- `SECURITY.md`, `CONTRIBUTING.md`, CI, tests, build checks.
## 12. Documentation outline
### English docs
1. `README.md`
- What is Hermes Kanban Mini App?
- Screenshots/GIFs
- Architecture summary
- Quickstart matrix: Vercel + bridge, Docker Compose, local dev
- Security warning: static frontend cannot run Hermes CLI
- Links to full docs
2. `docs/installation.en.md`
- Prerequisites: Node.js, Hermes Agent, Telegram bot, HTTPS URL
- Create Telegram bot and configure Mini App domain
- Vercel frontend/BFF deploy
- Self-hosted bridge setup
- Docker Compose setup
- Local dev setup with mock Telegram auth
3. `docs/configuration.en.md`
- All env vars
- Auth modes
- Board allowlist
- Rate limit settings
- Reverse proxy examples
4. `docs/security.en.md`
- Threat model
- Token handling
- Telegram initData validation
- Bridge exposure hardening
- Responsible disclosure
5. `docs/api.en.md`
- BFF and bridge endpoint contracts
- Error codes
- SSE/polling behavior
6. `docs/migration-from-fastapi-prototype.en.md`
- Mapping current prototype endpoints to new TS BFF/bridge
- Removed assumptions
- Compatibility notes
### Russian docs
1. `README.ru.md`
- Что это и кому нужно
- Рекомендуемая схема установки
- Главное предупреждение про Vercel/static и локальный Hermes CLI
2. `docs/installation.ru.md`
- Telegram BotFather
- Vercel
- self-hosted bridge
- Docker Compose
- локальный dev
3. `docs/configuration.ru.md`
- Переменные окружения
- режимы авторизации
- allowlist пользователей/досок
4. `docs/security.ru.md`
- модель угроз
- где хранить токены
- как безопасно открыть bridge
5. `docs/api.ru.md`
- контракт API
- ошибки
6. `docs/migration-from-fastapi-prototype.ru.md`
- план миграции с текущего прототипа
## 13. Environment variables for the new stack
Frontend build-time public vars:
```bash
VITE_APP_NAME=Hermes Kanban
VITE_BFF_BASE_URL= # empty for same-origin Vercel deployment
VITE_TELEGRAM_BOT_USERNAME= # optional display/link only, not token
```
Vercel BFF server vars:
```bash
TELEGRAM_BOT_TOKEN= # secret, server-only
TELEGRAM_ALLOWED_USER_IDS= # comma-separated Telegram IDs
TELEGRAM_ADMIN_USER_IDS= # optional
SESSION_SECRET= # random 32+ bytes
BRIDGE_BASE_URL= # https://bridge.example.com
BRIDGE_TOKEN= # secret, server-to-server
ALLOWED_ORIGINS= # exact frontend origins
AUTH_MAX_AGE_SECONDS=3600
READ_AUTH_MAX_AGE_SECONDS=86400
RATE_LIMIT_PER_MINUTE=60
```
Bridge server vars:
```bash
BRIDGE_TOKEN= # must match BFF
HERMES_BIN=hermes
HERMES_HOME= # target Hermes profile home
HERMES_KANBAN_BOARD=default
BRIDGE_HOST=127.0.0.1
BRIDGE_PORT=8787
BRIDGE_ALLOWED_BOARDS=default
COMMAND_TIMEOUT_SECONDS=30
LOG_TAIL_MAX_LINES=500
```
Docker Compose vars should mirror the same names. `.env` must stay gitignored; `.env.example` must contain placeholders only.
## 14. Migration plan from current FastAPI prototype
Do not modify the current Python implementation until this ADR is accepted.
Phase 0: Freeze and document
- Keep current prototype uncommitted as reference only.
- Add this ADR.
- Record endpoint inventory and data shapes from `backend/app/main.py`, `adapter.py`, `auth.py`, and frontend calls.
Phase 1: Extract contracts
- Create shared TypeScript schemas for Board, TaskSummary, TaskDetail, API envelope, and write payloads.
- Write contract tests against fixtures copied from current prototype responses.
- Define error code taxonomy.
Phase 2: Build TypeScript bridge
- Implement Node bridge with fixed command allowlist matching current `HermesCliKanbanAdapter` behavior.
- Port Telegram-independent validation helpers: board slug, task ID, safe log limits, error redaction.
- Add tests that assert `shell:false` / `execFile` usage and reject invalid IDs/slugs.
- Verify against local `hermes kanban` commands.
Phase 3: Build Vercel BFF
- Implement Telegram initData validation using a maintained Node package such as `@tma.js/init-data-node` or a small audited HMAC helper with tests from Telegram examples.
- Add allowlist auth and bridge proxy endpoints.
- Add rate limiting and CORS/origin checks.
Phase 4: Rebuild frontend UX
- Keep React + TypeScript + Vite.
- Replace ad-hoc Telegram integration with Telegram Apps SDK React package.
- Add Telegram UI components, mobile task drawer, board status tabs, desktop columns.
- Use BFF API only; no direct bridge/Hermes calls from browser.
Phase 5: Docs and release hardening
- Write EN/RU docs from outline.
- Add `.env.example`, `SECURITY.md`, `CONTRIBUTING.md`, CI.
- Add unit tests, contract tests, frontend tests, build check.
- Add Docker Compose example.
- Add Vercel deployment guide.
Phase 6: Optional legacy compatibility
- Either remove Python prototype from the public release or move it to `legacy/fastapi-prototype` with clear unsupported status.
- Do not publish both as equal recommended paths; it confuses users.
## 15. Consequences
Positive:
- Clear Vercel-first story without lying about local CLI access.
- Secrets stay server-side.
- TypeScript types can be shared across frontend, BFF, and bridge.
- Bridge is optional but honest: users who want real Hermes control must run a trusted server near Hermes.
- Future Hermes native Kanban HTTP API can replace bridge internals without changing frontend UX.
Negative/tradeoffs:
- Requires two deploy targets for real use: Vercel app and bridge.
- Public bridge exposure needs careful docs.
- Slightly more moving parts than a single FastAPI container.
- Until Hermes exposes first-class Kanban HTTP endpoints, the bridge still depends on CLI JSON behavior.
## 16. Rejected alternatives
### Static Vercel app directly runs Hermes CLI
Rejected. Browsers/static hosting cannot execute the user's local CLI. Any workaround would require unsafe local agents or browser extensions and is not community-ready.
### Static app directly calls Hermes API Server with API key
Rejected for public/community default. Hermes API Server bearer key controls a full agent toolset including terminal; exposing it to JavaScript is a critical security issue.
### Keep single Python/FastAPI backend as recommended public stack
Rejected as recommended stack. It works locally and may be a useful prototype, but it conflicts with TypeScript/Vercel preference and mixes trust boundaries.
### Full Next.js monolith
Not selected. Next.js could host frontend+BFF on Vercel, but Vite static + small serverless BFF is simpler, lighter, and closer to official Telegram Mini Apps React template patterns. If future SSR/auth needs grow, migration to Next.js remains possible.
### Bridge reads `kanban.db` directly
Rejected for v1. Direct DB reads couple the project to Hermes internals and risk schema drift. Use official CLI behavior first; switch to official HTTP API when available.
## 17. Precise next implementation milestones
M1. Contract foundation
- Add `packages/shared` or `src/shared` TypeScript schemas.
- Add API envelope and error code tests.
- No UI rewrite yet.
M2. Bridge MVP
- Create `apps/bridge` TypeScript service.
- Implement `GET /health`, `GET /bridge/v1/boards`, `GET /bridge/v1/boards/:board/tasks`, `GET /bridge/v1/boards/:board/tasks/:taskId`.
- Use fixed `execFile`/`spawn` calls to `hermes kanban` with tests.
M3. BFF auth MVP
- Create Vercel API routes.
- Validate Telegram initData in server code.
- Proxy read endpoints to bridge.
- Add auth fixtures/tests.
M4. Frontend read-only UX
- Migrate current React UI to new BFF contract.
- Add Telegram SDK initialization and Telegram UI theme handling.
- Ship read-only boards/tasks/log tail.
M5. Write actions
- Add create/comment/assign/promote/block/unblock/archive/dispatch.
- Add confirmations and role checks.
- Add audit log fields passed to bridge.
M6. Release docs and CI
- EN/RU install docs.
- `.env.example` for frontend, BFF, bridge, Docker Compose.
- `SECURITY.md`, `CONTRIBUTING.md`.
- CI: typecheck, lint, unit tests, frontend build, bridge tests.
M7. Optional live updates
- SSE endpoint from BFF to frontend.
- Bridge polling or future Hermes dashboard/events integration.
- Graceful fallback to polling.
## 18. Acceptance criteria for starting app-code rewrite
Do not write implementation code until these are accepted:
- This ADR stack decision is approved.
- Maintainer confirms whether bridge is a separate `apps/bridge` service or same package as BFF with a runtime mode switch.
- Maintainer confirms whether the public repo should delete the Python prototype or keep it under `legacy/`.
- First milestone is limited to contracts + tests + scaffold, not feature-complete UI.