Companion ops guide for [[services/ihnyc-rc-vote]]. This file captures runbooks and operational notes.

Purpose

Single place for how the app runs, how to develop safely, and how to recover.

Stack at a Glance

  • Cloudflare Worker (entry: src/index.ts) with Hono routes under src/routes.
  • Assets: public/ served via Worker assets binding.
  • Data: Cloudflare D1 (DB binding). Durable Object TokenManager handles token coordination.
  • Scheduler: Cron every minute (wrangler.jsonc).
  • Email: outbound templated emails in src/utils/email.ts (configure keys via secrets).

Local Development

  • Requirements: Node 22+, npm 10+, wrangler installed.
  • Env/config:
    • Copy .dev.vars.example to .dev.vars and fill RESEND_API_KEY, FROM_EMAIL, BASE_URL, ADMIN_API_KEY.
    • D1: use the dev DB automatically bound by Wrangler; see wrangler.jsonc.
  • Run dev server: npm install then npm run dev.
  • Type generation: npm run cf-typegen.

Testing

  • All tests: npm test
  • Unit: npm run test:unit
  • Integration (D1/Workers pool): npm run test:integration
  • Config: vitest.config.ts, uses @cloudflare/vitest-pool-workers.

Database & Migrations

  • Migrations live in migrations/ and are applied with Wrangler:
    • Remote: wrangler d1 migrations apply ihnyc-rc-vote --remote
    • Local dev: wrangler d1 migrations apply ihnyc-rc-vote
  • When altering tables with FKs, avoid data loss:
    • Preserve dependent tables (tokens, ballots, invites, candidates) before recreating elections.
    • Follow the pattern in migrations/011_batch_invites.sql (backup → recreate → restore).
  • Inspect schema: wrangler d1 execute ihnyc-rc-vote --remote --command="SELECT name, sql FROM sqlite_master WHERE type='table';"
  • See migrations for detailed migration documentation.

Backups & Recovery (D1 Time Travel)

  • D1 Time Travel is always on (no setup). You can restore to any minute in the last 30 days (paid) or 7 days (free).
  • Check current bookmark:
    • wrangler d1 time-travel info ihnyc-rc-vote
  • Get bookmark for a past timestamp (UTC or RFC3339):
    • wrangler d1 time-travel info ihnyc-rc-vote --timestamp="2026-01-03T20:20:00+00:00"
  • Restore to a bookmark (destructive, overwrites DB):
    • wrangler d1 time-travel restore ihnyc-rc-vote --bookmark=...
  • Keep the “undo” bookmark the CLI prints after a restore; you can restore back if needed.
  • Reference: Cloudflare docs on Time Travel https://developers.cloudflare.com/d1/reference/time-travel/

Deployment

  • Production deploy: npm run deploy (alias for wrangler deploy --minify).
  • Secrets (prod): wrangler secret put RESEND_API_KEY, FROM_EMAIL, BASE_URL, ADMIN_API_KEY.
  • Observability: wrangler.jsonc has observability.enabled: true.

Key Application Notes

  • Invite modes: invite_mode on elections supports individual and batch. Batch sends a single email listing all active elections for an email.
  • Bulk invite management: Use /admin/bulk-invites to send invites for multiple elections to multiple recipients in one operation. Accessible from Admin Dashboard → ”📧 Bulk Invites”.
  • Rate limits & auth: see src/middleware/ (auth and rate-limit).
  • Notion integration: configure in settings_json; code in src/utils/notion.ts and related routes.
    • See notion-setup for Notion database allowlist configuration.
  • Templates: server-rendered HTML in src/templates/.

Operational Runbook (quick)

  • Migration safety: run on staging first; for prod have a Time Travel bookmark before apply.
  • Recovery from bad migration:
    1. wrangler d1 time-travel info ... --timestamp="<pre-migration UTC>"
    2. wrangler d1 time-travel restore ... --bookmark=<bookmark>
    3. Re-run fixed migration.
  • Validate data after migrations:
    • SELECT COUNT(*) FROM elections; and dependent tables: tokens, ballots, invites, candidates.

Documentation

Additional documentation in IHNYC-Remote:

Core Features

Infrastructure

Integrations

Operations

Where to Add More

  • Infra changes: update this file.
  • New services or env vars: add a short “How to configure & test” subsection here.
  • Detailed feature documentation: add a new IHNYC-Remote doc and link from here.