# 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)
-
Create a project at supabase.com/dashboard (pick a region close to your users). Note the project ref from the URL (e.g.
abcdefghijklmnop). -
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 -
What that migration creates:
notificationstable (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.
-
From Project Settings → API, copy:
- Project URL →
SUPABASE_URL(e.g.https://abcdefghijklmnop.supabase.co) service_rolekey → Worker secretSUPABASE_SERVICE_ROLE_KEY
- Project URL →
Warning: Never put the service role key in browser code,
wrangler.tomlvars, 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.
-
Issuer (this is
AUTH_ISSUER):https://cognito-idp.<region>.amazonaws.com/<userPoolId>Multiple pools: comma-separate them.
-
Resource server (App integration → Resource servers → Create):
- Identifier:
notifications - Custom scope:
publish→ full scope stringnotifications/publish
- Identifier:
-
One confidential app client per producer (e.g. Assess):
- Client credentials flow enabled
- Allow
notifications/publish - That client id becomes the notification
sourceon every publish - Copy Client ID + Client secret from the app client page
-
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, notcurl— PowerShell aliasescurltoInvoke-WebRequest. Bash: cognito-setup.md.) -
End users: nothing extra — any valid pool token can read its own inbox.
-
Optional human publishers only: create Cognito group → add admins → set
PUBLISHER_GROUPSto that group name. Skip if you only have M2M publishers.
# Step 3 — Deploy the Cloudflare Worker
-
Edit
packages/api/wrangler.toml— replace placeholders in[env.staging.vars]and[env.production.vars]:Variable Example Notes AUTH_ISSUERCognito issuer from step 2 Comma-list for multiple pools PUBLISHER_SCOPESnotifications/publishM2M publishers (resource server scope — not a user group) PUBLISHER_GROUPS(omit unless needed) Optional human publishers only — requires creating that Cognito group DB_DRIVERsupabaseUse memoryonly for local-onlySUPABASE_URLProject URL from step 1 Per environment RETENTION_DAYS900disables purge cronV1_PAYLOAD_TEMPLATESJSON map Only if legacy CreatePayloadcallers exist -
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 -
Production is intended to sit on
notifications.totumcloud.com(custom domain inwrangler.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
GET https://<host>/health→{ "status": "ok" }(proves Worker + DB)- Open
/docs— authorize with a user token,GET /v1/notifications - With an M2M token,
POST /v1/notificationswith a knownuserGuid+dedupeKey - With that user’s token, confirm the item appears
- 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
- Set
AUTH_ISSUERin.dev.varsto your Cognito pool issuer; keepDB_DRIVER=memory. Restartpnpm dev. - Get an M2M token (
notifications/publish) and a user access token; copy the user’ssubfrom 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.
- 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`"}"
- 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 (
/selfor 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.