Files
Arah2/.planning/phases/02-bot-skeleton-initdata-security/02-CONTEXT.md
T
2026-10-23 22:37:55 +08:00

7.6 KiB

Phase 2: Bot Skeleton + initData Security - Context

Gathered: 2026-10-22 Status: Ready for planning

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

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

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


Phase: 02-bot-skeleton-initdata-security Context gathered: 2026-10-22