DOCS / DATA & INFRASTRUCTURE / HOSTING & INFRASTRUCTURE

Hosting & infrastructure

vCareer runs entirely on Cloudflare — landing + docs as static Pages, the authed app as a Pages-deployed SPA, the API + cron as a single Worker, and the database as Cloudflare D1. One vendor, EU-region primary storage, no AWS or external infra in the request path.

~4 MIN READ

Topology

                ┌──────────────────┐
                │   vcareer.net    │  landing + docs (Pages, static)
                └──────────────────┘
                          │
                          ▼
                ┌──────────────────┐
                │ app.vcareer.net  │  React SPA (Pages, static)
                └──────────────────┘
                          │ fetch
                          ▼
                ┌──────────────────┐
                │ api.vcareer.net  │  Hono on Workers
                │  ┌────────────┐  │   - HTTP routes
                │  │  scheduled │  │   - 1-min cron (pipe + tick)
                │  └────────────┘  │
                └──────────────────┘
                          │ binding
                          ▼
                ┌──────────────────┐
                │   D1 (SQLite)    │  primary region: weur
                └──────────────────┘
                          │
                          ▼ outbound only
              ┌──────────┴──────────┐
              │  VATSIM (auth + feed)│
              │  aviationweather.gov │
              └──────────────────────┘
  

Hosts

HOSTRUNSNOTES
vcareer.net Astro static (Cloudflare Pages) Public landing + docs.
app.vcareer.net React + Vite SPA (Cloudflare Pages) Authed app. Talks to api.vcareer.net.
api.vcareer.net Hono on Cloudflare Workers All API routes + cron. Single Worker, multiple handlers.
(internal) D1 Cloudflare D1 (managed SQLite) Application database. Worker binding, never exposed externally.

Data flow

  • Sign-in: Browser → api.vcareer.net/auth/login → VATSIM Connect → api.vcareer.net/auth/callback → set session cookie → redirect to app.vcareer.net.
  • Authed reads/writes: app.vcareer.net → fetch → api.vcareer.net/me/* with the session cookie → Hono router → Prisma → D1.
  • Cron tick (1-min schedule): scheduled handler → pollAndPersist() (position pipe, 4× per minute via internal sleeps) + tickMissions() (completion verifier).
  • Outbound: VATSIM auth + data feed + Member API + aviationweather.gov. All public, all read-only. No data egress to third-party processors.

Scheduled work

Two independent jobs share the same Cloudflare cron invocation:

  • pollAndPersist() — polls the VATSIM v3 data feed every 15s (cron fires every 60s; we sleep+poll 4×). Filters to known users; writes PilotPositionSample rows.
  • tickMissions() — reads recent position samples + VATSIM session record for each accepted mission, decides state transitions, writes MissionCompletionLog + updates balances.

Both are pure functions of (prisma, externalProviders) so they're identically runnable in a local Bun process for development and on Cloudflare Workers in prod.

Observability

  • Workers Analytics — built-in dashboards for request count, errors, CPU time. Free.
  • Sentry — client + Worker side, on by default in prod. Errors only; we do not capture user actions.
  • Logpush — Workers logs piped to R2 or Datadog if/when log volume justifies it. Off in alpha.

Regions + residency

D1 primary region is weur (Western Europe — Frankfurt). Read replicas may exist in other regions as part of Cloudflare's automatic distribution; writes always go to the primary. EU user-data residency is the default; we don't move data out of the EEA for processing.

Cloudflare is a data processor under GDPR; we sign their standard DPA on the production account. We never expose D1 directly — every query goes through the Worker, which authenticates the session cookie before reading.

See Privacy for the user-facing summary.