# INTEGRATIONS

External systems wired into the xtablo monorepo. Last updated 2026-05-14.

## Database — Supabase Postgres

- **Hosted Postgres** managed by Supabase. The frontend talks to it via the JS SDK; the API talks to it via the service-role key and (for `stripe-sync-engine`) a direct `postgres://` connection string.
- **Service-role client** is constructed per-request inside the API in `apps/api/src/middlewares/middleware.ts` (`supabaseMiddleware`):
  ```ts
  createClient(config.SUPABASE_URL, config.SUPABASE_SERVICE_ROLE_KEY)
  ```
  Mounted globally for every route in `apps/api/src/routers/index.ts`.
- **Browser client** factory at `packages/shared/src/lib/supabase.ts` re-exports `createClient`; instances are produced inside `@xtablo/shared` consumers and used directly (`supabase.from("table")...`).
- **RLS** — bypassed by the API using the service-role key when needed (see `CLAUDE.md` notes); browser clients use the anon key plus the user's JWT and rely on RLS policies.
- **Direct Postgres** — `SUPABASE_CONNECTION_STRING` (with base64-encoded `SUPABASE_CA_CERT`) is used by the Stripe sync engine `poolConfig` in `apps/api/src/middlewares/stripeSync.ts`.
- **Schema, migrations, snippets, tests** — `supabase/config.toml`, `supabase/migrations/`, `supabase/snippets/`, `supabase/tests/`.
- **Type codegen** — regenerate `packages/shared-types/src/database.types.ts` with:
  ```bash
  npx supabase gen types typescript > packages/shared-types/src/database.types.ts
  ```
  These types are then refined into domain/API types inside `@xtablo/shared-types` (`tablos.types.ts`, `tablo-data.types.ts`, `events.types.ts`, `stripe.types.ts`, `kanban.types.ts`).

## Authentication

### Supabase Auth (primary users)
- JWT-based, with sessions managed by `SessionContext` (`packages/shared/src/contexts/SessionContext.tsx`) listening to `supabase.auth.onAuthStateChange()`.
- API extracts and validates the bearer token via `authenticateFromHeader` in `apps/api/src/helpers/auth.ts`, wired in `authMiddleware` (`apps/api/src/middlewares/middleware.ts`).
- `maybeAuthenticatedMiddleware` (same file) sets `c.var.user = null` when the header is missing — used by booking and other "maybe authed" endpoints in `apps/api/src/routers/maybeAuthRouter.ts`.
- Passwordless temporary accounts (`is_temporary: true`) created via the public booking flow.
- Front-end auth UI lives in `@xtablo/auth-ui`.

### Admin auth (internal)
- Custom JWTs signed with `ADMIN_TOKEN_SIGNING_SECRET` (Google Secret Manager).
- Helpers in `apps/api/src/helpers/adminTokens.ts`, verified by `adminAuthMiddleware` (Bearer scheme) in `apps/api/src/middlewares/middleware.ts`.
- Routes: `apps/api/src/routers/admin.ts`, `adminActions.ts`, `adminAuth.ts`, `adminDatasets.ts`, `adminOverview.ts`, `adminTables.ts`.

### Client portal auth (read-only client users)
- Magic-link → cookie-based session JWT, stored in cookie `xtablo_client_session` on domain `clients.xtablo.com`.
- Issuance: `apps/api/src/helpers/clientMagicLinks.ts`, `apps/api/src/helpers/clientSessions.ts`.
- Middlewares `clientAuthMiddleware` / `maybeClientAuthMiddleware` (`apps/api/src/middlewares/middleware.ts`) verify cookies using `CLIENT_AUTH_JWT_SECRET`.
- Routers: `apps/api/src/routers/clientAuth.ts`, `clientInvites.ts`, `clientPortal.ts`.

### Task auth (cron / job runners)
- HTTP Basic with `TASKS_SECRET`, enforced by `basicAuthMiddleware` for `apps/api/src/routers/tasks.ts`.

### Chat worker auth
- Independent JWT verification using `jose` against `JWT_SECRET` Wrangler secret. WebSocket connections receive the token via `?token=` query string; REST via Authorization header (`apps/chat-worker/src/index.ts`, `apps/chat-worker/src/lib/auth.ts`).

## Payments — Stripe

- **Server SDK** — `stripe ^20.0.0` instantiated per-request inside `stripeMiddleware` with `apiVersion: "2025-11-17.clover"` (`apps/api/src/middlewares/middleware.ts`).
- **Browser SDK** — `@stripe/stripe-js` in `apps/main`.
- **Webhook router** — `apps/api/src/routers/stripe.ts`, mounted at `/api/v1/stripe-webhook` in `apps/api/src/routers/index.ts`. Plan tiers (`solo`, `team`, `founder`) keyed off `STRIPE_*_PRICE_ID` env vars.
- **Sync engine** — `@supabase/stripe-sync-engine` writes Stripe objects directly into the `stripe` schema of Supabase via a direct Postgres pool (`apps/api/src/middlewares/stripeSync.ts`). Refetches `subscription` and `customer` objects to avoid stale state.
- **Secrets** — `stripe-secret-key`, `stripe-webhook-secret` (prod) and `…-staging` variants pulled from Google Secret Manager (`apps/api/src/secrets.ts`).
- **Billing helpers** — `apps/api/src/helpers/billing.ts`. Active-plan enforcement via `activePlanAccessMiddleware`; only the org's billing owner can manage billing.
- **Docs** — `docs/STRIPE_ARCHITECTURE.md`, `docs/STRIPE_README.md`, `docs/STRIPE_WITH_SYNC_ENGINE.md`, `docs/STRIPE_INTEGRATION_COMPLETE.md` plus several setup/testing guides.

## Mobile Payments — RevenueCat

- Webhook router `apps/api/src/routers/revenuecat.ts`, mounted at `/api/v1/revenuecat-webhook`.
- Maps Apple in-app product IDs to plans via `apps/api/src/helpers/appleBilling.ts`.
- Env: `REVENUECAT_WEBHOOK_AUTH_HEADER`, `REVENUECAT_SOLO_PRODUCT_ID`, `REVENUECAT_ANNUAL_PRODUCT_ID`.

## Storage — Cloudflare R2 (S3 SDK)

- `@aws-sdk/client-s3` pointed at `https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com` with `region: "auto"`.
- Instantiated per-request in `r2Middleware` (`apps/api/src/middlewares/middleware.ts`) and exposed as `c.get("s3_client")`.
- Credentials: `R2_ACCESS_KEY_ID` / `R2_SECRET_ACCESS_KEY` from Secret Manager.
- Upload pipelines use `multer` + `sharp` for image processing (declared in `apps/api/package.json`).

## Email — Gmail OAuth2 (Nodemailer)

- SMTP through `smtp.gmail.com:465` using Google OAuth2 tokens.
- Built in `apps/api/src/middlewares/transporter.ts` via `nodemailer.createTransport` with `EMAIL_USER`, `EMAIL_CLIENT_ID`, `EMAIL_CLIENT_SECRET`, `EMAIL_REFRESH_TOKEN`.
- Exposed as `c.get("transporter")` everywhere `transporterMiddleware` is applied (mounted globally in `apps/api/src/routers/index.ts`).

## Chat — Custom Cloudflare Durable Objects

The CLAUDE.md mentions Stream Chat historically, but the current implementation is a **custom WebSocket chat** running on Cloudflare Workers + Durable Objects (no `stream-chat` dependency found anywhere in the repo).

- Worker entry: `apps/chat-worker/src/index.ts` (Hono).
- Durable Object class: `apps/chat-worker/src/durable-objects/ChatRoom.ts`. Declared in `apps/chat-worker/wrangler.toml`:
  ```toml
  [durable_objects]
  bindings = [{ name = "CHAT_ROOM", class_name = "ChatRoom" }]
  [[migrations]]
  tag = "v1"
  new_sqlite_classes = ["ChatRoom"]
  ```
- Membership enforced by querying Supabase via PostgREST (`apps/chat-worker/src/lib/supabase.ts`).
- UI consumed via `@xtablo/chat-ui` (used by `apps/main`, `apps/clients`, `@xtablo/tablo-views`).
- Custom domain: `chat.xtablo.com`.

## Observability

### Frontend — Datadog RUM
- `apps/main/src/lib/rum.ts` initialises `@datadog/browser-rum` with `applicationId: "8e268e1a-1be0-44c6-b12a-978530d497c7"`, `service: "xtablo-ui"`, `sessionSampleRate: 100`, `sessionReplaySampleRate: 80`, `defaultPrivacyLevel: "mask-user-input"`, and the React plugin with router instrumentation.
- `apps/clients/src/lib/rum.ts` does the same for the clients app.
- User is set in `apps/main/src/providers/UserStoreProvider.tsx` (`datadogRum.setUser` on login, `clearUser` on logout).
- Manual view names via `apps/main/src/hooks/useDatadogRumViewName.tsx`.

### Backend — dd-trace
- `tracer.init({ logInjection: true })` is the very first call in `apps/api/src/index.ts`.
- Datadog CI tooling (`@datadog/datadog-ci`, `@datadog/datadog-ci-plugin-cloud-run`) is wired for Cloud Run instrumentation uploads.
- Static analysis is configured at the repo root in `static-analysis.datadog.yml`.

## Secrets Management — Google Secret Manager

- `apps/api/src/secrets.ts` fetches every sensitive value from `projects/xtablo/secrets/{name}/versions/latest` using `@google-cloud/secret-manager`.
- Secrets loaded: `supabase-service-role-key`, `supabase-connection-string`, `supabase-ca-cert`, `admin-token-signing-secret`, `client-auth-jwt-secret`, `email-client-secret`, `email-refresh-token`, `r2-access-key-id`, `r2-secret-access-key`, `stripe-secret-key`, `stripe-webhook-secret`, plus their `…-staging` variants.
- Setup guide: `docs/GOOGLE_SECRET_MANAGER_SETUP.md`.

## Deployment Platforms

### Cloudflare Workers (web apps + chat)
- All Vite-built frontends are deployed to Workers via Wrangler (see `apps/*/wrangler.toml`).
- `apps/main/wrangler.toml` binds `app.xtablo.com` (prod) and `app-staging.xtablo.com` (staging), uses `not_found_handling = "single-page-application"`.
- `apps/external/wrangler.toml`, `apps/admin/wrangler.toml`, `apps/clients/wrangler.toml` each have their own custom domain.
- `apps/chat-worker/wrangler.toml` declares the `ChatRoom` Durable Object SQLite class and binds `chat.xtablo.com`.
- Worker scripts include observability blocks (`[observability] enabled = true`).

### Google Cloud Run (API)
- Containerised by `apps/api/Dockerfile` (multi-stage Node 20 Alpine, pnpm-driven).
- CI/CD via Google Cloud Build using `apps/api/cloudbuild.yaml`.
- Runtime configuration uses Cloud Run env vars + Secret Manager (no env files inside the image).
- Deployment guides: `docs/CLOUD_BUILD_SETUP.md`, `docs/CLOUD_BUILD_ENV_CONFIG.md`, `docs/DOCKER_BUILD.md`, `docs/DOCKER_PNPM_OPTIMIZATION.md`, `docs/DOCKER_BUILD_PERFORMANCE.md`.

### Go backend (separate)
- `go-backend/` deploys via its own `compose.yaml` / `justfile` and is independent of the TypeScript build pipeline (uses `chi`, `templ`, `sqlc`, `pgx`).

### Infra utilities
- `infra/docker-compose.yaml`, `infra/docker-compose.traefik.yaml`, `infra/Dockerfile` — generic infra/traefik setup. `infra/app/main.py` is a small Python helper.

## Other Integrations / Tooling

- **Google APIs** (`googleapis ^161.0.0`) — currently used to mint OAuth2 access tokens for the Gmail SMTP transport (`apps/api/src/middlewares/transporter.ts`).
- **PWA** — `vite-plugin-pwa` in `apps/main` generates the service worker; `workbox-window` registers it.
- **Sourcemaps to Datadog** — `@datadog/datadog-ci` is available at the workspace root for `datadog-ci sourcemaps upload`.
- **Chromatic** — `chromatic ^11.5.0` listed in `apps/main` dev deps for visual regression (no CI workflow inspected here).
