Verify on a physical device — and set FN_APNS_ENV=production at TestFlight release #7

Closed
opened 2026-09-01 21:45:18 +00:00 by paulhitt · 8 comments
paulhitt commented 2026-09-01 21:45:18 +00:00 (Migrated from gitlab.com)

Three things cannot be exercised on a simulator at all, and all three produced real
bugs on 2026-09-01:

  • APNs push — simulators receive none. The foreground-banner fix and the relay's new
    silent background push are both unverified in the only environment where they run.
  • VoIP push — a simulator has no VoIP token, so calls fall back to an ordinary alert
    push. The "New message" banners appearing during calls were specifically that
    fallback path. A real device would not have taken it.
  • The microphone — voice notes cannot be recorded on a simulator, so that feature has
    never been observed working end to end.

Done looks like: one call, one message, one voice note and one backgrounded
notification, between two physical devices, before any build goes to a tester.

Three things cannot be exercised on a simulator at all, and all three produced real bugs on 2026-09-01: - **APNs push** — simulators receive none. The foreground-banner fix and the relay's new `silent` background push are both unverified in the only environment where they run. - **VoIP push** — a simulator has no VoIP token, so calls fall back to an ordinary alert push. The "New message" banners appearing during calls were *specifically* that fallback path. A real device would not have taken it. - **The microphone** — voice notes cannot be recorded on a simulator, so that feature has never been observed working end to end. **Done looks like:** one call, one message, one voice note and one backgrounded notification, between two physical devices, before any build goes to a tester.
paulhitt commented 2026-09-01 22:23:13 +00:00 (Migrated from gitlab.com)

mentioned in commit bd7b7c96e4

mentioned in commit bd7b7c96e4c88c336b39b7c98e03308dd921a385
paulhitt commented 2026-09-01 22:29:01 +00:00 (Migrated from gitlab.com)

Release-time step, added 2026-09-01: FN_APNS_ENV=production

🔴 The moment Apple approves and the build goes out via TestFlight, the relay
must be told
, or nobody's iPhone rings and nothing says why.

Entrypoint.swift resolves the APNs environment as:

let sandbox = (envv["FN_APNS_ENV"] ?? "sandbox").lowercased() != "production"

FN_APNS_ENV is not set in /srv/prod/messenger/.env today, so the relay is on
sandbox — checked 2026-09-01. That default is deliberate and right for a build you
install yourself, and wrong for TestFlight.

Why it will not be obvious. A production push to a sandbox token — or the reverse —
fails with BadDeviceToken, and the error does not say which way round it is. From the
outside it reads as "calls don't ring on a locked phone", which is indistinguishable from
a bug in the call path. The relay's own source carries a warning about exactly this.

Steps at release:

  1. Set FN_APNS_ENV=production in /srv/prod/messenger/.env
  2. Recreate the relay so it re-reads the env
  3. Confirm the startup log line: APNs wake-ups enabled for net.flamenet.messenger (production)
  4. Ring a locked TestFlight phone from Android before telling anyone it works

⚠️ Order matters: a development build on a physical device needs sandbox. If both a
dev device and TestFlight are in play at once, one of them will not ring, and the relay
cannot serve both — the environment is per-relay, not per-token.

ℹ️ FN_APNS_TOPIC is also unset and defaults to net.flamenet.messenger, which matches
the shipping bundle id. Correct today, but a default rather than a decision.


What is already in place (verified 2026-09-01)

  • Info.plist declares both voip and remote-notification background modes
  • VoIPCallReceiver + CallKit are merged to main (8265298)
  • The relay holds secrets/apns-key.p8, with FN_APNS_KEY_ID and FN_APNS_TEAM_ID set
  • Android's side needs none of this: MessengerService uses CallStyle,
    CATEGORY_CALL and setFullScreenIntent(full, true), ships in 0.8, and is testable now

A simulator cannot test any of it. It never receives a VoIP token, so the relay falls
back to an ordinary alert push — which is the exact path that produced the spurious
"New message" banners during calls on 2026-09-01. The simulator does not merely fail to
exercise this; it exercises the wrong branch.

## Release-time step, added 2026-09-01: `FN_APNS_ENV=production` 🔴 **The moment Apple approves and the build goes out via TestFlight, the relay must be told**, or nobody's iPhone rings and nothing says why. `Entrypoint.swift` resolves the APNs environment as: ```swift let sandbox = (envv["FN_APNS_ENV"] ?? "sandbox").lowercased() != "production" ``` `FN_APNS_ENV` is **not set** in `/srv/prod/messenger/.env` today, so the relay is on **sandbox** — checked 2026-09-01. That default is deliberate and right for a build you install yourself, and wrong for TestFlight. **Why it will not be obvious.** A production push to a sandbox token — or the reverse — fails with `BadDeviceToken`, and the error does not say which way round it is. From the outside it reads as "calls don't ring on a locked phone", which is indistinguishable from a bug in the call path. The relay's own source carries a warning about exactly this. **Steps at release:** 1. Set `FN_APNS_ENV=production` in `/srv/prod/messenger/.env` 2. Recreate the relay so it re-reads the env 3. Confirm the startup log line: `APNs wake-ups enabled for net.flamenet.messenger (production)` 4. Ring a locked TestFlight phone from Android before telling anyone it works ⚠️ Order matters: a **development** build on a physical device needs sandbox. If both a dev device and TestFlight are in play at once, one of them will not ring, and the relay cannot serve both — the environment is per-relay, not per-token. ℹ️ `FN_APNS_TOPIC` is also unset and defaults to `net.flamenet.messenger`, which matches the shipping bundle id. Correct today, but a default rather than a decision. --- ## What is already in place (verified 2026-09-01) - `Info.plist` declares both `voip` and `remote-notification` background modes - `VoIPCallReceiver` + CallKit are merged to `main` (`8265298`) - The relay holds `secrets/apns-key.p8`, with `FN_APNS_KEY_ID` and `FN_APNS_TEAM_ID` set - Android's side needs none of this: `MessengerService` uses `CallStyle`, `CATEGORY_CALL` and `setFullScreenIntent(full, true)`, ships in 0.8, and is testable now **A simulator cannot test any of it.** It never receives a VoIP token, so the relay falls back to an ordinary alert push — which is the exact path that produced the spurious "New message" banners during calls on 2026-09-01. The simulator does not merely fail to exercise this; it exercises the wrong branch.
paulhitt commented 2026-09-01 22:29:01 +00:00 (Migrated from gitlab.com)

changed title from Nothing is verified on a physical device: push, VoIP, and the microphone to Verify on a physical device — and set FN_APNS_ENV=production at TestFlight release

<p>changed title from <code class="idiff"><span class="idiff left deletion">Nothing is v</span>erif<span class="idiff deletion">ied</span> on a physical device<span class="idiff right deletion">: push, VoIP, and the microphon</span>e</code> to <code class="idiff"><span class="idiff left addition">V</span>erif<span class="idiff addition">y</span> on a physical device<span class="idiff right addition"> — and set FN_APNS_ENV=production at TestFlight releas</span>e</code></p>
paulhitt commented 2026-09-01 23:17:38 +00:00 (Migrated from gitlab.com)

mentioned in issue #13

mentioned in issue #13
paulhitt commented 2026-09-09 21:29:49 +00:00 (Migrated from gitlab.com)

Partly done. Checked rather than assumed:

  • APNs environment: FN_APNS_ENV=production on the box, confirmed in the running relay's environment. That half is closed.
  • Physical-device testing has happened and found real defects, all since fixed: Face ID never prompting (a .task with no id: that took its early return while the scene was still inactive), the Files and wallet keychain read deadlocking against its own prompt on the main actor, the reaction row wrapping into an upside-down L, and a chat with no way to dismiss the keyboard at all.

Still genuinely unproven: a VoIP push ringing a locked iPhone. A simulator receives no push, so this cannot be closed from a test run — it needs someone holding a locked handset while a call comes in.

Partly done. Checked rather than assumed: - **APNs environment**: `FN_APNS_ENV=production` on the box, confirmed in the running relay's environment. That half is closed. - **Physical-device testing has happened** and found real defects, all since fixed: Face ID never prompting (a `.task` with no `id:` that took its early return while the scene was still inactive), the Files and wallet keychain read deadlocking against its own prompt on the main actor, the reaction row wrapping into an upside-down L, and a chat with no way to dismiss the keyboard at all. **Still genuinely unproven**: a VoIP push ringing a *locked* iPhone. A simulator receives no push, so this cannot be closed from a test run — it needs someone holding a locked handset while a call comes in.
paulhitt commented 2026-09-11 12:48:25 +00:00 (Migrated from gitlab.com)

Correcting the scope of this: it has been run on two physical phones. The blanket 'never verified on a device' reading is wrong and was repeated back to the owner today as a reason not to invite testers.

What is still genuinely open is narrower, and worth keeping the issue for:

  • Ringing on a LOCKED iPhone, which the simulator cannot test at all. Not exercised on 0.15.0.
  • FN_APNS_ENV=production at TestFlight, per fnmsg-apns-env-sandbox-at-release.

Owner will test the locked-phone ring once Apple approves the next build.

Correcting the scope of this: it has been run on two physical phones. The blanket 'never verified on a device' reading is wrong and was repeated back to the owner today as a reason not to invite testers. What is still genuinely open is narrower, and worth keeping the issue for: - **Ringing on a LOCKED iPhone**, which the simulator cannot test at all. Not exercised on 0.15.0. - **`FN_APNS_ENV=production` at TestFlight**, per `fnmsg-apns-env-sandbox-at-release`. Owner will test the locked-phone ring once Apple approves the next build.
paulhitt commented 2026-09-15 18:09:55 +00:00 (Migrated from gitlab.com)

Closing this: the locked-phone ring works (owner, 2026-09-15)

The one thing left on this issue was a VoIP push ringing a locked iPhone,
which a simulator cannot test at all. The owner has now run it on a real
handset and reports it works.

Both halves of this issue are therefore done:

  • FN_APNS_ENV=production on the box, confirmed in the running relay's
    environment on 2026-09-09.
  • The locked-phone ring, verified by the owner on a physical device today.

One standing hazard survives the issue and belongs with the release checklist
rather than here, so it is written into TESTFLIGHT.md section 5: the APNs
environment is per-relay, not per-token. A development build installed from
Xcode needs sandbox; TestFlight and the App Store need production. One
relay cannot serve both, and the wrong one fails as BadDeviceToken, which
from the outside looks like calls not ringing rather than like a configuration
mistake.

## Closing this: the locked-phone ring works (owner, 2026-09-15) The one thing left on this issue was a VoIP push ringing a **locked** iPhone, which a simulator cannot test at all. The owner has now run it on a real handset and reports it works. Both halves of this issue are therefore done: - `FN_APNS_ENV=production` on the box, confirmed in the running relay's environment on 2026-09-09. - The locked-phone ring, verified by the owner on a physical device today. One standing hazard survives the issue and belongs with the release checklist rather than here, so it is written into `TESTFLIGHT.md` section 5: the APNs environment is **per-relay, not per-token**. A development build installed from Xcode needs `sandbox`; TestFlight and the App Store need `production`. One relay cannot serve both, and the wrong one fails as `BadDeviceToken`, which from the outside looks like calls not ringing rather than like a configuration mistake.
paulhitt (Migrated from gitlab.com) closed this issue 2026-09-15 18:09:56 +00:00
paulhitt commented 2026-10-09 10:49:07 +00:00 (Migrated from gitlab.com)

mentioned in commit 0dfb2692fe

mentioned in commit 0dfb2692fec14162e0e4d4122915866a77e6bcb8
Sign in to join this conversation.