hermes-kanban-miniapp/docs/architecture-adr.md
kisskin ade88808fb
Some checks failed
CI / backend (push) Failing after 37s
CI / frontend (push) Failing after 21s
feat: M4 docker deployment - bridge + frontend containers
- packages/bridge/Dockerfile: multi-stage build (node:22-alpine)
- frontend/Dockerfile: multi-stage build + nginx:alpine
- frontend/nginx.conf: proxy /bridge to bridge service
- docker-compose.yml: bridge (3456) + frontend (8080)
- .dockerignore: exclude backend/node_modules/dist
- Fix vite proxy target to port 3456
- .env with bridge vars (AUTH_MODE=dev)
2026-07-10 14:58:20 +03:00

665 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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