This is why an iPhone stops receiving anything and says nothing about it. Found from production logs while the user was reporting, for the third or fourth time, that messages do not arrive, calls do not connect, and an incoming call shows no screen. All one cause.
The evidence
refused GET /api/flamenet/v1/e2e/messages: unauthorized
this device was removed from the account × 8 in half an hour
Meanwhile the relay is fanning envelopes out to all three of that account's devices and every one sits delivered = f.
What was wrong
The design was right and written down in two places — a removed device must learn that rather than poll an empty mailbox for ever, so /e2e/messages answers 410, deliberately not 401, because "sign in again" is the wrong instruction for a device somebody has removed.
But the retirement check runs earlier, in authenticated(), and every AuthError collapsed into WireError.unauthorized. A revoked device threw deviceRetired, got a generic 401, and never reached the 410 the route would have given it. The route's own comment says "the retirement case threw during authentication" — it did, into the wrong status.
The client only acts on 410. AppEnvironment.deregistrationWatch on iOS signs out and explains what happened; it is wired to deviceDeregistered, which fires on 410 only. On a 401 the app keeps polling, keeps failing, and says nothing.
The only path that behaved correctly was the one nobody records
how the device went away
key log entry
status
client learns?
row vanished, nothing recorded it
no
410
yes
revoked from the Devices screen
yes (revoke)
401
no
expired by the retention sweep
yes (expire)
401
no
The accident worked; both real doors did not.
And the existing test proved the accident
revokedDeviceLearnsItIsGone deletes the device row directly, so it exercised the branch that worked and stayed green the whole time. The new test revokes through revokeDevice — the door the Devices screen uses — and asserts KeyLog.isRetired first, so it cannot silently drift back onto the row-deleted path.
Falsified: with the new catch removed, the old test still passes and the new one fails on both assertions.
Note for whoever deploys this
A phone already in this state recovers by signing in again — the fix lets the app find out and say so, it does not un-revoke anything. FN_MAX_DEVICES is 3 and the account under test is at 3, so a re-registration will evict its oldest device.
**This is why an iPhone stops receiving anything and says nothing about it.** Found from production logs while the user was reporting, for the third or fourth time, that messages do not arrive, calls do not connect, and an incoming call shows no screen. All one cause.
## The evidence
```
refused GET /api/flamenet/v1/e2e/messages: unauthorized
this device was removed from the account × 8 in half an hour
```
Meanwhile the relay is fanning envelopes out to all three of that account's devices and every one sits `delivered = f`.
## What was wrong
The design was right and written down in two places — a removed device must **learn** that rather than poll an empty mailbox for ever, so `/e2e/messages` answers **410**, deliberately not 401, because *"sign in again" is the wrong instruction for a device somebody has removed.*
But the retirement check runs earlier, in `authenticated()`, and every `AuthError` collapsed into `WireError.unauthorized`. A revoked device threw `deviceRetired`, got a generic **401**, and never reached the 410 the route would have given it. The route's own comment says "the retirement case threw during authentication" — it did, into the wrong status.
The client only acts on 410. `AppEnvironment.deregistrationWatch` on iOS signs out and explains what happened; it is wired to `deviceDeregistered`, which fires on 410 only. On a 401 the app keeps polling, keeps failing, and says nothing.
## The only path that behaved correctly was the one nobody records
| how the device went away | key log entry | status | client learns? |
|---|---|---|---|
| row vanished, nothing recorded it | no | **410** | yes |
| revoked from the Devices screen | yes (`revoke`) | **401** | no |
| expired by the retention sweep | yes (`expire`) | **401** | no |
The accident worked; both real doors did not.
## And the existing test proved the accident
`revokedDeviceLearnsItIsGone` deletes the device row *directly*, so it exercised the branch that worked and stayed green the whole time. The new test revokes through `revokeDevice` — the door the Devices screen uses — and asserts `KeyLog.isRetired` first, so it cannot silently drift back onto the row-deleted path.
**Falsified:** with the new catch removed, the old test still passes and the new one fails on both assertions.
## Note for whoever deploys this
A phone already in this state recovers by signing in again — the fix lets the app find out and say so, it does not un-revoke anything. `FN_MAX_DEVICES` is 3 and the account under test is at 3, so a re-registration will evict its oldest device.
added 3 commits
<ul><li>83f1ef5a...bd466827 - 2 commits from branch <code>main</code></li><li>1384f27e - A removed device is told so, instead of being refused for ever (401 → 410)</li></ul>
[Compare with previous version](/paulhitt/flamenet-relay/-/merge_requests/78/diffs?diff_id=2009341247&start_sha=83f1ef5a50fe8a3450d371e111b64afe536575e4)
paulhitt
(Migrated from gitlab.com)
merged commit 82485da949 into main2026-09-07 14:19:33 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
This is why an iPhone stops receiving anything and says nothing about it. Found from production logs while the user was reporting, for the third or fourth time, that messages do not arrive, calls do not connect, and an incoming call shows no screen. All one cause.
The evidence
Meanwhile the relay is fanning envelopes out to all three of that account's devices and every one sits
delivered = f.What was wrong
The design was right and written down in two places — a removed device must learn that rather than poll an empty mailbox for ever, so
/e2e/messagesanswers 410, deliberately not 401, because "sign in again" is the wrong instruction for a device somebody has removed.But the retirement check runs earlier, in
authenticated(), and everyAuthErrorcollapsed intoWireError.unauthorized. A revoked device threwdeviceRetired, got a generic 401, and never reached the 410 the route would have given it. The route's own comment says "the retirement case threw during authentication" — it did, into the wrong status.The client only acts on 410.
AppEnvironment.deregistrationWatchon iOS signs out and explains what happened; it is wired todeviceDeregistered, which fires on 410 only. On a 401 the app keeps polling, keeps failing, and says nothing.The only path that behaved correctly was the one nobody records
revoke)expire)The accident worked; both real doors did not.
And the existing test proved the accident
revokedDeviceLearnsItIsGonedeletes the device row directly, so it exercised the branch that worked and stayed green the whole time. The new test revokes throughrevokeDevice— the door the Devices screen uses — and assertsKeyLog.isRetiredfirst, so it cannot silently drift back onto the row-deleted path.Falsified: with the new catch removed, the old test still passes and the new one fails on both assertions.
Note for whoever deploys this
A phone already in this state recovers by signing in again — the fix lets the app find out and say so, it does not un-revoke anything.
FN_MAX_DEVICESis 3 and the account under test is at 3, so a re-registration will evict its oldest device.added 3 commits
main1384f27e- A removed device is told so, instead of being refused for ever (401 → 410)Compare with previous version
mentioned in commit
82485da949changed the description