8.4 KiB
phase: 02-bot-skeleton-initdata-security plan: 01 subsystem: infra tags: [telegram, grammy, cloudflare-workers, wrangler, npm-workspaces, typescript, hmac, tdd]
Dependency graph
requires:
- phase: 01-design-tokens-rams-foundation provides: npm workspace pattern (root delegate scripts + per-workspace package.json) and hand-rolled zero-dep test runner shape (theme/scripts/audit.test.mjs, D-12 precedent) provides:
bot/npm workspace (@araharah/bot) with grammy ^1.46.0 + wrangler ^4.151.3 + TypeScript ^5.9.3, registry-verifiedbot/src/config.tsEnv contract (BOT_TOKEN, WEBHOOK_SECRET, chat_sessions KVNamespace) consumed by plans 02/03bot/wrangler.jsoncWorker config with main src/webhook.ts + chat_sessions KV binding (id placeholder → plan 03)bot/test/harness.mjs+runner.mjs— D-12 zero-dependency runner, suite-load failures become FAIL casesbot/test/initdata.test.mjs— RED vector suite T1–T7 (contract for plan 02 GREEN)- Secret hygiene baseline:
.gitignore(.dev.vars, .wrangler/) +.dev.vars.exampleshipped in first commit affects: [02-02 security core (GREEN against this suite), 02-03 commands+KV+deploy, phase 3 mini app shell, phase 5 session envelope]
Tech tracking
tech-stack: added: [grammy ^1.46.0, wrangler ^4.151.3, typescript ^5.9.3, @cloudflare/workers-types ^5.20261022.1, @types/node ^26.6.5] patterns: [npm workspace sibling to theme/ with root delegate scripts, hand-rolled registry/runner test harness (D-12), erasable-only TypeScript under type-stripping, RED-first TDD for security gates, secrets as wrangler secrets + gitignored .dev.vars mirror]
key-files: created: - bot/package.json - bot/tsconfig.json - bot/wrangler.jsonc - bot/.gitignore - bot/.dev.vars.example - bot/src/config.ts - bot/test/harness.mjs - bot/test/runner.mjs - bot/test/initdata.test.mjs modified: - package.json (workspaces + bot:* delegate scripts) - package-lock.json (bot workspace deps)
key-decisions:
- "Lazy per-case dynamic import of ../src/security/initdata.ts in the RED suite: each vector reports its own FAIL (8 named FAILs at RED) instead of losing the whole suite to one load error; the suite-load-failure path survives as the runner's crash guard"
- "Test-side HMAC signing helper uses node:crypto (two-step Telegram derivation) so the RED suite is provably independent of the SubtleCrypto implementation path plan 02 writes; T7 pins the primitive against the RFC-4231 case-1 vector"
patterns-established:
- "D-12 harness: import { test } from './harness.mjs' in any bot/test/*.test.mjs; runner auto-discovers, prints PASS/FAIL per case + '[runner] N passed, M failed', exit 0 only when all green"
- "Root delegate scripts: bot:test / bot:typecheck / bot:dev / bot:deploy / bot:commands (mirrors Phase 1 build:* pattern)"
- "Erasable-only TS in bot/src (no enums/parameter properties/namespaces); tsconfig: strict + noEmit + allowImportingTsExtensions + workers-types"
requirements-completed: [ONBD-01, PRIV-01, PRIV-02] # substrate only — D-15/D-17 secret-hygiene + D-12 verification substrate for these reqs; full req delivery lands in plans 02/03
Metrics
duration: 4min completed: 2026-10-22
Phase 2 Plan 1: Bot Workspace Scaffold + RED initdata Vector Suite Summary
bot/ npm workspace stood up with grammy/wrangler/TS and the D-12 zero-dep test harness, shipping a RED-demonstrated initdata HMAC vector suite (T1–T7, 8 FAILs / exit 1) that plan 02 goes GREEN against
Performance
- Duration: 4 min
- Started: 2026-10-22T03:35:10Z
- Completed: 2026-10-22T03:38:23Z
- Tasks: 2
- Files modified: 11 (9 created, 2 modified)
Accomplishments
bot/workspace installs and typechecks clean: grammy@1.46.0 + wrangler@4.151.3 + typescript@5.9.3 landed exactly in ranges pinned by RESEARCH.md (lockfile-verified)Envinterface contract (BOT_TOKEN,WEBHOOK_SECRET,chat_sessions: KVNamespace) committed as the type anchor plans 02/03 implement against- Secret hygiene from the first commit:
.gitignore(.dev.vars, .wrangler/) ships in the same commit as.dev.vars.example;git check-ignore bot/.dev.varsverified - Hand-rolled runner (D-12) replicates Phase 1's pattern in a second workspace: zero deps, suite-load failures become FAIL cases (never crashes),
[runner] N passed, M failedsummary, exit 1 on any failure - RED vector suite final-contract: T1 known-good, T2 tampered, T3 stale (3600 s), T4 boundary (899 ok / 901 stale), T5 order-independence, T6a–c malformed, T7 RFC-4231 node:crypto independence pin — demonstrated RED with 8 named FAILs against missing
src/security/initdata.ts
task Commits
Each task was committed atomically:
- task 1: bot workspace scaffold with Env contract (D-09/D-10/D-11/D-15) —
c9b6ba4(feat) - task 2: Wave 0 harness + RED initdata vector suite (D-12, TDD RED) —
fc5001a(test)
Note: task 2 is TDD RED by plan design; the GREEN commit belongs to plan 02 task 1 (wave 2), which will flip this suite from 8 FAILs to all-pass.
Files Created/Modified
package.json— workspaces["theme","bot"]+bot:*delegate scripts (theme scripts untouched)bot/package.json—@araharah/botworkspace: test/typecheck/dev/deploy/commands scripts, grammy + wrangler/TS depsbot/tsconfig.json— strict ES2023, noEmit, moduleResolution bundler, workers-types, allowImportingTsExtensionsbot/wrangler.jsonc— name araharah-bot,main: src/webhook.ts, chat_sessions KV binding (id = KV_ID_PLACEHOLDER until plan 03'swrangler kv namespace create)bot/.gitignore— exactly.dev.vars+.wrangler/(D-15/D-17)bot/.dev.vars.example— shape only (BOT_TOKEN=, WEBHOOK_SECRET=, WEBHOOK_URL=), no valuesbot/src/config.ts—export interface Env+ KVNamespace member (contract only, no implementation)bot/test/harness.mjs— zero-dep registry:test(name, fn)/registrybot/test/runner.mjs— D-12 runner: sorted*.test.mjsdiscovery, suite-load error → stored FAIL case, PASS/FAIL + summary + exit codesbot/test/initdata.test.mjs— vector suite T1–T7; signing helper in-file via node:crypto; fixtures in-processpackage-lock.json— bot workspace dep tree (grammy 1.46.0, wrangler 4.151.3 pinned)
Decisions Made
- Lazy per-case validator import in the RED suite: the plan allowed either "suite load failed" or per-case FAILs; per-case chosen so RED shows each vector's own failure (and plan 02's GREEN progress will be visible per-vector)
- Test-side signing via node:crypto (not SubtleCrypto): keeps the RED suite's expected-hash path independent of the implementation path under test; T7's RFC-4231 pin makes that independence a guarded invariant
Deviations from Plan
None - plan executed exactly as written. (T6 was split into T6a/T6b/T6c named cases within the plan's single T6 bullet — this stays within the documented contract and yields ≥7 independent FAILs at RED.)
Issues Encountered
- Verified at execution start that Node 24.18.0 dynamic-imports
.tsmodules from.mjsnatively (probe in /tmp/opencode/) — no--experimental-strip-typesflag needed, confirming the runner + type-test design works without tooling changes - npm emitted allow-scripts warnings for esbuild/workerd/@parcel/watcher install scripts (self-hosted toolchain hygiene; unrelated to this plan — dev-only tooling, noted for awareness, no action taken per scope boundary)
User Setup Required
None for this plan. (Real secrets — BOT_TOKEN / WEBHOOK_SECRET / Cloudflare account — are only needed at plan 03's deploy checkpoint, per D-15/D-16.)
Next Phase Readiness
- Plans 02/03 can implement against
bot/src/config.tsEnv +bot/wrangler.jsoncentry points;npm test -w botandnpm run bot:typecheckare the wave verification commands - Plan 02 task 1 must make
bot/test/initdata.test.mjsGREEN (implementsrc/security/initdata.tsper the T1–T7 contract) - Plan 03 task 1 fills the real KV namespace id into
bot/wrangler.jsonc(replaceKV_ID_PLACEHOLDER)
Self-Check: PASSED
- All 9 created files verified present on disk (bot/ tree + test files)
- Commits verified in history:
c9b6ba4(feat task 1),fc5001a(test task 2 RED) git check-ignore bot/.dev.vars→ ignored (secret hygiene confirmed)- STATE.md / ROADMAP.md untouched by this agent (ROADMAP.md's pre-existing modification belongs to the orchestrator)
Phase: 02-bot-skeleton-initdata-security Completed: 2026-10-22