Environments
Environments
Section titled “Environments”Last updated: 2026-08-23 — ADR-16: all 28 Workers now live on the Finstack account. The 15 non-critical Workers with their own D1 were folded by theme into the 10 existing critical databases (no Workers Paid upgrade needed — see DECISIONS.md); wallet/fanout/fraud-scoring-consumer deployed alongside them. gateway now carries the full 25-of-25 service-binding table. Prior: 2026-07-27 — ADR-9’s leftover Rust infrastructure torn down: the two remaining Fly-fronting CF Worker gateways (finstack-api-gateway, finstack-webhooks-gateway, Galactic account) deleted, closing the ADR-8 rollback path for good; a previously-unknown FGV-account leftover of all three original gateways (including finstack-admin-gateway, still carrying its unpatched unauthenticated-admin exposure) also found and deleted; Neon Postgres (ancient-heart-91326215) exported to a local backup then deleted. Fly.io finstack-api’s status is unresolved — no accessible credential exists to locate or tear it down. See DEPLOYMENT.md/ARCHITECTURE.md/DECISIONS.md for full detail. Prior: every finstack Worker except gateway now has workers_dev = false (closes direct *.workers.dev reachability fleet-wide; svc-auth first, since it had a confirmed live OAuth-gate bypass, then the remaining ~26 after explicit go-ahead — see BUGS.md). gateway keeps workers_dev on deliberately (it’s the intended public entry, and gateway.glcf.workers.dev remains a useful direct-to-gateway URL for ops/testing). Prior 2026-07-26: removed paystream.api.finstack.sh (Workers Custom Domain + managed DNS record deleted via the CF API; TLS handshake had been failing since 2026-07-25, deprioritized then, removed now on user directive since the hostname was non-functional and unneeded) and deleted the finstack-admin-gateway Worker outright (unauthenticated GATEWAY_TOKEN_ADMIN-injecting exposure with no WAF/IP allowlist, confirmed near-zero real traffic — see BUGS.md). Prior: ADR-9: finstack-rs fully deleted; Fly.io and Neon sections below removed/condensed (infra left running untouched but no longer maintained from this repo). finstack is the sole backend — its Worker/secrets inventory replaces the old Rust Fly.io (finstack-api) section. Prior: INC-003 — FGV zone deleted, all CF Workers/Pages/DNS moved to the Galactic account (a6b10be06096b8f9b7c718f29dcd2249, zone 5f2046fc706f3e8e4af2dcc7bab5d167, workers.dev subdomain glcf).
| Env | Purpose | URL | Database |
|---|---|---|---|
| dev | Local development | wrangler dev per Worker | D1 (local) |
| production (CF Workers) | Public gateway (/v1/*, /oauth/*) | https://api.finstack.sh | — |
| production (CF Workers) | Webhooks (/webhooks/stripe etc.) | https://webhooks.finstack.sh | — |
| production (CF Pages) | Dashboard SPA + BFF — passkey login live and working since 2026-07-25 (see CLAUDE.md); reconfirmed end-to-end 2026-08-04 | https://app.finstack.sh | — |
| production (CF Pages) | Developer portal — spec pipeline broken, points at deleted gateway | https://docs.finstack.sh | — |
| production (CF Workers) | Marketing site | https://finstack.sh | — |
No
/admin/*environment. It only ever existed via the now-deletedfinstack-admin-gateway→ Rustapi-admin; nothing replaces it. CUT OVER 2026-08-22 to a dedicated account, on explicit real-time user directive.finstack.sh’s DNS zone and every production hostname now live on the Finstack account (b38d376e1fe34bf09ddab7cfbf34c3f9,finstackworkers.dev subdomain), not Galactic. Update, 2026-08-23 (ADR-16): the 18 Workers deferred below are no longer deferred — all 28 are now live,gatewaycarries the full 25-of-25 service-binding table, and every route that used to return500 misconfigured_gatewaynow reaches its Worker (live-verified:401, auth-gated, not500). This was achieved by consolidating the 15 non-critical Workers’ D1 schemas into the 10 existing critical databases by theme (e.g.fraud’s tables now live insidecompliance-db) rather than by upgrading to Workers Paid — the account is still on the Free plan, still capped at 10 databases, and still sits at exactly 10. See DECISIONS.md’s ADR-16 for the full database-to-Worker mapping and BUGS.md for the 5 rows this closed. The paragraph below is kept for the historical record of what the original cutover scoped.api./webhooks.finstack.shbind to the new account’sgatewayWorker — deliberately reduced to 9 of its 24 service bindings (SVC_AUTH,PAYMENT,PAYOUT,RECIPIENT,COMPLIANCE,PLATFORM,MESSAGING,MERCHANT,ORDER), matching the 10 critical money-moving Workers (svc-auth,payment,order,ledger,compliance,recipient,payout,platform,routing,merchant) that got real production data migrated and are live. 18 non-critical Workers remain deliberately deferred (15 with their own D1 —ai,analytics,billing,composer,customer,database,fraud,inventory,loyalty,payfac,receipt,split,tax,tip,webhooks— plus 3 without one,wallet/fanout/fraud-scoring-consumer, 18 total) — the account is still on Cloudflare’s Free plan (10 D1 cap) — their gateway routes correctly return500 misconfigured_gatewayin production right now, an accepted tradeoff per explicit user direction (“deploy critical now, upgrade later”), not a bug.app.finstack.sh(dashboard) and barefinstack.sh(marketing) were also repointed to the new zone/account during cutover verification (out of the Workers-migration scope proper, but broken by the zone move otherwise) — both confirmed live.docs.finstack.sh(developer portal) remains broken, unchanged from its pre-existing state (see the row above). The old Galactic account’sgateway/D1s/etc. are untouched, not deleted — only theapi./webhooks.finstack.shCustom Domain bindings were detached from them, so a rollback (recreate those bindings + revert the NS change) is mechanically possible if something is found broken. That rollback is not clean, though — it’s data-losing. The new account’s D1s hold a point-in-time snapshot re-synced immediately before cutover; the old Galactic D1s are frozen at that same point. Any payment/order/auth write that lands in the new account after cutover has no counterpart in the old account, so reverting silently discards it. Treat rollback as a last resort, not a safe undo. Full history, every workaround (D1 cap, circular service bindings, the two secret-config bugs found, the Pages/Worker CNAME distinction):docs/superpowers/plans/2026-08-22-dedicated-cf-account-migration.md. Not yet merged tomain— still on branchinfra/dedicated-cf-account-migration.
Brand domain — finstack.sh
Section titled “Brand domain — finstack.sh”finstack.sh registered 2026-06-01 via Spaceship (auto-renew on, privacy high).
Nameservers: annabel.ns.cloudflare.com, jaziel.ns.cloudflare.com (Galactic CF zone 5f2046fc706f3e8e4af2dcc7bab5d167, since INC-003 2026-07-25 — the FGV zone this previously pointed at no longer exists).
DNS: docs CNAME finstack-developer-portal-k7n.pages.dev (proxied) — developer portal. app CNAME finstack-dashboard-8to.pages.dev (proxied) — dashboard.
paystream.fi is a separate project — not FinStack.
Cloudflare Workers (finstack, Finstack account b38d376e1fe34bf09ddab7cfbf34c3f9 — cut over 2026-08-22, see the note above)
Section titled “Cloudflare Workers (finstack, Finstack account b38d376e1fe34bf09ddab7cfbf34c3f9 — cut over 2026-08-22, see the note above)”~30 Workers under finstack/workers/ — see COMPONENTS.md for the full inventory. Internal dispatch is via CF service bindings (declared in each Worker’s wrangler.toml, e.g. gateway’s binding to svc-auth, payment, payout, …), not URL+bearer-token proxying like the deleted Rust gateways used.
gateway— public entry, URL:https://api.finstack.sh/https://webhooks.finstack.sh(custom domains ✓) /https://gateway.glcf.workers.dev. No secrets of its own — dispatches via service binding aftersvc-authvalidates the caller.svc-auth— Secret:INTERNAL_ASSERTION_SECRET(signs/verifies inter-Worker assertions; rotated across ~22 Workers as part of the 2026-07-25 cutover — a fresh source grep during this removal counts ~24 Workers referencing it now, likely growth since the cutover rather than a discrepancy). No/passkeys/*route mounted. No public URL as of 2026-07-27 —workers_dev = false, reachable only viagateway’s service binding or the dashboard’sSVC_AUTHPages service binding (see BUGS.md).payment— Secrets:STRIPE_SECRET_KEY,STRIPE_WEBHOOK_SECRET(webhookwe_1Tx5inAAGi1faYZSdM4OH72S).compliance— Secrets:STRIPE_IDENTITY_SECRET_KEY(same raw key aspayment’sSTRIPE_SECRET_KEY— Identity is a separately-activated product capability on the same Stripe test account, not a separate account/key),STRIPE_IDENTITY_WEBHOOK_SECRET(webhookwe_1TxXsiAAGi1faYZSKKJIVXUf). Service bindings:RECIPIENT,MERCHANT(added 2026-07-26 to close a terminal KYC/KYB decision back torecipients.status/merchant_accounts.status).recipient— Secret:STRIPE_SECRET_KEY. Correction (2026-07-28, ADR-12 Task 8 secrets provisioning): this entry previously claimed the secret was set on 2026-07-28 with the live key (stripe/FINSTACK_LIVE_SECRET_KEY) as part of Task 5 landing —wrangler secret listonrecipientshows that never actually happened; onlyINTERNAL_ASSERTION_SECRETandRECIPIENT_BANK_MASTER_KEYwere set, meaningPOST /recipients/:id/stripe-accountwould have thrown onenv.STRIPE_SECRET_KEYbeing undefined for any real production call since Task 5 shipped. Now genuinely set, using the test-mode key (stripe/FINSTACK_TEST_SECRET_KEY), not the live key — Task 8’s plan explicitly calls for test-mode verification of this brand-new code path, not spending real money. Used byPOST /recipients/:id/stripe-accountto create Custom Connect accounts + persons, distinct fromrouting’s per-tenantprocessor_configs.credentials-sourced key for the same purpose.messaging— Secrets:RESEND_API_KEY,TWILIO_ACCOUNT_SID,TWILIO_AUTH_TOKEN,TWILIO_FROM_NUMBER. Provisioned 2026-07-28 — a new Twilio trial account, credentials set as Worker secrets and vaulted (twilio/namespace).TWILIO_FROM_NUMBERis the trial account’s auto-provisioned number. Trial-tier restrictions (custom SMS text rejected, error 572006; destination numbers must be pre-verified) currently makesvc-auth’sPOST /auth/otp/sendfail loud (502) rather than deliver — see VENDORS.md’s Twilio entry for why that’s the correct, safe state right now, not a bug to route around. A same-day incident (PR #512, reverted) briefly shipped a workaround that bypassed the custom-text restriction using a vendor-generated code that turned out to be a fixed constant, not random — see CLAUDE.md’s Current State for the full account.ai— Secrets:NEUREUS_API_KEY,NEUREUS_BASE_URL.finstack-marketing— URL:https://finstack.sh(custom domain ✓) /https://finstack-marketing.glcf.workers.dev.
All other Workers not listed above (payment, payout, recipient, customer, receipt, split, tip, tax, inventory, loyalty, billing, fraud, compliance, database, composer, ai, wallet, platform, analytics, messaging, payfac, merchant, routing, ledger, fanout, fraud-scoring-consumer, webhooks) also have no public URL as of 2026-07-27 — same workers_dev = false fix as svc-auth above, reachable only via gateway’s service bindings (see BUGS.md). finstack-marketing and gateway are the only two Workers left with a *.workers.dev fallback.
No finstack-admin-gateway equivalent exists (see functional gaps above). Per-Worker D1 databases; migrations under each Worker’s own migrations/ directory.
-
routing— Secret:ROUTING_CREDENTIALS_MASTER_KEY(base64 of 32 random bytes,openssl rand -base64 32) — AES-256-GCM master key encryptingprocessor_configs.credentialsat rest (ADR-12). Routing is the only Worker that decrypts a processor credential. Set in production 2026-07-28 (ADR-12 Task 8 secrets provisioning) — was never actually set despite Task 1 shipping the encryption code (confirmed viawrangler secret list, onlyINTERNAL_ASSERTION_SECRETexisted); a fresh key was generated and vaulted (finstack-cf/ROUTING_CREDENTIALS_MASTER_KEY). Noprocessor_configsrow exists yet, so this secret is not yet reachable by any real request — inserting that row and running Task 8’s live-verification flow needs explicit go-ahead, not done as part of this secrets step. -
webhooks— Secrets:INTERNAL_ASSERTION_SECRET(shared value, same as every other Worker) andWEBHOOK_ENDPOINT_MASTER_KEY(base64 of 32 random bytes) — AES-256-GCM master key encryptingwebhook_endpoints.secret_encat rest, same scheme asrouting’s entry above (US-002-012 Part B, 2026-08-02).wrangler secret listconfirming the key NAME exists (as this entry originally said) turned out to be insufficient — the key existing doesn’t mean its VALUE is correct, and it wasn’t: initially set fromcloudflare/FINSTACK_CF_INTERNAL_ASSERTION_SECRET, which does not match the real production value (confirmed by live end-to-end testing failing withverifyAssertion’s “signature verification failed”, then extracting the real deployed value fromsvc-authitself via a temporary, immediately-removed debug route gated behindPROVISION_KEY_SECRET). The correct vault entry isfinstack-cf/INTERNAL_ASSERTION_SECRET, not thecloudflare/FINSTACK_CF_INTERNAL_ASSERTION_SECRETentry — the two vault entries hold different values, and only thefinstack-namespaced one matches what’s actually deployed tosvc-auth(the signer) and every already-correct Worker. Re-set onwebhookswith the correct value 2026-08-02 (US-002-012 Part B2, PR #523) and live-verified end-to-end (POST /v1/webhooks/endpointsthroughapi.finstack.sh→ 200, real row created, secret returned once). Revisit trigger: thecloudflare/FINSTACK_CF_INTERNAL_ASSERTION_SECRETvault entry is stale and should either be corrected to matchfinstack-cf/INTERNAL_ASSERTION_SECRETor deleted, since its mere existence under a very similar name is exactly what caused this — a future session has no way to know which of two identically-named-in-spirit vault entries is authoritative without hitting this same bug again.WEBHOOK_ENDPOINT_MASTER_KEY(vaulted atcloudflare/FINSTACK_CF_WEBHOOK_ENDPOINT_MASTER_KEY) is unaffected — it’s a new secret with only one vault entry, no drift possible. No route in this Worker decryptsWEBHOOK_ENDPOINT_MASTER_KEYyet (Part C, not built) — it exists purely soencryptWebhookSecretcan run at creation time. -
order— Secret:INTERNAL_ASSERTION_SECRET(shared value, same as every other Worker). D1:order-db(5ddd0a54-018e-4320-94eb-53b558c0c706), migration0001_create_orders.sqlapplied 2026-08-07. Service bindings:PAYMENT,INVENTORY,RECEIPT.workers_dev = false, reachable only viagateway’s/v1/ordersroute orpayment’s dispute-forward call. Provisioning note:order-dbhas awrangler.workers.tomlalongside its realwrangler.toml(for the vitest pool) — a barenpx wrangler d1 createwith no--configflag resolved the wrong account and failed authentication;--config wrangler.tomlfixed it. Worth checking on any other Worker that has both files before assuming a wrangler command targets the right config. (order-db’s id above corrects an earlier version of this entry that recorded a stale/wrong uuid — the live production id was re-verified via the D1 HTTP API during ADR-16’s 2026-08-23 work, not assumed.) -
The 16 Workers ADR-16 deployed for the first time on this account (2026-08-23) —
ai,analytics,billing,composer,customer,database,fraud,inventory,loyalty,payfac,receipt,split,tax,tip,webhooks,wallet— each gotINTERNAL_ASSERTION_SECRETset from the vaultedfinstack-cf/INTERNAL_ASSERTION_SECRETvalue (the same value already live on every other Worker;webhooksadditionally re-gotWEBHOOK_ENDPOINT_MASTER_KEYfromcloudflare/FINSTACK_CF_WEBHOOK_ENDPOINT_MASTER_KEY, see that Worker’s own entry above — unchanged by this deploy since it was a fresh, empty database with no prior encrypted rows to worry about).fanout/fraud-scoring-consumer(deployed the same session) need no secrets — pure queue consumers.database’sPOST_AI_API_KEYandai’s optionalNEUREUS_API_KEYwere deliberately left unset (pre-existing gaps, unrelated to this deploy — see CLAUDE.md’s functional-gaps list). See DECISIONS.md’s ADR-16 for the full deploy sequence, the 5 queues created foranalytics/webhooks/fraud-scoring-consumer, and each Worker’s consolidated D1 target.
Monitoring (Alertmanager — optional Fly app)
Section titled “Monitoring (Alertmanager — optional Fly app)”Alert notification routing via docs/prometheus/alertmanager.yml. Secrets:
SLACK_WEBHOOK_URL— Slack incoming webhook for#finstack-alertsSLACK_CRITICAL_WEBHOOK_URL— Slack webhook for#finstack-alerts-critical(optional; falls back toSLACK_WEBHOOK_URL)
See RUNBOOK.md §Monitoring Setup for deployment steps.
Cloudflare Pages (Galactic account a6b10be06096b8f9b7c718f29dcd2249, since INC-003 2026-07-25 — previously FGV)
Section titled “Cloudflare Pages (Galactic account a6b10be06096b8f9b7c718f29dcd2249, since INC-003 2026-07-25 — previously FGV)”Deliberately NOT migrated in the 2026-08-22 account cutover — the Pages projects themselves (finstack-dashboard, finstack-developer-portal) still run on Galactic, unchanged. Only their DNS now routes through the new Finstack-account zone (app.finstack.sh/docs.finstack.sh are CNAME records in the new zone pointing at the same *.pages.dev hosts below) — a deliberate cross-account CNAME, confirmed working for Pages (unlike Workers) per Equitas ADR-24.
finstack-dashboard (Vite SPA + BFF) — login works (2026-07-25)
Section titled “finstack-dashboard (Vite SPA + BFF) — login works (2026-07-25)”- URL:
https://app.finstack.sh(custom domain ✓ active) /https://finstack-dashboard-8to.pages.dev - BFF: CF Pages Function
functions/api/[[route]].ts—/api/passkeys/*(WebAuthn, proxied tosvc-authvia theSVC_AUTHservice binding) +/api/v1/*proxy - Config lives in
finstack-dashboard/wrangler.jsonc(not.toml—wrangler pages deployonly auto-detects.jsonc/.jsonfor Pages, and was previously silently reading an unrelated stray rootwrangler.jsoncinstead, so the old.toml’s bindings never actually applied). Declares theSVC_AUTHservice binding + plain varsFINSTACK_API_GATEWAY_URL=https://api.finstack.sh,RP_ORIGIN=https://app.finstack.sh. AUTH_SECRET(jose JWT signing key for the dashboard’s own session cookie) is a Pages secret, not in the repo — value stored in vaultfinstack/DASHBOARD_AUTH_SECRET. Rotate viawrangler pages secret put AUTH_SECRET --project-name finstack-dashboard.- Live-verified 2026-07-25:
/api/auth/session,/api/auth/signout(Origin-checked),/api/passkeys/register/begin(Origin-checked) all confirmed against production. Confirmed via the CF API that none of this — service binding, vars, or secret — was actually configured before this date, despitefinstack-dashboard/wrangler.tomlhaving declared the intent; see TODO.md’s 2026-07-25 entry for the full finding.
finstack-developer-portal (Astro static)
Section titled “finstack-developer-portal (Astro static)”- URL:
https://docs.finstack.sh(custom domain ✓ active) /https://finstack-developer-portal-k7n.pages.dev
Neon Postgres (retired 2026-07-25, ADR-9 — infra untouched, no longer read by any code in this repo)
Section titled “Neon Postgres (retired 2026-07-25, ADR-9 — infra untouched, no longer read by any code in this repo)”Project finstack (ancient-heart-91326215, FGV org, pg17, us-east-1) was finstack-rs’s database. Left running untouched (not deleted) as part of the code-only removal — the data still exists but nothing in this repo connects to it anymore. finstack uses per-Worker D1 databases instead; see the Cloudflare Workers section above. If Neon needs to be decommissioned or its data migrated, that’s a separate, not-yet-authorized infra action.