Skip to content

Infrastructure

Every piece of infrastructure is a Cloudflare resource bound to the one Worker. This page is the authoritative inventory — useful for cost planning, access reviews, and onboarding an engineer.

Cloudflare resources

Resource Type Name / identifier Purpose
Worker Compute bira-school-ielts The whole app (API + web + AI routing)
Custom domain Route kategeneral.long-cao.dev The only public URL
D1 Database bira-school-db All application data (SQLite at the edge)
R2 Object storage bira-school-media Audio recordings and file uploads
KV Cache CACHE binding Read-through cache for hot endpoints
Workers AI AI AI binding Grading, tutor, transcription
Durable Object Stateful compute ExtractionJob Background OCR extraction jobs
Service binding Service kate-ocr Separate Worker running PaddleOCR for real OCR

Public name vs. internal name

Users reach the app at kategeneral.long-cao.dev. The Worker, database, and bucket keep their original bira-school-* names so bindings, secrets, and deployment history stay intact. The generated *.workers.dev URL is disabled — the custom domain is the single entry point.

Environments & configuration

  • One production environment. Configuration lives in the Worker's config file (wrangler.jsonc): the bindings above, plus non-secret variables (APP_NAME, ENVIRONMENT=production).
  • Local development runs the same Worker against a local D1 copy.

Secrets

Secrets are stored as Cloudflare Worker secrets — never in the code or config files. There are three:

Secret Used for
JWT_SECRET Signs login tokens and the signed media URLs. Required in production — the app fails closed (refuses to run) rather than falling back to a public default if it's missing.
AZURE_SPEECH_KEY Optional — enables real per-phoneme pronunciation scoring on read-aloud.
OCR_SECRET Authenticates the main Worker to the kate-ocr container service.

Secret hygiene

Never commit a secret. JWT_SECRET in particular is the master key: it authenticates every user and signs every media URL. Rotating it logs everyone out and invalidates outstanding media links. Verify it is set with wrangler secret list before any deploy that touches auth.

The OCR side-service

Real OCR (turning a photo/PDF of a test into text) can't run inside a Worker, so it lives in a separate Cloudflare Worker with a container (kate-ocr) running PaddleOCR:

  • The main Worker calls it over a private service binding (no public endpoint), authenticated with OCR_SECRET.
  • A scheduled ping keeps the container warm during business hours so a teacher's first extraction of the day is fast.
  • The AI never reads the image — PaddleOCR extracts the text faithfully; the AI only structures that text into a draft.

The frontend build

The React app is a standard Vite build. deploy builds it and bundles the output as the Worker's static assets — so the web app and API always ship together as one version. Security headers (a strict Content-Security-Policy, clickjacking and MIME protections, HSTS) are applied to those asset responses via a _headers file. See Security.

Cost model

At ~150 students:

  • Workers / D1 / R2 / KV sit in low-cost usage tiers — there is no fixed server or database bill.
  • Workers AI is the main variable cost and is metered and capped so it can't overrun the monthly budget (see AI features → Budgets).
  • Azure pronunciation is optional and separately capped in minutes by the manager.
  • kate-ocr container costs only while warm/running.

There is no third-party payment processor — tuition is manual bookkeeping, so there are no transaction fees.