Skip to content

Architecture

RepoWrangler is a single-tenant, read-only estate command center built as a provider-neutral core with swappable infrastructure adapters. The domain, API, and UI are written once; where it runs (Cloudflare, Node/Docker, Azure, Kubernetes), what stores its data (D1, SQLite, PostgreSQL), and how people sign in (GitHub, Entra) are all adapter choices behind stable seams. This is the platform-neutrality commitment of ADR-013.

C4 level 1 — system context

RepoWrangler reads from providers and never writes to them (ADR-003, ADR-008). It stores a normalized snapshot of the estate and evaluates health locally.

C4 level 2 — containers

The same apps/worker Hono app runs in two host shells:

  • Cloudflare Workerapps/worker deployed directly; D1 for storage, wrangler cron for the scheduler, the assets runtime for the SPA.
  • Node host (apps/server, ADR-014) — serves the same app.fetch via @hono/node-server, storage via the SQLite or PostgreSQL adapter, an in-process minute-tick scheduler, and static SPA serving. This is what the Docker, Azure Container Apps, and Kubernetes recipes run.

C4 level 3 — components & packages

Key packages:

PackageResponsibility
apps/webReact SPA (host-agnostic; VITE_API_BASE_URL, ADR-011).
apps/workerHono app: API (/api/v1), auth (/auth), webhooks, scheduled sync.
apps/serverNode host shell (SQLite/PostgreSQL) — zero Cloudflare.
domainProvider-neutral entities and explainable health rules.
contractsShared DTOs / validation between API and SPA.
provider-github / provider-gitlab / provider-mockData-source adapters.
persistence-coreThe storage seam (IDataStore).
persistence-d1The SQLite/D1-dialect queries the API calls (~60 sites).
persistence-sqlite / persistence-postgresD1-compatible adapters for the Node host.
creditsOpen-source attribution surfaced in-product.

The three seams that make it portable

  1. Storage (PN-1). The API calls c.env.DB, a D1-shaped handle. Cloudflare supplies real D1; the Node host supplies a D1-compatible object backed by SQLite (ADR-014) or PostgreSQL (ADR-015). The persistence SQL is written once; PostgreSQL gets compatibility datetime() functions + a tiny translator so the same migrations/ and queries run on both.
  2. Host (PN-2). apps/worker re-exports { app, runScheduled } and the Env type. Any host shell can drive the same app — Cloudflare does it natively; apps/server does it on Node.
  3. Auth (PN-5). AUTH_MODE selects the sign-in provider (GitHub App or Entra ID); both issue the same signed session cookie, so everything downstream is identical.

Data model & sync

  • Provider-neutral schema with stable internal IDs, provider external IDs stored separately, freshness metadata on every record, and deletion modeled as a state transition (tombstones, never destructive deletes). See migrations/0001_initial.sql.
  • Sync is checkpointed and resumable: bounded, claimable sync_jobs are driven by the scheduler (*/15 * * * * incremental, 17 3 * * * daily) and by webhooks for near-real-time updates, with idempotency by delivery ID (ADR-006).
  • Health is evaluated locally by explainable rules in domain, producing a per-repository attention level with the findings that justify it.

Deployment topologies

See the deployment guide for the full matrix and decision flowchart. In short: Mode A integrated single-origin (Cloudflare Worker, or the Node host serving its own SPA); Mode B decoupled SPA (GitHub Pages / Azure SWA) calling a separate API origin with CORS; Mode C self-hosted container (Docker / Azure Container Apps / Kubernetes) on SQLite or PostgreSQL.

Decision records

The full rationale lives in the ADR index. Most relevant here: ADR-004 (provider-neutral domain), ADR-005 (D1), ADR-011 (host-agnostic frontend), ADR-013 (platform neutrality), ADR-014 (Node host), ADR-015 (PostgreSQL adapter), ADR-016 (Entra sign-in). The original design pack is under docs/design/.

Apache-2.0 licensed. Read-only by design.