112 lines
7.6 KiB
Markdown
112 lines
7.6 KiB
Markdown
# Phase 2: Bot Skeleton + initData Security - Context
|
|
|
|
**Gathered:** 2026-10-22
|
|
**Status:** Ready for planning
|
|
|
|
<domain>
|
|
## Phase Boundary
|
|
|
|
Users can reach araharah through Telegram with a working command surface (/start, /help, /privacy, plus Malay aliases) served by a grammY bot on one Cloudflare Worker, and the backend's identity + privacy architecture is provably sound from the first deploy: HMAC-validated Telegram WebApp initData with CI test vectors, one-time opaque session tokens in TTL-only KV (`chat_sessions`). Mini App shell, search, and the `guidance_sessions` envelope design are later phases.
|
|
|
|
</domain>
|
|
|
|
<decisions>
|
|
## Implementation Decisions
|
|
|
|
### Bot copy + interaction style
|
|
- **D-01:** Tone is clinical + honest (Rams-aligned): what araharah is in one sentence, no emoji clutter, no marketing flourish. Privacy statement states what is NOT stored as plainly as what is.
|
|
- **D-02:** /start is a plain text-only message — no reply keyboard, no inline buttons. Telegram commands are the interface (the Mini App launch path is Phase 4's decision).
|
|
- **D-03:** Permanent command menu contains only commands that exist in this phase (/help, /privacy) registered via setMyCommands — no stub entries for future commands.
|
|
- **D-04:** Malay command aliases are first-class citizens of the surface from this phase: `/mula` runs the same flow as `/start`, `/privasi` as `/privacy`. Later flow aliases are named now and implemented by their phases: `/pergi_ke` (~ goto), `/cari` (~ search, Phase 4). setMyCommands registers localized command descriptions via a `ms` language_code scope in addition to the default.
|
|
|
|
### Copy localization
|
|
- **D-05:** All bot copy lives in a strings module (`src/i18n/strings.ts`): English default + Bahasa Melayu `ms` locale. Telegram's per-user `language_code` picks the locale; unhandled codes fall back to English. Command aliases are handlers on the same command stack, not separate flows.
|
|
|
|
### Session token mechanics
|
|
- **D-06:** Session tokens are opaque random token ids stored in KV `chat_sessions` alongside a minimal bootstrap claim. Not JWTs — revocable and audit-friendly. Handing out the token is the DSGN-equivalent security gate: only successful initData validation mints one.
|
|
- **D-07:** Rotate-on-new: when a validated identity arrives for a chat that already holds a live session token, the old token is invalidated (overwritten). One live token per chat. The recent-stale-replay risk that a replicated validation could mint extra tokens is thereby closed structurally.
|
|
- **D-08:** The stored claim is deliberately minimal: Telegram `user.id`, `chat.id`, `issued_at`, and the user's `language_code` (needed to route the bot's future chat cards in the user's locale). No location, no routes, no profile.
|
|
|
|
### Backend repo layout
|
|
- **D-09:** Backend lives in a `bot/` workspace sibling to `theme/` (npm workspaces, root package.json gets thin delegate scripts, mirroring the Phase 1 pattern).
|
|
- **D-10:** Language is TypeScript for everything backend (grammY typings first-class, `@cloudflare/workers-types` for the runtime).
|
|
- **D-11:** bot/ layout is an explicit `src/` tree: `src/webhook.ts` (worker entry), `src/security/initdata.ts` (HMAC validation — isolated, testable without provider dependencies), `src/kv/sessions.ts` (TTL-only access layer), `src/i18n/strings.ts`, `src/bot/commands/{start,help,privacy}.ts`, wrangler config colocated.
|
|
|
|
### Validation + test harness
|
|
- **D-12:** Test runner is a hand-rolled node runner in the same shape as `theme/scripts/audit.test.mjs` (Phase 1's pattern) — no extra test dependency for this pure-Crypto HMAC vector suite.
|
|
- **D-13:** initData freshness policy: reject payloads whose `auth_date` is older than ~15 minutes; the Mini App re-validates with fresh initData on launch. This pairs with the `chat_sessions` minutes-TTL envelope.
|
|
- **D-14:** Rejection logging is category-only: tampered / stale / replayed / expired — never the raw payload, never coordinates, no user-marking timestamps. Satisfies the "logs carry no coordinate data" criterion from day one.
|
|
|
|
### Secrets + deploy setup
|
|
- **D-15:** Secrets (bot token, HMAC secret) live in Cloudflare Wrangler secrets. Local dev mirrors via a gitignored `.dev.vars` (only `.dev.vars.example` ships in the repo).
|
|
- **D-16:** Phase 2 deploy is local `npm run deploy` (wrangler deploy + setWebhook). No CI workflow yet — it arrives in Phase 3 with real Mini App users.
|
|
- **D-17:** Strict secret hygiene: no secret value ever committed, webhook URL in wrangler config, and the /privacy copy stays verifiable by reading the code's storage calls — the architecture can't hold more than it says it does.
|
|
|
|
### OpenCode's Discretion
|
|
- Exact setMyCommands scope/locale structuring details
|
|
- Webhook secret token mechanics (Telegram's `secret_token` param) at the researcher's discretion
|
|
- Exact KV TTL values inside the "minutes" envelope
|
|
- How local wrangler dev simulates KV for testing the product loop
|
|
- How the researcher verifies exact grammY initData typings (flagged research gap)
|
|
|
|
</decisions>
|
|
|
|
<specifics>
|
|
## Specific Ideas
|
|
|
|
- Command menu and bot surface should feel like the Rams product inside Telegram: spare, honest, functional. /privacy is a promise visibly kept, not a legal disclaimer.
|
|
- Malay aliases are the user's explicit addition — locals should be able to drive in their own language from day one.
|
|
- Single-file source of truth predilection carries over: the strings module is the copy's tokens.css — no copy pasted inline in handlers.
|
|
|
|
</specifics>
|
|
|
|
<canonical_refs>
|
|
## Canonical References
|
|
|
|
**Downstream agents MUST read these before planning or implementing.**
|
|
|
|
### Phase scope + decisions
|
|
- `.planning/ROADMAP.md` §Phase 2 — goal, 4 success criteria, notes (KV namespace, grammY research gap)
|
|
- `.planning/REQUIREMENTS.md` — ONBD-01, PRIV-01, PRIV-02 (this phase's requirements)
|
|
- `.planning/phases/01-design-tokens-rams-foundation/01-CONTEXT.md` — Phase 1 pattern precedent (workspace structure, hand-rolled test runner, TDD discipline established)
|
|
|
|
### Codebase
|
|
- `.planning/codebase/CONCERNS.md`, `STACK.md`, `ARCHITECTURE.md` — pre-existing repo caveats (secret handling from day one)
|
|
- `AGENTS.md` — coding conventions
|
|
|
|
</canonical_refs>
|
|
|
|
<code_context>
|
|
## Existing Code Insights
|
|
|
|
### Reusable Assets
|
|
- `theme/` npm workspace pattern (root delegates + per-workspace package.json + scripts) — the repo's established structure for every subsequent workspace
|
|
- `theme/scripts/audit.test.mjs` — hand-rolled test runner pattern: node:assert, minimal, no deps (D-12 builds on this shape)
|
|
- Package-layout precedent: isolated concerns in their own module files (e.g. tokens/components/audit separation)
|
|
|
|
### Established Patterns
|
|
- TDD discipline from Phase 1 (RED→GREEN commits where sensible)
|
|
- Type-only imports, zero-radius kebab-case vocabulary, strict gitignored `.planning/`
|
|
- npm workspaces with self-hosted assets — same mental model applies to backend secrets
|
|
|
|
### Integration Points
|
|
- `theme/dist/rams.css` + basemap style become Mini App static assets served by the same Worker in Phase 3 — Phase 2's worker must implicitly plan where static asset binding lands (researcher's structural hint, not Phase 2 implementation)
|
|
- `chat_sessions` KV namespace is the first durable namespace — its TTL/shape is what Phase 5's envelope design extends
|
|
|
|
</code_context>
|
|
|
|
<deferred>
|
|
## Deferred Ideas
|
|
|
|
- Webhook dispatch via static-asset router to inline Telegram Mini App serving (Phase 3 shapes the mixed static+api worker, not this phase)
|
|
- Metrics/diagnostics beyond category logs (Phase 7 audit scope decides)
|
|
|
|
None — discussion stayed within phase scope.
|
|
|
|
</deferred>
|
|
|
|
---
|
|
|
|
*Phase: 02-bot-skeleton-initdata-security*
|
|
*Context gathered: 2026-10-22*
|