Narrow the delivery failure to the receive side
The headless peer tool settles the direction. iOS sent to a real shell-driven
account and it arrived:
testAMessageReachesANonIOSClient passed (39.2s)
peer poll -> "hello from iOS to the peer tool", from_user 403
So the send path and the relay are fine, and neither explains two iOS clients
failing one way. What differs there is that both sides initiate a session
simultaneously; the peer tool never initiates, which is why it works.
Suspect is E2EInbox.swift:995. `ingest` returning true means "advance the
cursor" — consume the envelope — and false means hold it for the next poll. The
retry branch is taken only when `existing == nil`. A recipient that already
created its own outgoing session has `existing != nil`, so an envelope it
cannot yet decrypt is skipped forever. That is exactly the simultaneous-
initiation case.
Recorded as a hypothesis with its evidence, not as a fix. It has not been
instrumented, and this file's history includes a previous advance-uncondition-
ally bug that lost messages silently, so the area deserves care rather than a
quick patch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
95659f4685
commit
18499ae746
@@ -384,3 +384,50 @@ successful create would have rendered a row. It did not — the create threw.
|
||||
⚠️ Both concern sending **to** the second account. Suggestive, not a diagnosis.
|
||||
The next run needs the thread dump (added) plus the relay's own view of what is
|
||||
queued for that device.
|
||||
|
||||
### Narrowed 2026-08-25: it is the RECEIVE side, and only when both sides opened first
|
||||
|
||||
The headless peer tool settles the direction. `qz89925` (user 402), a real
|
||||
account driven from a shell, was sent to by the iOS app:
|
||||
|
||||
```
|
||||
CrossPlatformUITests/testAMessageReachesANonIOSClient passed (39.2s)
|
||||
peer poll → {"messages":[{"text":"","from_user":403},
|
||||
{"text":"hello from iOS to the peer tool","from_user":403}]}
|
||||
```
|
||||
|
||||
✅ **iOS → non-iOS delivery works.** The sender encrypts correctly and the relay
|
||||
delivers. (The empty first message is the §14.4 delivery-key prelude, as
|
||||
designed.) So neither the send path nor the relay explains the two-simulator
|
||||
failure.
|
||||
|
||||
**What differs in the two-simulator case is that both sides initiate a session
|
||||
with each other at the same time** — each signs up, opens the chat, and sends
|
||||
before either has received anything. The peer tool never initiates, which is
|
||||
exactly why it works.
|
||||
|
||||
🔴 **Suspect: `E2EInbox.swift:995`.**
|
||||
|
||||
```swift
|
||||
// Transient: a non-prekey message arrived before the establishing
|
||||
// prekey created the session. Do NOT advance -- retry on the next poll.
|
||||
guard let r = ratchet else { return existing == nil ? false : true }
|
||||
```
|
||||
|
||||
`ingest` returning **true means "advance the cursor"** — the envelope is
|
||||
consumed. `false` holds the cursor so the next poll retries it. The condition
|
||||
returns `false` (retry) only when `existing == nil`. A recipient that has
|
||||
already created its OWN outgoing session to that device has `existing != nil`,
|
||||
so an envelope it cannot yet decrypt returns **true and is skipped forever**.
|
||||
|
||||
That is precisely the simultaneous-initiation case, and it matches every
|
||||
observation: the peer tool (`existing == nil`) receives fine; a second iOS
|
||||
client that sent first does not. The comment above the line describes holding
|
||||
the cursor, which is what the `false` branch does — the `existing != nil` branch
|
||||
does the opposite.
|
||||
|
||||
⚠️ **Hypothesis, not a confirmed diagnosis.** It has not been instrumented, and
|
||||
the neighbouring code deliberately archives retired sessions, so there may be an
|
||||
intended reason for the asymmetry. Verify before changing: the same file's
|
||||
history shows a previous "advance unconditionally" bug that caused silent
|
||||
message loss, so this area has form.
|
||||
|
||||
Reference in New Issue
Block a user