Skip to content

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).

EnvPurposeURLDatabase
devLocal developmentwrangler dev per WorkerD1 (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-04https://app.finstack.sh
production (CF Pages)Developer portal — spec pipeline broken, points at deleted gatewayhttps://docs.finstack.sh
production (CF Workers)Marketing sitehttps://finstack.sh

No /admin/* environment. It only ever existed via the now-deleted finstack-admin-gateway → Rust api-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, finstack workers.dev subdomain), not Galactic. Update, 2026-08-23 (ADR-16): the 18 Workers deferred below are no longer deferred — all 28 are now live, gateway carries the full 25-of-25 service-binding table, and every route that used to return 500 misconfigured_gateway now reaches its Worker (live-verified: 401, auth-gated, not 500). 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 inside compliance-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.sh bind to the new account’s gateway Worker — 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 return 500 misconfigured_gateway in production right now, an accepted tradeoff per explicit user direction (“deploy critical now, upgrade later”), not a bug. app.finstack.sh (dashboard) and bare finstack.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’s gateway/D1s/etc. are untouched, not deleted — only the api./webhooks.finstack.sh Custom 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 to main — still on branch infra/dedicated-cf-account-migration.

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 after svc-auth validates 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-27workers_dev = false, reachable only via gateway’s service binding or the dashboard’s SVC_AUTH Pages service binding (see BUGS.md).
  • payment — Secrets: STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET (webhook we_1Tx5inAAGi1faYZSdM4OH72S).
  • compliance — Secrets: STRIPE_IDENTITY_SECRET_KEY (same raw key as payment’s STRIPE_SECRET_KEY — Identity is a separately-activated product capability on the same Stripe test account, not a separate account/key), STRIPE_IDENTITY_WEBHOOK_SECRET (webhook we_1TxXsiAAGi1faYZSKKJIVXUf). Service bindings: RECIPIENT, MERCHANT (added 2026-07-26 to close a terminal KYC/KYB decision back to recipients.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 list on recipient shows that never actually happened; only INTERNAL_ASSERTION_SECRET and RECIPIENT_BANK_MASTER_KEY were set, meaning POST /recipients/:id/stripe-account would have thrown on env.STRIPE_SECRET_KEY being 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 by POST /recipients/:id/stripe-account to create Custom Connect accounts + persons, distinct from routing’s per-tenant processor_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_NUMBER is the trial account’s auto-provisioned number. Trial-tier restrictions (custom SMS text rejected, error 572006; destination numbers must be pre-verified) currently make svc-auth’s POST /auth/otp/send fail 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 encrypting processor_configs.credentials at 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 via wrangler secret list, only INTERNAL_ASSERTION_SECRET existed); a fresh key was generated and vaulted (finstack-cf/ROUTING_CREDENTIALS_MASTER_KEY). No processor_configs row 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) and WEBHOOK_ENDPOINT_MASTER_KEY (base64 of 32 random bytes) — AES-256-GCM master key encrypting webhook_endpoints.secret_enc at rest, same scheme as routing’s entry above (US-002-012 Part B, 2026-08-02). wrangler secret list confirming 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 from cloudflare/FINSTACK_CF_INTERNAL_ASSERTION_SECRET, which does not match the real production value (confirmed by live end-to-end testing failing with verifyAssertion’s “signature verification failed”, then extracting the real deployed value from svc-auth itself via a temporary, immediately-removed debug route gated behind PROVISION_KEY_SECRET). The correct vault entry is finstack-cf/INTERNAL_ASSERTION_SECRET, not the cloudflare/FINSTACK_CF_INTERNAL_ASSERTION_SECRET entry — the two vault entries hold different values, and only the finstack-namespaced one matches what’s actually deployed to svc-auth (the signer) and every already-correct Worker. Re-set on webhooks with the correct value 2026-08-02 (US-002-012 Part B2, PR #523) and live-verified end-to-end (POST /v1/webhooks/endpoints through api.finstack.sh → 200, real row created, secret returned once). Revisit trigger: the cloudflare/FINSTACK_CF_INTERNAL_ASSERTION_SECRET vault entry is stale and should either be corrected to match finstack-cf/INTERNAL_ASSERTION_SECRET or 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 at cloudflare/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 decrypts WEBHOOK_ENDPOINT_MASTER_KEY yet (Part C, not built) — it exists purely so encryptWebhookSecret can run at creation time.

  • order — Secret: INTERNAL_ASSERTION_SECRET (shared value, same as every other Worker). D1: order-db (5ddd0a54-018e-4320-94eb-53b558c0c706), migration 0001_create_orders.sql applied 2026-08-07. Service bindings: PAYMENT, INVENTORY, RECEIPT. workers_dev = false, reachable only via gateway’s /v1/orders route or payment’s dispute-forward call. Provisioning note: order-db has a wrangler.workers.toml alongside its real wrangler.toml (for the vitest pool) — a bare npx wrangler d1 create with no --config flag resolved the wrong account and failed authentication; --config wrangler.toml fixed 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 got INTERNAL_ASSERTION_SECRET set from the vaulted finstack-cf/INTERNAL_ASSERTION_SECRET value (the same value already live on every other Worker; webhooks additionally re-got WEBHOOK_ENDPOINT_MASTER_KEY from cloudflare/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’s POST_AI_API_KEY and ai’s optional NEUREUS_API_KEY were 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 for analytics/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-alerts
  • SLACK_CRITICAL_WEBHOOK_URL — Slack webhook for #finstack-alerts-critical (optional; falls back to SLACK_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 to svc-auth via the SVC_AUTH service binding) + /api/v1/* proxy
  • Config lives in finstack-dashboard/wrangler.jsonc (not .tomlwrangler pages deploy only auto-detects .jsonc/.json for Pages, and was previously silently reading an unrelated stray root wrangler.jsonc instead, so the old .toml’s bindings never actually applied). Declares the SVC_AUTH service binding + plain vars FINSTACK_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 vault finstack/DASHBOARD_AUTH_SECRET. Rotate via wrangler 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, despite finstack-dashboard/wrangler.toml having declared the intent; see TODO.md’s 2026-07-25 entry for the full finding.
  • 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.