Connecting to Cloudflare¶
End-to-end setup for the three Cloudflare pieces: the backend (Worker + D1), the engineer portal (Pages), and the docs site (Pages). Do them in this order.
0. Prerequisites¶
- A Cloudflare account (note your Account ID from the dashboard home).
- Node 18+ and Wrangler:
npm i -g wranglerthenwrangler login. - An Entra (Azure AD) tenant you can register an app in (for SSO). 29eeab3ba80ed3d7152cb919f84e9225
1. Backend — Worker + D1¶
From backend/:
cd backend
npm install
# Create the database; copy the printed database_id into wrangler.toml.
wrangler d1 create totlprovision
# Apply the schema to the remote DB.
npm run db:init:remote
Edit backend/wrangler.toml:
- set
account_id, - paste the
database_idfrom the create step, - set
ACCESS_TEAM_DOMAIN = "totlcom.cloudflareaccess.com"(your Zero Trust team domain), - leave
ACCESS_AUDblank for now — you fill it in step 2.
Deploy:
Note the Worker URL it prints (e.g. https://totlprovision-api.<sub>.workers.dev) — the portal and
engine point at it.
2. Entra SSO + Cloudflare Access¶
This is what protects the portal and lets the backend verify engineer identity.
Five parts, in order: (A) give the Worker a domain, (B) register an app in Microsoft Entra, (C) connect Entra to Cloudflare, (D) create the Access application, (E) copy the AUD to the Worker.
A. Give the Worker a custom domain¶
A workers.dev host can't be protected by Access, so the API needs a domain on your zone.
- Cloudflare dashboard → Workers & Pages → click the totlprovision-api worker.
- Settings tab → Domains & Routes → Add → Custom Domain.
- Enter
totlprovision-api.totlcom.com→ Add domain. Wait until it shows Active (~1 min).
B. Register an app in Microsoft Entra¶
- Go to https://portal.azure.com → search Microsoft Entra ID → open it.
- Left menu → App registrations → New registration.
- Name:
Cloudflare Access. Supported account types: "Accounts in this organizational directory only". Redirect URI: platform Web, value:https://totlcom.cloudflareaccess.com/cdn-cgi/access/callback - Register.
- On the app's Overview, copy Application (client) ID and Directory (tenant) ID (save both).
- Left menu → Certificates & secrets → New client secret → description + expiry → Add. Copy the secret Value immediately (it's hidden after you leave the page).
- Left menu → API permissions → Add a permission → Microsoft Graph → Delegated → add
openid,email,profile(andUser.Read). → Add permissions → Grant admin consent.
C. Connect Entra to Cloudflare Zero Trust¶
- Go to https://one.dash.cloudflare.com (Zero Trust) → Settings → Authentication.
- Under Login methods → Add new → Azure AD (Microsoft Entra ID).
- Paste App ID (client ID), Client secret (the Value), and Directory (tenant) ID.
- Save, then click Test on the method and sign in to confirm it works.
D. Create the Access application (covers BOTH hostnames)¶
- Zero Trust → Access → Applications → Add an application → Self-hosted.
- Application name:
TotlProvision. - Under Application domain, add the portal hostname
totlprovision.totlcom.com, then click Add hostname and addtotlprovision-api.totlcom.com. Both must be on this one application — that's what lets the login cookie work across the two (wildcards won't). - Identity providers: select your Entra method (turn off One-time PIN if you only want SSO). Next.
- Add a policy: Name
Allow Totlcom, Action Allow, Include → Emails ending in →@totlcom.com(or pick a specific Entra group/emails). Next. - CORS settings (in the app's settings): set Access-Control-Allow-Origins to
https://totlprovision.totlcom.com, allow methodsGET, POST, OPTIONS, and turn on Allow credentials. Save the application.
E. Copy the AUD to the Worker¶
- Zero Trust → Access → Applications → open TotlProvision → copy the Application Audience (AUD) Tag (a long hex string).
- Set it on the backend:
Done. The backend now verifies each engineer's Cf-Access-Jwt-Assertion against your team JWKS and the
AUD — no OAuth code to maintain.
3. Portal — Cloudflare Pages¶
Point the portal at your Worker, then deploy and assign its domain.
- The API origin is set in
portal/index.htmlto the API custom domain: - Deploy from the repo root (not from
backend/orportal/, or the path resolves wrong): - Assign the custom domain
totlprovision.totlcom.comto the Pages project (Pages → the project → Custom domains). It's protected by the combined Access application from step 2.
First run inside the portal: an admin clicks "First-time setup", enters a strong passphrase, and a tenant keypair is generated in the browser. Record that passphrase in your break-glass scheme — it cannot be recovered from the server (see Security).
4. Docs site — Cloudflare Pages (auto-deploy on push)¶
A separate Pages project, connected to Git so it rebuilds on every push. Full steps in Deploying these docs. Short version:
- Workers & Pages → Create → Pages → Connect to Git → this repo.
- Build command:
pip install -r requirements.txt && mkdocs build - Output directory:
site· Root directory:/· envPYTHON_VERSION=3.12. - Protect the hostname with Access (these docs contain internal architecture).
5. Issue a machine API token (per tenant)¶
The engine authenticates ingest with a per-tenant Bearer token. Only a salted hash is stored, so a DB leak can't replay it. Generate a token, insert its hash, and put the plaintext in the engine config (kept off source/USB):
# Example token + hash (Node). prefix = first 8 chars.
node -e '
const c=require("crypto");
const token="tok_"+c.randomBytes(24).toString("base64url");
const salt=c.randomBytes(8).toString("hex");
const hash=c.createHash("sha256").update(salt+":"+token).digest("hex");
console.log({token, prefix:token.slice(0,8), salt, hash});
'
Then insert the row (replace values):
cd backend
wrangler d1 execute totlprovision --remote --command \
"INSERT INTO tenants (id,name) VALUES ('11111111-1111-1111-1111-111111111111','Acme MSP');
INSERT INTO users (id,tenant_id,email,role) VALUES ('u1','11111111-1111-1111-1111-111111111111','you@yourmsp.com','admin');
INSERT INTO api_tokens (id,tenant_id,token_prefix,token_hash,token_salt,label)
VALUES ('t1','11111111-1111-1111-1111-111111111111','<prefix>','<hash>','<salt>','engine');"
Hand the plaintext token to the engine (it will use it for /v1/report and /v1/secret). A GUI
helper for tenant/token creation lands in a later phase.
Verify it's wired¶
# Worker is up
curl https://totlprovision-api.totlcom-inc.workers.dev/health # -> {"ok":true,...}
# Token works (after step 5) — should 409 until a tenant key exists, which is expected
curl -H "Authorization: Bearer <plaintext-token>" \
https://totlprovision-api.totlcom-inc.workers.dev/v1/tenant/pubkey
Then sign in to the portal (Access prompts Entra SSO), run admin onboarding to create the tenant key,
and the /v1/tenant/pubkey call above will start returning the public key the engine encrypts to.