Skip to content

Beta Request Flow (Friends & Family TestFlight invites)

Dark Cosmos now has a public TestFlight join link (testflight.apple.com/join/MUCrRgEP, set up 2026-07-11) on the marketing site, feeding an App Store Connect External “Public” beta group. That’s the right mechanism for anonymous site visitors.

Chris also wants to invite close friends and family, but doesn’t want to personally message each one (“hey, I added you, please test it”) — a personal ask creates social obligation. He wants to send them a link to a page on the site where they can express interest with one click, without it feeling like a commitment, and without requiring a raw self-serve public link (which he doesn’t want to hand to this smaller, more personal circle in the same undifferentiated pool as internet strangers).

  • A lightweight “request access” form on the marketing site, separate from the existing public Join Beta CTA.
  • Chris approves each request with one tap, from his phone, no laptop/Claude session needed.
  • Approved requesters land in a distinct App Store Connect group from the raw public link crowd, so the two audiences stay distinguishable later.
  • No new persistent datastore — this is friends-and-family scale (a handful of people), not a system that needs to survive/query historical requests.
  • Rate limiting / anti-abuse beyond a basic honeypot — low stakes if someone spams the form (worst case: an ignorable Pushover notification).
  • A “reject” action or any record-keeping of unapproved requests. If Chris never taps approve, the request simply never happens — nothing to clean up.
  • Changing or replacing the existing public TestFlight link/CTA (bullet-heaven-multiagent-deploy work from 2026-07-11) — this is an additional, separate path.

Three pieces, chosen for zero new infrastructure (approach “stateless signed link” over a persistent queue or a fully-manual Claude-mediated flow — see alternatives below):

[Site form] --POST--> [n8n: Beta Request webhook] --Pushover (signed link)--> [Chris's phone]
|
tap
v
[n8n: Beta Approve webhook] --> [ASC API: invite tester]

A new card, styled consistently with the existing .beta-callout/.bible sections, at its own anchor (#request) — NOT part of the primary hero CTA flow (that stays the public link). Chris shares this anchor directly (text/WhatsApp) with friends and family. A low-key link also appears in the footer for organic discovery.

Fields: name, email, plus a hidden honeypot input (filled by bots, left empty by humans — if non-empty, the n8n webhook silently 200s without notifying Chris).

Submit via fetch(POST) to the n8n webhook URL. On success, show a static “Thanks — you’ll get an email if approved” message. No client-side library needed; matches the plain hand-written HTML/JS style already used on this site (no build step).

2. n8n — “Dark Cosmos: Beta Request” workflow

Section titled “2. n8n — “Dark Cosmos: Beta Request” workflow”
  • Trigger: Webhook (POST), public (n8n’s /webhook* path already bypasses CF Access per existing convention — same as the Site Health Checker webhook).
  • Validates email looks like an email; if the honeypot field is filled, return 200 and stop (no Pushover, no error — don’t tip off bots that they were caught).
  • Builds an HMAC-SHA256 signature over email|name using a secret stored in n8n’s .env (BETA_REQUEST_HMAC_SECRET, new var — never embedded in the workflow JSON itself, per the project’s standing n8n secrets rule).
  • Sends a Pushover notification to Chris: title “Dark Cosmos beta request”, message "<name> (<email>) wants into the beta", with a url field pointing at the Approve webhook: https://n8n.lumara.digital/webhook/beta-approve?email=...&name=...&sig=... (all three URL-encoded) and url_title: "Approve & invite".
  • Responds 200 to the site’s fetch call immediately — approval is async, the site doesn’t wait on it.
  • settings.errorWorkflow = the standard Error Handler (mbNM85YFutYZ54ii), per project convention — any failure (Pushover API down, etc.) pages Chris via the existing monitor channel instead of failing silently.

3. n8n — “Dark Cosmos: Beta Approve” workflow

Section titled “3. n8n — “Dark Cosmos: Beta Approve” workflow”
  • Trigger: Webhook (GET) — the link Chris taps from the Pushover notification.
  • Recomputes the HMAC over the received email|name using the same secret; if it doesn’t match the received sig, return a plain “invalid or expired link” HTML page and stop. This is what stops someone from editing the URL’s email param to self-invite a different address — the signature only validates the exact pair Chris was actually shown.
  • On a valid signature: calls the App Store Connect API, POST /v1/betaTesters with attributes.email, attributes.firstName (from name), and relationships.betaGroups.data → the Friends & Family group id (new group, see below). Apple sends that person their own official TestFlight invite email automatically — Chris never needs to personally message them.
  • Returns a plain “✓ <name> invited” HTML confirmation page (not raw JSON — this is opened directly in a phone browser from the notification tap).
  • Same errorWorkflow convention as above.
  • ASC auth: reuses the same JWT-over-ES256 pattern the bh-appstore-release skill uses locally (key id, issuer id, p8 private key) — these credentials get added to n8n’s .env (or a mounted secret file) so the workflow can sign its own short-lived JWT per call. Exact signing mechanism (n8n’s built-in JWT node if it supports ES256, vs. a Code node using Node’s crypto module) is an implementation-time decision, not a design blocker — flagged as an open technical risk to resolve during planning.

4. App Store Connect — new “Friends & Family” beta group

Section titled “4. App Store Connect — new “Friends & Family” beta group”

A second External testing group (alongside the existing “Public” group from 2026-07-11), isInternalGroup: false, no public link enabled (invite-only — testers only get in via the betaTesters API call above, never via a shareable URL). Needs the same build(s) attached as the Public group so invitees get a working build, and needs Beta App Review clearance the same way (shared per-app review state — if a build has already cleared review for the Public group, it’s usable by Friends & Family too without a second review).

  • Persistent queue (SQLite/NocoDB-backed): every request gets a durable row, approve link references just an id. More conventional, gives a historical audit log — rejected as unnecessary infrastructure for a feature that will see a handful of submissions ever.
  • Fully manual (no Approve webhook): Request workflow just Pushover-notifies Chris with the name/email; “approving” means telling Claude in a future session to run the ASC invite by hand. Zero n8n build beyond the first webhook — rejected because it defeats the point: Chris explicitly wants a one-tap phone action, not a requirement to open a laptop/Claude session per invite.
  • Malformed email on submit → site-side validation blocks submit before it reaches n8n (basic UX; not a security boundary).
  • n8n webhook receives a malformed/missing email → return 400, no Pushover sent.
  • Honeypot triggered → silent 200, no Pushover, no ASC call.
  • Approve link tampered (bad signature) → plain-text rejection page, no ASC call, nothing logged beyond n8n’s own execution history.
  • ASC API call fails (auth expired, Apple-side error, tester already exists, etc.) → workflow errors normally, errorWorkflow pages Chris via the existing monitor Pushover channel with the failure — separate from and clearly distinguishable in tone from the “beta request” notification itself.

No automated test suite for this (n8n workflows + a static HTML form aren’t part of the Godot test suite). Manual verification only:

  1. Submit the site form with a real, Chris-controlled email address.
  2. Confirm the Pushover notification arrives with the expected name/email and a tappable link.
  3. Tap the link; confirm the confirmation page renders and the ASC API call succeeds (tester appears in the Friends & Family group in App Store Connect within a few minutes).
  4. Submit again with the honeypot field manually filled (via devtools) — confirm no Pushover fires.
  5. Manually edit the email query param on a valid approve link — confirm the signature check rejects it.