Infrastructure
The edge service is deployed across two platforms: Cloudflare Workers (compute and routing) and Auth0 (identity provider actions and tenant policy). Cloudflare Workers deployments and route bindings are managed via wrangler.toml and wrangler publish. Supporting infrastructure — DNS records, Auth0 actions, and the GrowthBook proxy — is managed by Terraform from the deploy/ directory. Worker binaries are built with webpack and published via wrangler.
The Terraform state backend is S3 at key terraform/edge/terraform.tfstate.
Deployment
The deploy workflow (.github/workflows/deploy.yaml) authenticates to AWS with GitHub OIDC; there are no static credentials for Terraform. Pull requests assume the read-only site-github-services-plan role and run terraform plan; pushes to main (test) and published releases (prod) assume site-github-services-deploy and run terraform apply. Both roles are owned by the infrastructure repo. AWS is used only for the state backend; the Cloudflare and Auth0 providers use their own API tokens. The FLOATME_AWS_* secrets uploaded to the Workers are the Workers' runtime identity, not the deploy credential, and are unaffected.
Provider versions in use:
-
cloudflare/cloudflare4.52.5 -
auth0/auth01.33.0 -
hashicorp/aws6.19.0
Environments
Edge supports three named environments controlled by the FLOATME_ENVIRONMENT variable (default: test): test deploys from main, staging deploys from main into the staging AWS account and Auth0 tenants, and prod deploys on release. Every resource is namespaced by environment.
Worker environment split
Each wrangler.toml defines [env.prod], [env.test] and [env.staging] stanzas. The top-level name/route keys are local-dev defaults only. Wrangler is always invoked with --env $FLOATME_ENVIRONMENT so the correct name and hostname are selected at publish time.
Prod and test use routes on hostnames whose DNS records were made by hand; staging uses wrangler custom domains, so Cloudflare creates the record and certificate with the worker.
| Worker | prod route | test route | staging custom domain |
|---|---|---|---|
auth |
|
|
|
backoffice-auth |
|
|
|
sms |
|
|
|
links |
|
|
|
Auth0 tenant split
The auth and backoffice-auth workers point to different Auth0 tenants per environment. This is a hard split — each environment uses entirely separate tenants with separate client IDs, management credentials, and MFA audiences.
| Worker | prod tenant | test tenant | staging tenant |
|---|---|---|---|
auth |
|
|
|
backoffice-auth |
|
|
|
Castle blocking (ENABLE_CASTLE_BLOCKING) is set to "true" in prod and "false" elsewhere, so fraud signals are enforced only in production. Code that bypasses checks for QA integration users keys on isTestLike(env) (cloudflare/helpers/environment.ts), which is true for test and staging.
Environment → Worker Deployed Name → Cloudflare hostname ──────────────────────────────────────────────────────────────── prod auth prod-auth auth.floatme.io/* prod backoffice-auth prod-backoffice-auth auth.backoffice.floatme.io/* prod sms prod-sms sms.floatme.io/* prod links prod-links links.floatme.io/* test auth test-auth auth.test.floatme.io/* test backoffice-auth test-backoffice-auth auth.backoffice.test.floatme.io/* test sms test-sms sms.test.floatme.io/* test links test-links links.test.floatme.io/* staging auth staging-auth auth.staging.floatme.io staging backoffice-auth staging-backoffice-auth auth.backoffice.staging.floatme.io staging sms staging-sms sms.staging.floatme.io staging links staging-links links.staging.floatme.io
Cloudflare Workers
Workers are TypeScript source files. Two workers (auth, sms) are compiled by webpack into ES module bundles under dist/cloudflare/workers/<name>/index.mjs. The backoffice-auth and links workers are published directly from source via wrangler without a webpack build step. Each worker has its own wrangler.toml in cloudflare/workers/<name>/.
| Worker | Source | Wrangler config | Purpose |
|---|---|---|---|
auth |
|
|
Auth0 proxy for the FloatMe mobile app. Handles login, signup, MFA, social auth, password reset, session tokens, and Castle fraud checks. |
backoffice-auth |
|
|
Auth0 proxy for the internal backoffice admin console. Uses a dedicated Auth0 tenant. |
sms |
|
|
"Text me the app" (TMTA) worker — sends an SMS with the app download link via Twilio. |
links |
|
|
Deeplink rewriter ( |
Worker environment variables
Non-secret configuration is declared inline in wrangler.toml under [env.<name>.vars]. Secrets are uploaded separately via wrangler secret put (see Secrets Management).
| Variable | prod value | test value |
|---|---|---|
|
|
|
|
||
|
|
|
|
||
|
||
|
|
|
|
|
|
|
|
|
Staging additionally sets AUTH_BASE_URL/LINKS_BASE_URL (the worker’s own hostnames, replacing hard-coded test values), FLOATME_QA_SERVICE_REGION, and the AWS identity pair FLOATME_AWS_ROLE_ARN and AUTH0_AWS_AUDIENCE. When FLOATME_AWS_ROLE_ARN is set, cloudflare/helpers/aws-credentials.ts signs with temporary credentials from STS (an Auth0 client-credentials token for that audience, exchanged with AssumeRoleWithWebIdentity, cached for the hour-long session) instead of the static FLOATME_AWS_* key. See AWS Identity.
| Variable | prod value | test value |
|---|---|---|
|
||
|
|
|
|
||
|
|
|
| Variable | Value |
|---|---|
|
|
|
|
|
|
The links worker has no non-secret vars; it uses only WORKER_ENV.
DNS
DNS records are managed in Terraform, all within the floatme.io Cloudflare zone (zone ID supplied via var.cloudflare_zone_id). All records are proxied through Cloudflare.
| Resource (Terraform) | Type | Name (prod) | Name (test) | Purpose |
|---|---|---|---|---|
|
AAAA |
|
|
Discard record ( |
|
CNAME |
|
|
CNAME to |
The auth, sms and links hostnames in test and prod are hand-made records in the Cloudflare dashboard, with the worker routes in each wrangler.toml registered on top when wrangler publishes. Staging uses wrangler custom domains instead, so Cloudflare creates and removes those records with the worker.
GrowthBook Proxy
deploy/growthbook_proxy.tf provisions a Cloudflare DNS CNAME record that points growthbook.floatme.io (prod) or test-growthbook.floatme.io (test) at cdn.growthbook.io. The record is proxied, meaning all GrowthBook SDK requests from FloatMe clients flow through Cloudflare rather than directly to GrowthBook’s CDN.
This provides two benefits:
-
Cloudflare caches the GrowthBook feature flag payload at the edge, reducing latency for SDK initialization.
-
FloatMe controls the DNS name, so the GrowthBook CDN URL can be changed without updating client builds.
Auth0 Actions
Auth0 Actions are deployed via Terraform (deploy/auth0_actions.tf). All actions run Node.js 18. They are automatically deployed (deploy = true) when Terraform applies.
| Terraform resource | Action name (env-prefixed) | Trigger | Source file |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
The post-login trigger pipeline (auth0_trigger_actions.login_flow) runs ip-metadata first, then mfa-custom-claim, on every post-login event.
The auth0/rules/ directory contains legacy Auth0 rules that have been superseded by Actions and are not deployed or documented.
|
Secrets Management
Secrets are never stored in wrangler.toml. They are uploaded directly to the Cloudflare Workers secret store using wrangler secret put --env $FLOATME_ENVIRONMENT. Each worker has a secrets.sh script in its source directory.
Run all secrets in one pass:
FLOATME_ENVIRONMENT=prod make secrets.host
Or per-worker:
FLOATME_ENVIRONMENT=prod make secrets.auth.host
FLOATME_ENVIRONMENT=prod make secrets.backoffice-auth.host
FLOATME_ENVIRONMENT=prod make secrets.sms.host
FLOATME_ENVIRONMENT=prod make secrets.links.host
| Worker | Secret name (in Cloudflare) | Source env var |
|---|---|---|
auth |
|
|
auth |
|
|
auth |
|
|
auth |
|
|
auth |
|
|
auth |
|
|
auth |
|
|
auth |
|
|
auth |
|
|
auth |
|
|
auth |
|
|
auth |
|
|
auth |
|
|
backoffice-auth |
|
|
backoffice-auth |
|
|
sms |
|
|
sms |
|
|
links |
|
|
The AWS credentials uploaded to the auth worker (FLOATME_AWS_ACCESS_KEY_ID / FLOATME_AWS_SECRET_ACCESS_KEY) are scoped to the IAM user named by var.cloudflare_workers_iam_user_name and are used to sign SigV4 requests to the user-service API Gateway.
Build and Publish Flow
Building workers
make build # runs webpack inside the Docker devkit container
make build.host # runs webpack directly on the host (requires Node + webpack)
Both targets invoke webpack and output compiled bundles to dist/cloudflare/workers/<name>/index.js. A final edge.tar.gz is created in dist/ containing all built artifacts.
The webpack config compiles all four workers in a single pass. The build is a prerequisite of the tarball target.
Publishing workers
After building, publish each worker with wrangler:
FLOATME_ENVIRONMENT=prod \
wrangler publish dist/cloudflare/workers/auth/index.js \
--env prod \
-c cloudflare/workers/auth/wrangler.toml
Repeat for each worker. The --env flag selects which wrangler.toml stanza ([env.prod] or [env.test]) to use for the worker name and route pattern.
For local development, wrangler dev can be started per-worker without building:
make auth.local # wrangler dev --env test (auth worker)
make backoffice-auth.local # wrangler dev --env test (backoffice-auth worker)
make sms.local # wrangler dev (no env flag — uses top-level defaults)
make links.local # wrangler dev (no env flag — uses top-level defaults)
Terraform apply flow
All Terraform is in deploy/. The deploy/Makefile exposes three targets driven by the FLOATME_ENVIRONMENT variable:
cd deploy
FLOATME_ENVIRONMENT=prod make init # terraform init
FLOATME_ENVIRONMENT=prod make plan # terraform plan
FLOATME_ENVIRONMENT=prod make apply # terraform apply
The root takes two inputs. FLOATME_ENVIRONMENT is mapped to TF_VAR_environment and CLOUDFLARE_ZONE_ID to TF_VAR_cloudflare_zone_id by the Makefile. Everything else the providers need arrives as their own environment variables: CLOUDFLARE_API_TOKEN, the AUTH0_DOMAIN/AUTH0_CLIENT_ID/AUTH0_CLIENT_SECRET trio for the Auth0 provider, and AWS credentials for the state backend.
The root Makefile also exposes make tf WHAT=<subcommand> which delegates to deploy/Makefile.
Terraform Structure
| File | Contents |
|---|---|
|
Provider configuration for Cloudflare, Auth0, and AWS. S3 backend definition. |
|
The two inputs: environment name and the Cloudflare zone ID. |
|
Three |
|
|
|
|
Related Pages
-
Architecture — System context diagram, external service integrations, and secrets loading pattern
-
Auth Worker — Handler reference and Castle integration details
-
Backoffice Auth Worker — Admin console auth flow
-
Auth0 Actions — Action logic and post-login pipeline ordering