# Setup

This page walks you through standing up the service from scratch, including the database. Use it when you’re creating a new environment (or learning how the pieces fit). The exhaustive checklist with CI secrets also lives in setup.md — same story, more ops detail.

flowchart TD
  A["1. Database (Supabase)"] --> B["2. Auth (Cognito scopes)"]
  B --> C["3. Worker (Cloudflare)"]
  C --> D["4. Point apps at the base URL"]
  D --> E["5. Optional: CI release pipeline"]

# Do you need to set this up?

Situation What to do
Integrating with the shared Inovus production service Skip deploy — use https://notifications.totumcloud.com and ask for an M2M publisher client if you need to publish
New environment (staging / another tenant) Follow From scratch below (or the full setup.md)
Local exploration only Local development (memory store)

# From scratch (including the database)

You’ll end up with: a Postgres table in Supabase, Cognito publisher credentials, a Cloudflare Worker API, and apps pointed at that API.

Prerequisites: Node ≥ 20, pnpm ≥ 9, Cloudflare account, Supabase org, admin on your Cognito user pool.

# Step 1 — Create the database (Supabase)

  1. Create a project at supabase.com/dashboard (pick a region close to your users). Note the project ref from the URL (e.g. abcdefghijklmnop).

  2. Apply the schema from supabase/migrations/0001_notifications.sql.

    Option A — SQL editor: open the migration file, paste into the Supabase SQL editor, run it.

    Option B — CLI:

    npx supabase link --project-ref <PROJECT_REF>
    npx supabase db push
  3. What that migration creates:

    • notifications table (title, body, severity, action URL, dedupe key, metadata, read state…)
    • Indexes for inbox listing, unread badge, retention purge
    • Partial unique index for race-safe dedupe on (user_guid, source, dedupe_key)
    • RLS enabled with no policies (deny-all). Only the Worker’s service role should touch this table.
  4. From Project Settings → API, copy:

    • Project URLSUPABASE_URL (e.g. https://abcdefghijklmnop.supabase.co)
    • service_role key → Worker secret SUPABASE_SERVICE_ROLE_KEY

Warning: Never put the service role key in browser code, wrangler.toml vars, or the anon key slot. Do not enable Supabase Auth for this table, and do not add permissive RLS policies — Cognito JWTs can’t drive Supabase RLS; the Worker is the only trusted path.

One Supabase project per environment (staging and production are separate).

# Step 2 — Configure Cognito

Uses your existing user pool(s) — user sign-in does not change. Full console detail: cognito-setup.md.

Publisher config (important):

Worker env Purpose Create a Cognito user group?
PUBLISHER_SCOPES=notifications/publish Lets M2M / backend tokens publish (normal case) No — create a resource server scope instead
PUBLISHER_GROUPS=… Lets human user tokens publish Only if you want that — otherwise omit

Most installs set only PUBLISHER_SCOPES. End users need neither setting to read their inbox.

  1. Issuer (this is AUTH_ISSUER):

    https://cognito-idp.<region>.amazonaws.com/<userPoolId>

    Multiple pools: comma-separate them.

  2. Resource server (App integration → Resource servers → Create):

    • Identifier: notifications
    • Custom scope: publish → full scope string notifications/publish
  3. One confidential app client per producer (e.g. Assess):

    • Client credentials flow enabled
    • Allow notifications/publish
    • That client id becomes the notification source on every publish
    • Copy Client ID + Client secret from the app client page
  4. Fetch an M2M token (cache until expires_in) — PowerShell on Windows:

    $ClientId = "…"       # App client → Client ID
    $ClientSecret = "…"   # App client → Client secret
    $CognitoDomain = "https://<prefix>.auth.<region>.amazoncognito.com"
    
    curl.exe -X POST "$CognitoDomain/oauth2/token" `
      -H "Content-Type: application/x-www-form-urlencoded" `
      -u "${ClientId}:${ClientSecret}" `
      -d "grant_type=client_credentials&scope=notifications/publish"

    (Use curl.exe, not curl — PowerShell aliases curl to Invoke-WebRequest. Bash: cognito-setup.md.)

  5. End users: nothing extra — any valid pool token can read its own inbox.

  6. Optional human publishers only: create Cognito group → add admins → set PUBLISHER_GROUPS to that group name. Skip if you only have M2M publishers.

# Step 3 — Deploy the Cloudflare Worker

  1. Edit packages/api/wrangler.toml — replace placeholders in [env.staging.vars] and [env.production.vars]:

    Variable Example Notes
    AUTH_ISSUER Cognito issuer from step 2 Comma-list for multiple pools
    PUBLISHER_SCOPES notifications/publish M2M publishers (resource server scope — not a user group)
    PUBLISHER_GROUPS (omit unless needed) Optional human publishers only — requires creating that Cognito group
    DB_DRIVER supabase Use memory only for local-only
    SUPABASE_URL Project URL from step 1 Per environment
    RETENTION_DAYS 90 0 disables purge cron
    V1_PAYLOAD_TEMPLATES JSON map Only if legacy CreatePayload callers exist
  2. Put the service role in a secret (not in the toml file):

    cd packages/api
    npx wrangler login
    npx wrangler secret put SUPABASE_SERVICE_ROLE_KEY --env staging
    pnpm deploy:staging
    
    npx wrangler secret put SUPABASE_SERVICE_ROLE_KEY --env production
    pnpm deploy:production
  3. Production is intended to sit on notifications.totumcloud.com (custom domain in wrangler.toml). Staging typically lands on *.workers.dev.

# Step 4 — Point applications at the API

new NotificationsClient({
  baseUrl: 'https://notifications.totumcloud.com', // or your staging URL
  getAccessToken: () => auth.getValidAccessToken(),
});

Publishers use an M2M token getter instead — see Publishing.

# Step 5 — Smoke test

  1. GET https://<host>/health{ "status": "ok" } (proves Worker + DB)
  2. Open /docs — authorize with a user token, GET /v1/notifications
  3. With an M2M token, POST /v1/notifications with a known userGuid + dedupeKey
  4. With that user’s token, confirm the item appears
  5. Publish again with the same key → 200 + "duplicate": true

# Step 6 — Optional CI / releases

GitHub Actions can publish the npm client and deploy staging → production. Secrets, Environments, and the day-to-day changeset flow are documented in setup.md §5.


# Local development (no Supabase required)

git clone <this repo> && cd org.inovus.notifications
pnpm install
cd packages/api
cp .dev.vars.example .dev.vars
pnpm --filter @inovus-medical/notifications-api dev   # http://localhost:8787

Open http://localhost:8787/docs for Swagger UI. GET /health should return {"status":"ok"}.

Default local config uses the memory store (data vanishes on restart). Authenticated send/receive needs a real AUTH_ISSUER plus Cognito tokens — you still do not need Supabase for that dry run.

# Try send / receive locally

  1. Set AUTH_ISSUER in .dev.vars to your Cognito pool issuer; keep DB_DRIVER=memory. Restart pnpm dev.
  2. Get an M2M token (notifications/publish) and a user access token; copy the user’s sub from the JWT.
    • Client ID / Client secret: Cognito → your user pool → App integration → App clients → (confidential client). Domain for the token URL: App integration → Domain. Full clicks: cognito-setup.md.
  3. Publish (PowerShell — use curl.exe):
curl.exe -s -X POST http://localhost:8787/v1/notifications `
  -H "Authorization: Bearer $M2MToken" `
  -H "Content-Type: application/json" `
  -d "{`"userGuid`":`"$UserSub`",`"title`":`"Hello`",`"body`":`"Local test`",`"dedupeKey`":`"local-setup-1`"}"
  1. List as the user:
curl.exe -s http://localhost:8787/v1/notifications `
  -H "Authorization: Bearer $UserToken"

Expect 201 on first publish, then the item in the list. Full curl detail (duplicate retry, mark-read, Swagger Authorize): setup.md §1b.

To exercise against a real Supabase project locally later, set DB_DRIVER=supabase, SUPABASE_URL, and the service role in .dev.vars (never commit secrets).


# Security checklist (please don’t skip)

  • [ ] Migration applied; RLS left deny-all (no extra policies)
  • [ ] Service role key only in Worker secrets — never in the SPA
  • [ ] Browser tokens are user tokens — not publisher M2M secrets
  • [ ] Cross-user publish only from trusted backends
  • [ ] Self-publish is fine in the browser (/self or convenience API)
  • [ ] Producers always send a meaningful dedupeKey

# More detail

Topic Doc
Full ops + CI secrets table setup.md
Cognito deep dive cognito-setup.md
Schema / SQL 0001_notifications.sql
New DB backends adapters.md
Repo commands & invariants Developer reference

Next: Everyday usage.