# 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). Laptop-only: Local developer testing. Exhaustive Cloudflare / CI checklist: setup.md.
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 a publisher API key or M2M client if you need to publish |
| New environment (staging / another tenant) | Follow From scratch below (or the full setup.md) |
| Local exploration only | Local developer testing — API keys first, then optional M2M |
# 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, git, 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.
If resource servers / M2M / client secrets are new: Cognito explained.
Production safety / checklists / rollback: Cognito M2M reference.
Full console how-to: cognito-setup.md.
Publisher config (important):
| Worker env | Purpose | Create a Cognito user group? |
|---|---|---|
PUBLISHER_API_KEYS=name:secret,… |
Easiest backend publish via X-Api-Key — api-keys.md |
No |
PUBLISHER_SCOPES=notifications/publish |
Lets M2M / backend Cognito tokens publish | No — create a resource server scope instead |
PUBLISHER_GROUPS=… |
Lets human user tokens publish | Only if you want that — otherwise omit |
Use API keys for a quick bootstrap, or PUBLISHER_SCOPES for Cognito M2M. 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
Preferred: Cloudflare dashboard for Worker + Variables & Secrets + domain + cron, then connect Git (Workers Builds) for code. Full tables: setup.md §3.2 (Build / Deploy commands are spelled out there).
Summary:
- Workers & Pages → Create Worker twice — names
inovus-notifications-stagingandinovus-notifications. - On each Worker → Settings → Variables and Secrets, add plaintext Variables (
AUTH_ISSUER,SUPABASE_URL,DB_DRIVER=supabase,PUBLISHER_SCOPES, …) and Secrets (SUPABASE_SERVICE_ROLE_KEY, optionalPUBLISHER_API_KEYS). - Production → Settings → Domains & Routes → Custom Domain →
notifications.totumcloud.com. - Each Worker → Triggers → Cron Triggers →
0 3 * * *(retention purge). - Settings → Builds (connect this repo). Root directory = repo root. Build command = leave empty. Deploy command:
- Staging:
pnpm --filter @inovus-medical/notifications-api deploy:staging - Production:
pnpm --filter @inovus-medical/notifications-api deploy:production
- Staging:
Laptop one-shot: from packages/api, pnpm deploy:staging / pnpm deploy:production after wrangler login.
CLI-only standup: setup.md §3.4.
# Step 4 — Point applications at the API
new NotificationsClient({
baseUrl: 'https://notifications.totumcloud.com', // or your staging URL
getAccessToken: () => auth.getValidAccessToken(),
});
Publishers use an API key or M2M token getter instead — see Publishing and api-keys.md.
# Step 5 — Smoke test
GET https://<host>/health→{ "status": "ok" }(proves Worker + DB)- Open
/docs— authorize with a user token,GET /v2/notifications - With an M2M token,
POST /v2/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. Plain-language flow (what a changeset is): releasing.md. Secrets and Environments: setup.md §4.
# Local development (no cloud required)
Full walkthrough: Local developer testing (boot → API-key publish → user inbox → optional M2M).
Short version:
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
Set AUTH_ISSUER, DB_DRIVER=memory, and PUBLISHER_API_KEYS=local:<long-random-secret> in .dev.vars, restart, then publish with X-Api-Key and list with a user Bearer token. Details and PowerShell examples are in the local guide.
# 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 or API keys
- [ ] Cross-user publish only from trusted backends (M2M or named API keys)
- [ ] Self-publish is fine in the browser (
/selfor convenience API) - [ ] Producers always send a meaningful
dedupeKey
# More detail
| Topic | Doc |
|---|---|
| Local laptop dry run | local-development.md |
| Full ops + CI secrets table | setup.md |
| Cognito “why” (resource servers, M2M) | cognito-explained.md |
| Publisher API keys (easy publish) | api-keys.md |
| Cognito M2M production reference | authentication/cognito-m2m-notifications.md |
| Cognito how-to | cognito-setup.md |
| Schema / SQL | 0001_notifications.sql |
| Memory vs Supabase | adapters.md |
| New DB backends | adapters.md |
| Repo commands & invariants | Developer reference |
Next: Everyday usage.