Deployed test stack
Scope: the deployed testing environment itself — what it is, what runs there, how to operate it. Driving flows against it is the test-stack runbook; who you test as is test accounts.
Live-stack facts are recorded, not source-checkable from this repo (latest re-verification 2026-10-04) — re-verify live on next use. Code-backed claims carry their own verification line.
What it is
- Heroku app
optolink-test— backend athttps://optolink-test-f1a54d0fbc1c.herokuapp.com(/health✅,/docs-json✅). dashboard.optoapp.linkand the*.pages.devportal previews hit THIS backend. There is no separate production deployment of the platform — "production" writes the test DB. (A second Heroku appoptolinkexists behindshare.optoapp.link; test-only pages 404 there — don't confuse the two.)NODE_ENVis unset on the Heroku app → the sandbox module is live there (source-checked against optolink-backend @ 29f8589, 2026-10-06:src/app.module.tsregistersSandboxModulewhenNODE_ENV !== 'production').
Environment map
| Host | Role |
|---|---|
go.optoapp.link | test link domain — Cloudflare CNAME → Heroku (DNS-only), cert live, Apple CDN serves the AASA (verified 2026-09-28) |
dashboard.optoapp.link | test portal |
optolink-test-f1a54d0fbc1c.herokuapp.com | test API |
share.optoapp.link | the OTHER Heroku app (optolink) — a different stack |
optolink.io / optolink.app | seed fiction — no DNS zone anywhere, nothing to publish |
Clerk — separate instance, bot protection on
- The stack runs Clerk dev instance
active-shrimp-6400.clerk.accounts.dev(pk_test_/sk_test_). Local tokens are invalid on the deployed stack and vice versa — the deployed ops org 404s through the localCLERK_SECRET_KEY. - Turnstile bot protection is ON: automated (headless) sign-ups cannot
pass. Password sign-in via FAPI curl is captcha-free. The
__clientcookie is never issued to curl (client-state FAPI steps returnsigned_out) — only the Clerk JS runtime in a real browser completes sign-ups / token mints. - Sign-up on the deployed instance enforces email verification (code from the inbox; recorded 2026-10-04).
Gateways — both TEST mode
Stripe (sk_test_) and Paymob (egy_sk_test_) run test mode on this
stack — billing flows are safe to exercise. Paymob mode is readable from
the key prefix (egy_sk_test_ vs egy_sk_live_).
Operating it
- Heroku access: the CLI ignores
HEROKU_API_KEYfrom~/.bashrc(demands interactive login). Use the Platform API:curl -H "Authorization: Bearer $HEROKU_API_KEY" \-H "Accept: application/vnd.heroku+json; version=3" \https://api.heroku.com/apps/optolink-test/config-vars - Release-phase deploys:
heroku config:setonly becomes visible after the release command succeeds —config:getimmediately after a set can show the OLD value. Re-read after a beat before concluding failure. - DB: local
optolink-backend/.envDATABASE_URLis the SAME shared RDS TEST DB (heroku config:get DATABASE_URL -a optolink-test).psqlit SELECT-only; it serves several machines and concurrent runs, and is high-latency/intermittent (a 3–13 sSELECT 1is normal). The local-snapshot recipe lives in the backend repo docs (backlog B-003). - AuditLog rows are immutable on this DB (DB-level trigger blocks
UPDATE/DELETE). Consequence: verification orgs can't be cleaned up —
revoke minted keys (
isRevoked=true) and leave the org; audit history survives by design.
Write-safety
All live-audit writes go inside the dedicated QA org only. amgad_*
accounts and the "Optomatica" org (plan solo) are the teammate's —
READ ONLY.
Gotchas (recorded, stack artifacts)
Each bullet carries its date; re-verify on next occurrence.
- 2026-10-05 — Android App Links verification fails with state
1024against this stack (adb shell pm get-app-links <pkg>): the system verifier fetchedassetlinks.jsonwhile the dyno was asleep, timed out, and cached the failure — https links fall through to Chrome + the redirect interstitial. 1024 does not self-heal: state re-checks only on app install/update or explicit re-verify (pm verify-app-links --re-verifystayed 1024 twice through the retry backoff). Dev unblock:adb shell pm set-app-links-user-selection --user 0 --package <pkg> true <domain>. For real installs, keep the dyno awake while verification runs. Production dynos don't sleep — dev-stack artifact only. - 2026-09-28 — backend degrades under sustained keyed load. After
~50 min uptime against the shared Postgres/Redis, back-to-back SDK-suite
runs start failing BACKEND-side (persist errors in the boot log,
/sdk/*500s,::1request timeouts). The suites are blameless — every failure is an HTTP 500 or timeout, zero assertion-shaped. Reboot the backend if it has been up a while before a keyed gate pair; green immediately after. - Seeding into this stack:
- Clerk memberships created via Backend API without a pending invitation
get stamped
owner— fix metadata + DB row after (see test accounts). - AuditLog seed scripts must be insert-once (immutability above). The
elevate seed marks rows
metadata.source: 'seed_elev'; tambe's audit rows are unmarked — don't re-run its audit section blindly. - Users/orgs land on the onboarding wizard until the org has
AppConfigrows (iOS + Android bundle ids); seed 2 per org to skip onboarding. AppConfigs withoutteamId/sha256Fingerprintsshow cosmetic "Universal links inactive / App links inactive" warnings in Settings — intended (real configs come from the wizard). - Quota bars read:
Link.count,ClickEvent.count >= subscription .currentPeriodStart(calendar month if no sub),OrgMember.count, non-systemLinkTemplate.count, non-GRACEDomain.count. The "at or over plan limits" display rule is ≥0.8 utilisation — portal-side (source-checked against optolink-portal @ 7d31d45, 2026-10-06:src/app/(app)/settings/billing-tab.tsx). - Seed scripts (recorded 2026-09-22):
misc/seed-visual-test/seed-elevate.mjs/seed-tambe.mjs, run fromoptolink-backendwithDATABASE_URL='<heroku url>'. Idempotent EXCEPT the audit sections. Transient scratch — expected to move with backlog B-021.
- Clerk memberships created via Backend API without a pending invitation
get stamped