Skip to content

C4 container view — bc-subscriptions (Phase 1, Cloudflare)

Generated from a canonical source

This page is a read-only projection of docs/architecture/c4-container.md. Edit the canonical file, then run npm --prefix tools/project-knowledge-derive run derive.

@generated — mechanically parsed from infra/cloudflare/wrangler.toml, apps/api/src/types.ts (the Env binding contract), and the sibling apps/*/wrangler.toml / tools/*/worker/wrangler.toml deploy configs by tools/arch-derive/. Do not hand-edit — regenerate with npx tsx tools/arch-derive/derive.ts whenever a wrangler.toml or the Env interface changes. CI (arch-derive-ci.yml) fails if the committed docs drift from a fresh derive, which is what makes the derives_from contract above real (mirrors the pattern proven by tools/erd-derive/ for the ERD).

Anti-fabrication note. Every node below traces to a binding declared in a real wrangler.toml. ARCHITECTURE.md §0 lists a Cloudflare object-storage binding as part of the Phase-1 product stack, but no product-runtime wrangler.toml (apps/api, apps/storefront-svelte, apps/email-consumer) declares that binding type — only the tools/archaeology/worker/ substrate tool (not a product deployable) does. This generator therefore correctly omits that binding from the product container view below; §0 is stale on this point pending a spec fix, not this diagram.

5 deployable configs parsed, 12 apps/ directories enumerated. Cross-cutting context: see ARCHITECTURE.md §0 — Current Architecture (as built).

C4 legend. This is a flowchart-style rendering of a C4 container diagram (GitHub does not render Mermaid C4Container/C4Context blocks, so a plain flowchart stands in). Rectangles are containers (independently deployable units); cylinders are datastores; the diagram omits the C4 component layer. Person actors are drawn as ((...)) circles.

flowchart TD
  Merchant((Merchant / Staff))
  Subscriber((Subscriber))

  subgraph containers ["Product-runtime containers"]
    Admin["Admin UI\napps/admin\nCF Pages"]
    API["API + Webhooks Worker\napps/api (subs-api)\nCF Workers"]
    Portal["Subscriber Portal\napps/storefront-svelte (subs-storefront-svelte)\nCF Pages (assets) + Workers routing"]
    Catalyst["Catalyst Storefront Integration\napps/storefront-catalyst\nCF Pages"]
    Docs["Docs Site\napps/docs-site (MkDocs)\nCF Pages"]
    EmailConsumer["Email Consumer Worker\napps/email-consumer (subs-email-consumer)\nCF Workers (queue consumer)"]
  end

  subgraph data ["Bindings — infra/cloudflare/wrangler.toml"]
    DB[("DB\nD1: subs-api-d1")]
    CATEGORY_CACHE[("CATEGORY_CACHE\nKV namespace")]
    CHANNEL_CACHE[("CHANNEL_CACHE\nKV namespace")]
    EVENTS_QUEUE{{"EVENTS_QUEUE\nQueue: subs-events"}}
    RATE_LIMITER[("RATE_LIMITER\nratelimit binding")]
  end

  Merchant -->|BC admin iframe| Admin
  Subscriber -->|hosted portal| Portal
  Subscriber -->|storefront widget / PDP| Catalyst
  Admin -->|REST, signed JWT| API
  Portal -->|service binding SUBS_API| API
  Catalyst -->|REST| API
  API --> DB
  API --> CATEGORY_CACHE
  API --> CHANNEL_CACHE
  API -->|produces| EVENTS_QUEUE
  API --> RATE_LIMITER
  EVENTS_QUEUE -->|consumes| EmailConsumer
Container Service Source config
Admin UI Cloudflare Pages apps/admin/ (Vite SPA)
API + Webhooks Cloudflare Workers (subs-api) infra/cloudflare/wrangler.toml
Subscriber Portal Cloudflare Pages assets + Workers routing (subs-storefront-svelte) apps/storefront-svelte/wrangler.toml
Catalyst Storefront Integration Cloudflare Pages apps/storefront-catalyst/
Docs Site Cloudflare Pages apps/docs-site/mkdocs.yml
Email Consumer Cloudflare Workers (subs-email-consumer) apps/email-consumer/wrangler.toml
DB (D1) Cloudflare D1 infra/cloudflare/wrangler.toml
CATEGORY_CACHE (KV) Cloudflare KV infra/cloudflare/wrangler.toml
CHANNEL_CACHE (KV) Cloudflare KV infra/cloudflare/wrangler.toml
EVENTS_QUEUE (Queue: subs-events) Cloudflare Queues infra/cloudflare/wrangler.toml
RATE_LIMITER (ratelimit) Cloudflare rate-limit binding infra/cloudflare/wrangler.toml

Env binding cross-check (apps/api/src/types.ts)

The Env interface declares 18 fields (5 required, 13 optional). Bindings above (DB, EVENTS_QUEUE, RATE_LIMITER, CATEGORY_CACHE, CHANNEL_CACHE) must have a matching field here; the remainder are secrets (wrangler secret put, not declared in wrangler.toml) or runtime config vars.

DB
BC_CLIENT_ID
BC_CLIENT_SECRET
BC_API_TOKEN
CREDENTIAL_ENCRYPTION_KEY
CREDENTIAL_ENCRYPTION_KEY_PREV?
CREDENTIAL_KEY_EPOCH?
EVENTS_QUEUE?
RATE_LIMITER?
CATEGORY_CACHE?
CUSTOM_FIELD_CACHE?
CHANNEL_CACHE?
PORTAL_ORIGIN?
SSO_HANDOFF_SECRET?
BC_WEBHOOK_SIGNING_SECRET?
BC_STOREFRONT_TOKEN?
STRIPE_WEBHOOK_SECRET?
NETWORK_UPDATER_WEBHOOK_SECRET?