Skip to main content

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 at https://optolink-test-f1a54d0fbc1c.herokuapp.com (/health ✅, /docs-json ✅).
  • dashboard.optoapp.link and the *.pages.dev portal previews hit THIS backend. There is no separate production deployment of the platform — "production" writes the test DB. (A second Heroku app optolink exists behind share.optoapp.link; test-only pages 404 there — don't confuse the two.)
  • NODE_ENV is unset on the Heroku app → the sandbox module is live there (source-checked against optolink-backend @ 29f8589, 2026-10-06: src/app.module.ts registers SandboxModule when NODE_ENV !== 'production').

Environment map​

HostRole
go.optoapp.linktest link domain — Cloudflare CNAME → Heroku (DNS-only), cert live, Apple CDN serves the AASA (verified 2026-09-28)
dashboard.optoapp.linktest portal
optolink-test-f1a54d0fbc1c.herokuapp.comtest API
share.optoapp.linkthe OTHER Heroku app (optolink) — a different stack
optolink.io / optolink.appseed 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 local CLERK_SECRET_KEY.
  • Turnstile bot protection is ON: automated (headless) sign-ups cannot pass. Password sign-in via FAPI curl is captcha-free. The __client cookie is never issued to curl (client-state FAPI steps return signed_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_KEY from ~/.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:set only becomes visible after the release command succeeds — config:get immediately after a set can show the OLD value. Re-read after a beat before concluding failure.
  • DB: local optolink-backend/.env DATABASE_URL is the SAME shared RDS TEST DB (heroku config:get DATABASE_URL -a optolink-test). psql it SELECT-only; it serves several machines and concurrent runs, and is high-latency/intermittent (a 3–13 s SELECT 1 is 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 1024 against this stack (adb shell pm get-app-links <pkg>): the system verifier fetched assetlinks.json while 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-verify stayed 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, ::1 request 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 AppConfig rows (iOS + Android bundle ids); seed 2 per org to skip onboarding. AppConfigs without teamId/sha256Fingerprints show 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-system LinkTemplate.count, non-GRACE Domain.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 from optolink-backend with DATABASE_URL='<heroku url>'. Idempotent EXCEPT the audit sections. Transient scratch — expected to move with backlog B-021.