Architecture - vudh1/ballerwatch GitHub Wiki

Architecture

BallerWatch separates the browser UI, backend runtime, encrypted generated state, and deployment control plane.

Runtime boundaries

frontend/web/ GitHub Pages PWA
  |-- app.js              view orchestration
  |-- lib/client.js       API/session transport
  |-- lib/notification-state.js local fallback + profile normalization
             |
             v
backend/infra/web-worker/ Cloudflare Worker
       |                       |
       |                       +--> encrypted runtime-state
       +--------------------------> auth / Q&A / push registration / API

cron-job.org --> GitHub Actions --> backend/pickup + backend/league
GitHub schedule --> backend/watchdog + backend/weather
RATS changes --> backend/league --> in-app match calendar + Web Push
  • main: reviewed integration source.
  • production: exact promoted release commit.
  • runtime-state: encrypted generated data only.
  • frontend/web/: static installable PWA.
  • backend/: server, watcher, reconciliation, encryption, scheduling, and integration source.
  • GitHub Actions: source reconciliation, Calendar updates, Web Push delivery, release promotion, recovery.
  • cron-job.org: pickup every 2 minutes and league every 5 minutes.

Workers KV and Cloudflare Cron Triggers are intentionally disabled.

Source vs storage paths

The source tree is organized as frontend/ and backend/. Runtime-state document names intentionally retain stable paths such as pickup/state/feed.json and league/state/schedule.json. This keeps encrypted storage compatibility independent from source-code organization.

RATS historical Q&A uses league/state/history.json, a complete encrypted runtime-state document built from public seasonal RATS aggregates. The normal league workflow refreshes it only when stale; the Worker reads the encrypted index separately from the live schedule snapshot so dashboard hydration is not coupled to the potentially larger history archive.

Authentication

The PWA uses username/password sign-in. First-time bootstrap or forgotten-password recovery is performed by a repository administrator through Reset web user password and the temporary BALLERWATCH_RECOVERY_PASSWORD secret.

Signed user tokens carry a server-side auth revision. Password changes, recovery resets, and global sign-out advance that revision and revoke earlier tokens.

Accounts and notification state

The authenticated user document remains a complete AES-GCM encrypted runtime-state envelope. BallerWatch 7 adds a bounded notification profile per account containing read IDs, deleted IDs, and Pickup / League / App category preferences.

Notification mutations merge server-side instead of replacing the full profile, so actions from two signed-in devices converge without one device overwriting the other. Existing local notification state is migrated into the signed-in profile after authentication.

Notifications

Proactive output is limited to the documented Web Push / notification-board policy. User-specific RSVP names, confirmation state, waitlist data, read/delete state, and preferences remain authenticated/encrypted-only.

Web Push remains payload-free. Subscription state associates a push endpoint with short-lived per-device teams, private RSVP membership tags and categories inside encrypted runtime state; delivery can skip categories before contacting the browser push service. The service worker also keeps the installed-app badge and local background category preferences synchronized.

Production pinning

Scheduled workflows execute production. Release-driven Worker deployment checks out the published release. Pages also checks out production and deploys only after Worker readiness succeeds.

After an administrator requests promotion, an already-open PWA watches both the Worker version and Pages shell. Once both match the target release, it updates the service worker and reloads automatically.