docs: tick the audit items fixed in this pass

Every audit item a commit on main fixed is ticked with the commit that
fixed it, including the "fix these ten first" list and the stale parity
entries that the fixes resolved. Items fixed only in part keep their box
and say what is left; U8, PF9, B2 and the product calls stay open.
This commit is contained in:
Linus Rath
2026-09-24 21:02:08 +02:00
parent 6fe907b4bc
commit 06ce86e31a
+235 -175
View File
@@ -56,24 +56,28 @@ Effort: S (hours), M (a day or two), L (a week or more).
## Summary: fix these ten first
1. [ ] **P0 · S:** read receipts carry attacker-controlled headers and are sent for messages the user
never opened ([S1](#s1), [B7](#b7)).
2. [ ] **P0 · S–M:** published release APKs are signed with the public React Native debug key
([S2](#s2)).
3. [ ] **P0 · S–M:** viewer actions on a shared/group message hit the user's own account and can
delete an unrelated message ([B3](#b3)).
4. [ ] **P0 · S:** opening, starring or tagging replaces the whole keyword map; stars and tags are
erased ([B4](#b4)).
5. [ ] **P1 · S:** the viewer switches to another message when new mail arrives, and its buttons act
on the message you are not looking at ([B5](#b5)).
6. [ ] **P1 · S–M:** messages disappear from the list after a background refresh until you pull to
refresh ([B6](#b6)).
7. [ ] **P1 · S:** scheduled send, the undo-send delay and the Scheduled folder are dead on every
Stalwart server (#57, [B8](#b8)).
8. [ ] **P1 · S–M:** sharing a file into the app always fails, and `SENDTO`/`+`-address `mailto:` links
open nothing ([B9](#b9), [B10](#b10), [B11](#b11)).
9. [ ] **P1 · M:** opening a message takes 1.7–1.9 s and the layout jumps three times ([PF1](#pf1)).
10. [ ] **P1 · M:** cold start takes 3.6–4.1 s to a settled inbox and is JS-bound ([PF2](#pf2)).
1. [x] **P0 · S:** read receipts carry attacker-controlled headers and are sent for messages the user
never opened ([S1](#s1), [B7](#b7)). Fixed in `cb6ddd6`, `5f3f584`.
2. [x] **P0 · S–M:** published release APKs are signed with the public React Native debug key
([S2](#s2)). Fixed in `6f0abb4`, `746572c`; the release key and secrets still have to be set up
before the next release.
3. [x] **P0 · S–M:** viewer actions on a shared/group message hit the user's own account and can
delete an unrelated message ([B3](#b3)). Fixed in `5c7301b`.
4. [x] **P0 · S:** opening, starring or tagging replaces the whole keyword map; stars and tags are
erased ([B4](#b4)). Fixed in `5041897`.
5. [x] **P1 · S:** the viewer switches to another message when new mail arrives, and its buttons act
on the message you are not looking at ([B5](#b5)). Fixed in `9c31de2`.
6. [x] **P1 · S–M:** messages disappear from the list after a background refresh until you pull to
refresh ([B6](#b6)). Fixed in `0a8686a`.
7. [x] **P1 · S:** scheduled send, the undo-send delay and the Scheduled folder are dead on every
Stalwart server (#57, [B8](#b8)). Fixed in `e28e8b4`.
8. [x] **P1 · S–M:** sharing a file into the app always fails, and `SENDTO`/`+`-address `mailto:` links
open nothing ([B9](#b9), [B10](#b10), [B11](#b11)). Fixed in `cd4ce9d`, `06e8942`, `ee652d3` (not
yet checked on a device).
9. [x] **P1 · M:** opening a message takes 1.7–1.9 s and the layout jumps three times ([PF1](#pf1)).
Fixed in `33e303e`, `ac8e3fb`, `1348e7d`, `01fc6fb`, `6ebadf7`, `54b169c` (not re-timed yet).
10. [x] **P1 · M:** cold start takes 3.6–4.1 s to a settled inbox and is JS-bound ([PF2](#pf2)).
Fixed in `604eb39`, `063432b`, `5bcfe1f`, `82ca359`, `b9db376` (not re-timed yet).
**Why these ten:**
- **1–2:** security holes that can be exploited today without any user mistake.
@@ -89,8 +93,8 @@ suspected, or cosmetic.
### Crashes
- [ ] <a id="b1"></a>**B1 · P1 · M: session restore can hang forever when a persisted cache fails
to load.**
- [x] <a id="b1"></a>**B1 · P1 · M: session restore can hang forever when a persisted cache fails
to load.** Fixed in `29169c3`, `e37621b`, `24849ba`.
- Where: `src/stores/auth-store.ts:23-31`, `src/stores/auth-store.ts:603-608`,
`src/stores/settings-store.ts:423`.
- Why:
@@ -110,8 +114,8 @@ suspected, or cosmetic.
### Data correctness
- [ ] <a id="b3"></a>**B3 · P0 · S–M: viewer actions use the open folder's account instead of the
message's.**
- [x] <a id="b3"></a>**B3 · P0 · S–M: viewer actions use the open folder's account instead of the
message's.** Fixed in `5c7301b`, `d2066da`.
- Where: `src/screens/EmailThreadScreen.tsx:107-113` (owner falls back to the list folder),
`src/screens/EmailThreadScreen.tsx:355-358` (delete), `:388-414` (archive, move, spam),
`src/stores/email-store.ts:1194-1197`.
@@ -122,8 +126,8 @@ suspected, or cosmetic.
id, since Stalwart ids are only unique per account) to Trash.
- The team message stays where it was.
- **Verified.**
- [ ] <a id="b4"></a>**B4 · P0 · S: mark-read, star, pin and tag replace the whole `keywords`
map.**
- [x] <a id="b4"></a>**B4 · P0 · S: mark-read, star, pin and tag replace the whole `keywords`
map.** Fixed in `5041897`, `ddff2f7`.
- Where: `src/stores/email-store.ts:963-983`, `src/api/email.ts:602-609`. Outbox replay does
the same.
- Web: `lib/jmap/client.ts` (`keywords/$seen` pointer patch, `batchMarkAsRead`). RN already has
@@ -135,8 +139,8 @@ suspected, or cosmetic.
- Repro 2 (message in the list): Star sent the full map `{$notjunk, $seen, $flagged}`. That
reverts any keyword another client changed since the list loaded.
- **Verified.**
- [ ] <a id="b5"></a>**B5 · P1 · S: the viewer pager follows the live list, so new mail shifts the
page under the user.**
- [x] <a id="b5"></a>**B5 · P1 · S: the viewer pager follows the live list, so new mail shifts the
page under the user.** Fixed in `9c31de2`, `e0b7288`.
- Where: `src/screens/EmailThreadScreen.tsx:81-102`, `:639`.
- Repro:
- Open "Quarterly numbers" (row 2) and deliver 2 new messages.
@@ -145,7 +149,7 @@ suspected, or cosmetic.
Archive use the same `activeEmailId`.
- Fix: snapshot the id list when the viewer opens.
- **Verified.**
- [ ] <a id="b6"></a>**B6 · P1 · S–M: messages vanish after a delta refresh.**
- [x] <a id="b6"></a>**B6 · P1 · S–M: messages vanish after a delta refresh.** Fixed in `0a8686a`.
- Where: `src/stores/email-store.ts:1953-1990` and `:2058-2060`.
- Web: `stores/email-store.ts` (`applyEmailDeltaNow`, which runs `Email/changes` against the
list's own state).
@@ -161,8 +165,8 @@ suspected, or cosmetic.
- A pull-to-refresh (a full query) brings it back.
- Before the empty-folder visit the same test worked (`queryChanges` → `changes` → `get`).
- **Verified.**
- [ ] <a id="b7"></a>**B7 · P1 · S: with "Always send", read receipts go out for messages that were
never displayed, and a failed one leaves junk in Sent.**
- [x] <a id="b7"></a>**B7 · P1 · S: with "Always send", read receipts go out for messages that were
never displayed, and a failed one leaves junk in Sent.** Fixed in `5f3f584`, `1fb60a5`.
- Where: `src/components/email/ReadReceiptBanner.tsx:92-101` (mounted in the neighbouring pages
the pager pre-renders, `src/screens/EmailThreadScreen.tsx:641` `windowSize={3}`),
`src/api/email.ts:1456-1486`.
@@ -173,8 +177,8 @@ suspected, or cosmetic.
confirm receipt" stays in Sent. `$mdnsent` is not set and a failed auto-send clears its guard
(`autoRef.current = null`), so it can run again and import another copy.
- **Verified.**
- [ ] <a id="b8"></a>**B8 · P1 · S: scheduled send and the undo-send delay are off on Stalwart
(#57).**
- [x] <a id="b8"></a>**B8 · P1 · S: scheduled send and the undo-send delay are off on Stalwart
(#57).** Fixed in `e28e8b4`, `4e7ae2f`.
- Where: `src/api/jmap-client.ts:973-999` reads `session.capabilities[submission]`. Stalwart
only puts `maxDelayedSend` and FUTURERELEASE in `accountCapabilities`. `:919-925` (attachment
size cap) has the same problem.
@@ -183,7 +187,8 @@ suspected, or cosmetic.
→ "In 1 hour" → "Scheduling unavailable: This mail server does not support scheduled send".
- The Scheduled screen and the undo delay use the same check.
- **Verified.**
- [ ] <a id="b9"></a>**B9 · P1 · S: files shared into the app never upload.**
- [x] <a id="b9"></a>**B9 · P1 · S: files shared into the app never upload.** Fixed in `cd4ce9d`,
`3c7b96e`.
- Where: `src/api/blob.ts:99` passes the `content://` URI to `createUploadTask`, which
requires a file path. `src/lib/share-intent.ts:40` names the file after the URI's last
segment.
@@ -191,20 +196,23 @@ suspected, or cosmetic.
→ alert "Failed to upload 1000000027 … IOException: Directory for
'/external/images/media/1000000027' doesn't exist".
- **Verified.**
- [ ] <a id="b10"></a>**B10 · P1 · S: `ACTION_SENDTO mailto:` opens the inbox, not the composer.**
- [x] <a id="b10"></a>**B10 · P1 · S: `ACTION_SENDTO mailto:` opens the inbox, not the composer.**
Fixed in `06e8942`.
- Where: `android/app/src/main/java/com/anonymous/bulwarkmobile/ShareIntentStore.kt:25` only
handles SEND and SEND_MULTIPLE. RN's `Linking` forwards only VIEW. The manifest registers
SENDTO anyway.
- Repro: `am start -a android.intent.action.SENDTO -d mailto:bob@partner.example` → no
composer. The same URI with VIEW works.
- **Verified.**
- [ ] <a id="b11"></a>**B11 · P2 · S: `mailto:` links with a `+` address do nothing.**
- [x] <a id="b11"></a>**B11 · P2 · S: `mailto:` links with a `+` address do nothing.** Fixed in
`ee652d3`.
- Where: `src/lib/mailto.ts:21` turns `+` into a space, validation then fails, and the parser
returns null.
- Web: `lib/validation.ts`.
- Repro: `mailto:alice+news@partner.example` → no composer.
- **Verified.**
- [ ] <a id="b12"></a>**B12 · P1 · S: tasks can't be completed (#958).**
- [x] <a id="b12"></a>**B12 · P1 · S: tasks can't be completed (#958).** Fixed in `dcdda5e`,
`a7e4e84`.
- Where: `src/stores/calendar-store.ts:710-719` sends `progressUpdated`. The checkbox at
`src/components/calendar/TasksSheet.tsx:442` swallows the error.
- Web: `6b30e91c`, `stores/task-store.ts`.
@@ -212,35 +220,38 @@ suspected, or cosmetic.
stays `needs-action` and the UI shows no error.
- Tasks are off by default (`src/stores/settings-store.ts:381`), which limits the reach.
- **Verified.**
- [ ] <a id="b13"></a>**B13 · P1 · M: the Files tab stops at 500 nodes (#1069).**
- [x] <a id="b13"></a>**B13 · P1 · M: the Files tab stops at 500 nodes (#1069).** Fixed in
`4eb8c47`, `115e162`.
- Where: `src/api/files.ts:98-112` (`FileNode/get ids:null`, which Stalwart silently caps at
`maxObjectsInGet`). The same code runs at `:133-167`.
- Web: `lib/jmap/client.ts` (`fetchAllFileNodes`, `9893b0a8`).
- Repro: with 510 root files created before the folders, `file-0490`–`0499` are listed, while
`file-0500`–`0509` and the Documents and Photos folders (and their files) never appear.
- **Verified.**
- [ ] <a id="b14"></a>**B14 · P2 · S: bodies over 512 KB render cut off with no notice (#884).**
- [x] <a id="b14"></a>**B14 · P2 · S: bodies over 512 KB render cut off with no notice (#884).**
Fixed in `4767805`.
- Where: `src/api/email.ts:493-499` (`maxBodyValueBytes: 512000`, `isTruncated` never read).
- Web: `lib/jmap/client.ts` (`2cf7427e`, refetch of truncated bodies).
- Repro: for a 586 KB HTML report, the server returns `isTruncated: true` with 59 characters.
The viewer shows the heading, one line and "…"; the list preview shows the full text.
- **Verified.**
- [ ] <a id="b15"></a>**B15 · P1 · S: a redirect that drops the Authorization header signs the user
out on launch.**
- [x] <a id="b15"></a>**B15 · P1 · S: a redirect that drops the Authorization header signs the user
out on launch.** Fixed in `859770b`.
- Where: `src/api/jmap-client.ts:737-763`. The retry keys on `response.redirected`, which RN's
fetch never sets. An empty session then throws `AuthenticationError`, and
`src/stores/auth-store.ts:677-683` evicts the account.
- Web: `40e38d16` (#892).
- Only affects servers behind a redirecting proxy; direct Stalwart is fine.
- **Suspected.**
- [ ] <a id="b16"></a>**B16 · P1 · S: "Save draft" discards the text when identities didn't load
(offline) or there is no Drafts folder.**
- [x] <a id="b16"></a>**B16 · P1 · S: "Save draft" discards the text when identities didn't load
(offline) or there is no Drafts folder.** Fixed in `92b42e4`: the text is no longer dropped and
the iOS swipe is blocked; saving a draft offline is still impossible.
- Where: `src/screens/ComposeScreen.tsx:1208` returns early; `:1288-1290` closes anyway.
- On iOS, the `beforeRemove` listener at `:1340` doesn't stop a native swipe-down; that needs
`usePreventRemove`.
- **Suspected.**
- [ ] <a id="b17"></a>**B17 · P1 · S: calendar reminders get cancelled when the calendar view
moves.**
- [x] <a id="b17"></a>**B17 · P1 · S: calendar reminders get cancelled when the calendar view
moves.** Fixed in `4b903ed`.
- Where: `src/lib/calendar-notifications.ts:116-134` rebuilds the reminder set from the
windowed `events` and cancels everything else.
- Viewing another month drops all pending reminders. Nothing reschedules them in a session
@@ -248,8 +259,8 @@ suspected, or cosmetic.
- There is no `setNotificationHandler`, so reminders don't show while the app is in the
foreground.
- **Suspected.**
- [ ] <a id="b18"></a>**B18 · P1 · S: push can end up permanently off for users with ACL-shared
mailboxes.**
- [x] <a id="b18"></a>**B18 · P1 · S: push can end up permanently off for users with ACL-shared
mailboxes.** Fixed in `d8046a8`.
- Where: `src/lib/push-notifications.ts` (HEAD `:223-229` builds the `emailPush` map from
every account with mailboxes).
- Stalwart rejects the whole map with `forbidden` for accounts the user doesn't own and isn't
@@ -257,31 +268,34 @@ suspected, or cosmetic.
fails the same way.
- Web: `lib/web-push.ts:86-94` has the same code.
- **Suspected.**
- [ ] <a id="b19"></a>**B19 · P2 · S: "Reschedule" drops Cc and Bcc.**
- [x] <a id="b19"></a>**B19 · P2 · S: "Reschedule" drops Cc and Bcc.** Fixed in `e67947a`,
`7e598c5`.
- Where: `src/api/email.ts:1393` rebuilds recipients from `scheduled.to`;
`src/screens/ScheduledScreen.tsx:140`.
- Web: `lib/jmap/client.ts` (reuses the envelope).
- Unreachable on Stalwart until B8 is fixed.
- **Suspected.**
- [ ] <a id="b20"></a>**B20 · P2 · S: "Load more" skips messages in Unread and Starred views.**
- [x] <a id="b20"></a>**B20 · P2 · S: "Load more" skips messages in Unread and Starred views.**
Fixed in `35e0792`.
- Where: `src/stores/email-store.ts:776` uses `position: emails.length`, which counts rows that
were kept after they stopped matching the query.
- **Suspected.**
- [ ] <a id="b21"></a>**B21 · P2 · S: the event editor wipes what the user typed when a calendar push
or the alias lookup arrives.**
- [x] <a id="b21"></a>**B21 · P2 · S: the event editor wipes what the user typed when a calendar push
or the alias lookup arrives.** Fixed in `15717f1`.
- Where: `src/components/calendar/EventModal.tsx:231-273` (the reset effect depends on
`calendars` and `currentUserEmails`).
- **Suspected.**
- [ ] <a id="b22"></a>**B22 · P2 · S: an invitation's Accept sends no RSVP when the event is outside
the loaded calendar window, yet says "Added to calendar".**
- [x] <a id="b22"></a>**B22 · P2 · S: an invitation's Accept sends no RSVP when the event is outside
the loaded calendar window, yet says "Added to calendar".** Fixed in `c0582ab`.
- Where: `src/components/email/CalendarInvitationBanner.tsx:177-188`.
- **Suspected.**
- [ ] <a id="b23"></a>**B23 · P2 · S: "Send failed" can be shown for mail that was sent, so a retry
sends it twice.**
- [x] <a id="b23"></a>**B23 · P2 · S: "Send failed" can be shown for mail that was sent, so a retry
sends it twice.** Fixed in `c51a848`.
- Where: `src/api/email.ts:1178-1181` throws on any `error` response. Stalwart appends one when
`onSuccessUpdateEmail` fails after a successful submission.
- **Suspected.**
- [ ] <a id="b24"></a>**B24 · P3 · S: some writes report success when the server refused them.**
- [x] <a id="b24"></a>**B24 · P3 · S: some writes report success when the server refused them.**
Fixed in `00ad2f4` (files), `a6ef0ab` (calendar), `ddfe85a` (push).
- Where: `src/api/files.ts:199` and `:225` (rename, move, delete read `[0][1]` without checking
for an `error` response); `src/api/calendar.ts:449-459` ignores `notDestroyed`;
`src/api/push.ts:14,82,105`.
@@ -289,7 +303,8 @@ suspected, or cosmetic.
### UI glitches
- [ ] <a id="u1"></a>**U1 · P2 · S: the cold-start sequence shows a blank screen and jumps twice.**
- [x] <a id="u1"></a>**U1 · P2 · S: the cold-start sequence shows a blank screen and jumps twice.**
Fixed in `b9db376`, `f2dd3a8`.
- Where: `App.tsx:613-620`; `AppTheme` has no `windowBackground`; `PushOnboardingPrompt`.
- Frames:
- The dark splash is followed by about 1 s of blank white.
@@ -297,36 +312,38 @@ suspected, or cosmetic.
- The header then pops in and pushes the list down.
- The notification card lands over the status bar and pushes the list down again.
- **Verified** (`screenrecord`).
- [ ] <a id="u2"></a>**U2 · P2 · S: opening a message shows a skeleton, then a 120 px strip of body,
then the full body.**
- [x] <a id="u2"></a>**U2 · P2 · S: opening a message shows a skeleton, then a 120 px strip of body,
then the full body.** Fixed in `54b169c`, `ac8e3fb`.
- Where: `src/components/EmailBodyView.tsx:588-591` (the height resets to 120 on every
message).
- The header data is already in the list row but isn't used. The quick-reply box jumps as the
WebView grows. See PF1.
- **Verified.**
- [ ] <a id="u3"></a>**U3 · P2 · S: sender image and table widths are overridden.**
- [x] <a id="u3"></a>**U3 · P2 · S: sender image and table widths are overridden.** Fixed in
`7531118`.
- Where: `src/lib/email-html.ts:452-454`. `img { max-width: 100% !important }` beats inline
`max-width`.
- Web: `68341a0b`, `ab4bf91b` (#790).
- Repro: an image with `max-width:200px; max-height:134px; width:100%` renders full width and
squashed about 1.9×; a table with `max-width:300px` spans the pane.
- **Verified.**
- [ ] <a id="u4"></a>**U4 · P2 · S: RTL mail renders left-to-right.**
- [x] <a id="u4"></a>**U4 · P2 · S: RTL mail renders left-to-right.** Fixed in `910c503`.
- Where: `src/lib/email-html.ts:627` and `:686` (no `dir="auto"`).
- Web: `e2990da5`, `48048f16`.
- Repro: an Arabic plain-text body is left-aligned and its sentence-final period sits on the
wrong side.
- **Verified.**
- [ ] <a id="u5"></a>**U5 · P3 · S: list previews keep the server's newlines.**
- [x] <a id="u5"></a>**U5 · P3 · S: list previews keep the server's newlines.** Fixed in `d45c799`.
- Rows show a dangling "…" line and have uneven heights ("Q3 revenue is up 12 %.\n\n…").
- A subject that contains CR/LF also renders over several lines in the list and the viewer.
- Where: `src/screens/EmailListScreen.tsx` row.
- **Verified.**
- [ ] <a id="u6"></a>**U6 · P3 · S: the schedule sheet offers presets, then refuses them.**
- [x] <a id="u6"></a>**U6 · P3 · S: the schedule sheet offers presets, then refuses them.** Fixed in
`802b199`, `4e7ae2f`.
- Where: `src/screens/ComposeScreen.tsx:1846-1851`. The sheet should hide the presets.
- **Verified.**
- [ ] <a id="u7"></a>**U7 · P3 · S: Back in Files selection mode switches to the Mail tab instead of
clearing the selection.**
- [x] <a id="u7"></a>**U7 · P3 · S: Back in Files selection mode switches to the Mail tab instead of
clearing the selection.** Fixed in `8d7fbfc`, `4fdf39a`.
- **Verified.**
- [ ] <a id="u8"></a>**U8 · P3 · M: icon buttons and switches have no accessibility label.**
- Unlabeled controls:
@@ -338,7 +355,8 @@ suspected, or cosmetic.
- Web: `4947c6c7`.
- Repro: `uiautomator dump` shows an empty `content-desc` on all of these.
- **Verified.**
- [ ] <a id="u9"></a>**U9 · P3 · S: Android notifications use the stock envelope as their small icon.**
- [x] <a id="u9"></a>**U9 · P3 · S: Android notifications use the stock envelope as their small
icon.** Fixed in `695aa23`.
- Where: `android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkFcmModule.kt:128` and
`:181` (`android.R.drawable.ic_dialog_email`).
- **Suspected.**
@@ -348,8 +366,9 @@ suspected, or cosmetic.
All numbers come from the x86_64 emulator with a release build and a local server. Use them to
compare against each other, not as phone numbers.
- [ ] <a id="pf1"></a>**PF1 · P1 · M: opening a message takes 1.65–1.87 s from tap to a settled
screen.**
- [x] <a id="pf1"></a>**PF1 · P1 · M: opening a message takes 1.65–1.87 s from tap to a settled
screen.** Fixed in `33e303e`, `ac8e3fb`, `1348e7d`, `01fc6fb`, `6ebadf7`, `54b169c`, `7cdebc4`,
`b7a5953`; not re-timed on a device yet.
- Where: `src/screens/EmailThreadScreen.tsx:161-199` (a network-first detail fetch even for
cached ids, plus a thread fetch with full bodies), `:641` (the pager mounts 3 panes, each
with its own WebView), `src/components/EmailBodyView.tsx:588-591` (the body is sanitized
@@ -363,8 +382,11 @@ compare against each other, not as phone numbers.
- One open from the Unified Inbox made 12 requests: the body twice, the thread twice,
`Identity/get`, and a forbidden `x:Account/get`.
- **Verified** (numbers and requests); causes suspected.
- [ ] <a id="pf2"></a>**PF2 · P1 · M: cold start takes 3.6 / 3.8 / 4.1 s from launch to a settled
inbox (3 runs).**
- [x] <a id="pf2"></a>**PF2 · P1 · M: cold start takes 3.6 / 3.8 / 4.1 s from launch to a settled
inbox (3 runs).** Fixed in `604eb39`, `063432b`, `5bcfe1f`, `82ca359`, `b9db376`, `9c2f819`:
modules run before the first frame 3,590 → 1,516, Hermes bundle 9.34 → 7.61 MB, a headless push
start 202 modules (`npx expo export`). The four hydrations were already awaited together
(`85f23df`). Not re-timed on a device yet.
- Measured:
- `am start` reports 451–564 ms to the first frame (the splash).
- Cached rows at about +1.2 s.
@@ -383,7 +405,9 @@ compare against each other, not as phone numbers.
- The headless push task imports the whole app (`index.ts:10`).
- **Verified** (device timings; bundle shares measured by rebuilding the bundle with hermesc and
removing module groups one at a time).
- [ ] <a id="pf3"></a>**PF3 · P2 · M: the mail list is heavier than the emulator floor.**
- [ ] <a id="pf3"></a>**PF3 · P2 · M: the mail list is heavier than the emulator floor.** Partly:
`cb9a741`, `aa25075`, `ebd8dac` fix the inline callbacks, the swipe bands and the per-row date
formatters; the swipe drag still runs on the JS thread, and nothing was re-measured.
- Measured over already-loaded rows (14 flings): p50 21 ms, p90 32 ms, p95 38 ms, p99 57 ms.
The same flings in the system Settings list: 17 / 18 / 20 / 36 ms.
- Causes:
@@ -394,20 +418,24 @@ compare against each other, not as phone numbers.
- Row dates build two `Intl` formatters per row (`src/lib/date-format.ts:75-97`).
- The PanResponder drag runs on the JS thread (`src/components/SwipeableRow.tsx:170-172`).
- **Verified** (numbers); causes suspected.
- [ ] <a id="pf4"></a>**PF4 · P2 · S: hidden screens keep rendering.**
- [ ] <a id="pf4"></a>**PF4 · P2 · S: hidden screens keep rendering.** Partly: `4a5d837` adds
`freezeOnBlur` and a count-only badge selector; the list directly under an open message stays live
on Fabric, and `fetchMailboxes` still sets a new array on every fetch.
- Where: there is no `enableFreeze` or `freezeOnBlur` (`App.tsx:634`, `index.ts`).
- The tab navigator subscribes to the whole `mailboxes` array to compute one badge
(`App.tsx:127`), and `src/stores/email-store.ts:1855` sets a new array on every mailbox
fetch.
- So every refresh also re-renders the mail list under an open message.
- **Suspected.**
- [ ] <a id="pf5"></a>**PF5 · P2 · S–M: every store write re-serializes the whole persisted cache.**
- [x] <a id="pf5"></a>**PF5 · P2 · S–M: every store write re-serializes the whole persisted cache.**
Fixed in `48825e9`, `e37621b`.
- Where: `src/stores/email-store.ts:1723-1731` (the email cache, with the current folder
stored twice), `src/stores/contacts-store.ts:824-829`,
`src/stores/calendar-store.ts:769-775`. This includes `loading` flips; there is no debounce.
- Opening one unread message causes about 5 full writes.
- **Suspected.** The stringify cost was measured off-device: 1.25 ms for 425 rows in V8.
- [ ] <a id="pf6"></a>**PF6 · P2 · S: requests that can be cut, all seen on the wire.**
- [x] <a id="pf6"></a>**PF6 · P2 · S: requests that can be cut, all seen on the wire.** Fixed in
`d097ae9`, `668e010`, `1c7c2a3`, `018accd`, `3bf2c30`, `1196ad5`, `9fa931c`.
- Every drawer open sends 14 `Email/query` calls for tag counts.
- The Contacts tab re-downloads all cards (58 KB) on every open, although they load at
startup.
@@ -422,7 +450,8 @@ compare against each other, not as phone numbers.
- There is a 30 s `Core/echo` on top of the SSE pings (`App.tsx:581-596`).
- Backgrounding is fine: no requests during 40 s in the background.
- **Verified.**
- [ ] <a id="pf7"></a>**PF7 · P2 · S: every list page and every search is 3 sequential requests.**
- [x] <a id="pf7"></a>**PF7 · P2 · S: every list page and every search is 3 sequential requests.**
Fixed in `d987930`, `4f3940f`, `9c2f819`.
- The chain is `Email/query` → `Email/get` → `Thread/get` (the last one fired after render).
First login adds two `Mailbox/get` and a 500 ms gap.
- Where: `src/stores/email-store.ts:2049-2060`, `src/screens/EmailListScreen.tsx:339-358`.
@@ -430,8 +459,8 @@ compare against each other, not as phone numbers.
`#ids /list/*/threadId` in one request). RN's `src/api/unified-inbox.ts:383-397` already
chains query and get.
- **Verified.**
- [ ] <a id="pf8"></a>**PF8 · P2 · M: each keystroke in the composer re-renders the 3,000-line
screen twice.**
- [x] <a id="pf8"></a>**PF8 · P2 · M: each keystroke in the composer re-renders the 3,000-line
screen twice.** Fixed in `fc00781`.
- Where: `src/lib/editor-html.ts:160-168` posts the full `innerHTML` on every input and on every
selection change; `src/screens/ComposeScreen.tsx:2276-2277`.
- `htmlToPlainText` and a `JSON.stringify` snapshot also run on every render.
@@ -441,7 +470,9 @@ compare against each other, not as phone numbers.
- Contents: 25.85 MB of unminified dex, 24,621 classes; the ML Kit barcode scanner accounts for
5.83 MB. The UnifiedPush work in progress adds 3,009 classes (Tink, protobuf).
- **Verified** (APK inspection of the published 0.1.62).
- [ ] <a id="pf10"></a>**PF10 · P3 · S: slow paths that need large data to show.**
- [ ] <a id="pf10"></a>**PF10 · P3 · S: slow paths that need large data to show.** Partly: contacts
search (`e6be892`) and bulk read/star (`cb2f03b`) are fixed; upload progress and the offline-cache
index writes are unchanged.
- Contacts search re-sorts with `localeCompare` on every keystroke
(`src/stores/contacts-store.ts:923-925`).
- Bulk mark-read and star send N requests against a concurrency limit of 4
@@ -458,12 +489,14 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
### Auth and accounts
- [ ] **P2 · S · missing: adding a second OAuth account silently signs in the same user again.**
- [x] **P2 · S · missing: adding a second OAuth account silently signs in the same user again.**
Fixed in `ab84578`.
- Where: `src/lib/oauth-native.ts:127-141` (no `prompt=select_account`, no ephemeral browser
session).
- Web: `5771a213`.
- **Suspected.**
- [ ] **P3 · S · partial: a pasted `…/jmap/session` URL fails discovery with a 404.**
- [x] **P3 · S · partial: a pasted `…/jmap/session` URL fails discovery with a 404.** Fixed in
`bd42206`.
- Where: `src/lib/server-discovery.ts:59-63`.
- Web: `b26c1746` (#971).
- **Suspected.**
@@ -471,37 +504,43 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
### Mail list, folders and search
- [ ] **P2 · L · diverges: "All folders" search only covers the open folder's account (#1082).**
- [x] **P2 · L · diverges: "All folders" search only covers the open folder's account (#1082).**
Fixed in `ee50418`, `edab181`, `d2066da`.
- Where: `src/stores/email-store.ts:384-389`.
- Web: `lib/unified-mailbox.ts` (`searchAcrossAccounts`, `9ef7068b`).
- Repro: "zephyr" with All folders → `Email/query` on account `c` only → 3 hits. The 5 matches
in the team inbox are missing.
- **Verified.**
- [ ] **P2 · M · diverges: the tag view and tag badges only cover one account (#1038).**
- [x] **P2 · M · diverges: the tag view and tag badges only cover one account (#1038).** Fixed in
`8d5bced`, `d097ae9`.
- Where: `src/stores/email-store.ts:209` and `:387`, `src/components/SidebarDrawer.tsx:426-436`.
- Web: `stores/email-store.ts` (`fetchTagEmails`, `fedfc89d`).
- Repro: 2 own and 2 team messages tagged Red → the Red view shows 2.
- **Verified.**
- [ ] **P3 · S · missing: folders created on the phone are not subscribed.**
- [x] **P3 · S · missing: folders created on the phone are not subscribed.** Fixed in `30aea63`.
- Where: `src/api/email.ts:211-221` sends no `isSubscribed: true`, so IMAP clients hide them.
- Web: `2b186910` (#951).
- **Suspected.**
- [ ] **P3 · S · diverges: archiving to a role-less "Archives" folder is offered but does nothing.**
- [x] **P3 · S · diverges: archiving to a role-less "Archives" folder is offered but does nothing.**
Fixed in `9f75e70`, `ad1f053`.
- Where: the list matches by name (`src/screens/EmailListScreen.tsx:393-395`), but the store
needs the role or the exact name (`src/stores/email-store.ts:1202-1205`).
- Web: `2b32a011` (#578).
- **Suspected.**
- [ ] **P3 · S · missing: no unread badge on the app icon.**
- [ ] **P3 · S · missing: no unread badge on the app icon.** Partly: `25c0883` badges the icon on
iOS; Android is left to the launcher (see the fix-pass decisions).
- expo-notifications `setBadgeCountAsync` is already available.
- Web: `hooks/use-app-badge.ts` (`e7d37823`).
- **Suspected.**
- [ ] **P3 · S · missing: list rows show no attachment chips.**
- [x] **P3 · S · missing: list rows show no attachment chips.** Fixed in `b1fb2e9`, `edab181` (not
yet on Unified Inbox rows).
- Web: `0e1c34cd`.
- **Suspected.**
- [ ] **P3 · S · missing: no "clear search when switching folders" setting.**
- [x] **P3 · S · missing: no "clear search when switching folders" setting.** Fixed in `4e64261`.
- Web: `12f01409`.
- **Suspected.**
- [ ] **P3 · S · diverges: the Unified section shows even when it can't be populated.**
- [x] **P3 · S · diverges: the Unified section shows even when it can't be populated.** Fixed in
`af6290f`.
- Where: `src/components/SidebarDrawer.tsx:639`.
- Web: `f3f0127c` (#843).
- **Suspected.**
@@ -510,34 +549,36 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
### Viewer
- [ ] **P2 · M · missing: no per-message HTML/plain-text toggle, and reopened drafts are forced into
the global format.**
- [x] **P2 · M · missing: no per-message HTML/plain-text toggle, and reopened drafts are forced into
the global format.** Fixed in `472c4d8`.
- Where: `src/screens/ComposeScreen.tsx:531-536`. With plain text on, an HTML draft is
flattened on its first autosave.
- Web: `f6ad8a59` (#1022), `components/email/compose-format.ts`.
- **Suspected.**
- [ ] **P2 · S · missing: sender avatars ignore address-book photos (#54).**
- [x] **P2 · S · missing: sender avatars ignore address-book photos (#54).** Fixed in `eca3871`.
- Web: `components/ui/avatar.tsx` (looks up the sender's contact photo by email; the photo wins
over favicons and initials).
- Repro: Alice Nguyen's contact photo shows in Contacts; her mail shows "AN" initials.
- **Verified.**
- [ ] **P2 · S · diverges: the viewer's Spam button is a plain move.**
- [x] **P2 · S · diverges: the viewer's Spam button is a plain move.** Fixed in `5e42c67`.
- It sends no `$junk`/`$notjunk` flip and doesn't honour trash-and-read.
- Where: `src/screens/EmailThreadScreen.tsx:395-407`. The list and swipe paths are right
(`src/stores/email-store.ts:1058-1146`).
- Web: `150c25ca` (#695).
- **Suspected.**
- [ ] **P2 · S · diverges: viewer Archive silently does nothing for messages that are not in the
loaded list.**
- [x] **P2 · S · diverges: viewer Archive silently does nothing for messages that are not in the
loaded list.** Fixed in `33d2674`.
- This covers the unified inbox, notification taps and deep links.
- Where: `src/stores/email-store.ts:1196-1197`.
- Web: `150c25ca`.
- **Suspected.**
- [ ] **P3 · S · partial: failed actions and sends give no feedback.**
- [x] **P3 · S · partial: failed actions and sends give no feedback.** Fixed in `9e95273`,
`cd911ad`.
- The store actions are called with `void`, and there is no "Email sent" toast.
- Web: `ec67ab99`, `b03a0c1d`, `lib/email-action-toast.ts`.
- **Verified** (no toast after send); failure path suspected.
- [ ] **P3 · S · missing: body-embedded `cid:` parts typed octet-stream appear as nameless chips.**
- [x] **P3 · S · missing: body-embedded `cid:` parts typed octet-stream appear as nameless chips.**
Fixed in `1d1d6a5`.
- Where: `src/lib/attachment-display.ts:91-104`.
- Web: `1c00812a`.
- **Suspected.**
@@ -545,26 +586,29 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
### Composer
- [ ] **P2 · S · diverges: replies go out from the default identity unless "Reply from received
address" is on.**
- [x] **P2 · S · diverges: replies go out from the default identity unless "Reply from received
address" is on.** Fixed in `a0c2bd7`.
- Forward also rewrites the From address, and replies to your own sent mail resolve from the
other party.
- Where: `src/screens/ComposeScreen.tsx:836-845`, `src/lib/reply-identity.ts:46-77,170`.
- Web: `4fc7b8b5`, `lib/reply-identity.ts`.
- **Suspected.**
- [ ] **P3 · S · missing: a new message in a shared folder doesn't default to that mailbox's
identity.**
- [x] **P3 · S · missing: a new message in a shared folder doesn't default to that mailbox's
identity.** Fixed in `0ffdcac`.
- Where: `findComposeIdentityId` is ported but never called.
- Web: `129f545a`.
- **Suspected.**
- [ ] **P3 · S · missing: no exact-address vs same-domain choice for "reply from received".**
- [x] **P3 · S · missing: no exact-address vs same-domain choice for "reply from received".** Fixed
in `2d907ff`.
- Web: `565d5c10` (#1000).
- **Suspected.**
- [ ] **P3 · M · partial: no font-size picker or background colour; 8 text colours instead of 16.**
Partly: `548b647` adds the background colour and the 16 text colours; the font-size picker is not
ported.
- Where: `src/components/RichTextEditor.tsx:8-26`.
- Web: `7e0a86a6`, `3957b358`.
- **Suspected.**
- [ ] **P3 · S · diverges: quick replies carry no signature.**
- [x] **P3 · S · diverges: quick replies carry no signature.** Fixed in `3faac98`.
- Where: `src/components/email/QuickReplyBox.tsx:79-90`.
- **Suspected.**
- [ ] **P3 · S · partial: the undo-send snackbar exists only on the list screen.**
@@ -575,55 +619,58 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
### Calendar
- [ ] **P2 · S · missing: CalDAV tasks with `COMPLETED`, `NEEDS_ACTION` or `canceled` count as
pending and toggle the wrong way.**
- [x] **P2 · S · missing: CalDAV tasks with `COMPLETED`, `NEEDS_ACTION` or `canceled` count as
pending and toggle the wrong way.** Fixed in `1b64c14`.
- Web: `5f1d2795` (`normalizeCalendarTask`).
- **Suspected.**
- [ ] **P3 · L · missing: no free-scrolling month, week or day views (#759) and no setting for them.**
- [x] **P3 · L · missing: no free-scrolling month, week or day views (#759) and no setting for
them.** Fixed in `caf61ec`, `de3d697`.
- Where: `src/screens/CalendarScreen.tsx:89,169-171,360-374`.
- Web: `15a5497a`, `ae403b06`, `hooks/use-scroll-window.ts`.
- **Suspected.**
- [ ] **P3 · M · missing: the agenda is a fixed 30-day page instead of infinite scroll.**
- [x] **P3 · M · missing: the agenda is a fixed 30-day page instead of infinite scroll.** Fixed in
`71522b9`.
- Where: `src/components/calendar/AgendaView.tsx:46`.
- Web: `60e05c00`.
- **Suspected.**
- [ ] **P3 · L · diverges: recurring events are always expanded client-side.**
- [x] **P3 · L · diverges: recurring events are always expanded client-side.** Fixed in `8454889`,
`f397ee3`, `96c5a3f`.
- Where: `src/api/calendar.ts:267-304`, `src/lib/recurrence-expansion.ts`.
- This matches the webmail's pre-0.16.20 fallback, but there is no server-side expansion and no
per-occurrence edit through synthetic ids.
- Web: `lib/recurrence-instances.ts`.
- **Suspected.**
- [ ] **P3 · S · partial: participants aren't deduplicated or matched to contact names, and the
organizer's status dot is wrong.**
- [x] **P3 · S · partial: participants aren't deduplicated or matched to contact names, and the
organizer's status dot is wrong.** Fixed in `1d755fc`.
- Where: `src/lib/calendar-participants.ts:130-167`.
- Web: `8cbc5bc5`, `6f99c017`.
- **Suspected.**
- [ ] **P3 · S · diverges: a task's due time shows the raw Europe/Berlin wall time (17:00), while
events are converted to the device zone.**
- [x] **P3 · S · diverges: a task's due time shows the raw Europe/Berlin wall time (17:00), while
events are converted to the device zone.** Fixed in `6a975b8`, `1a651a9`, `a8e6f6e`.
- **Suspected.**
- Links in event descriptions are fixed on GitHub `main` (`c2584c2`) but not in this checkout.
- Also see B12, B17, B21, B22.
### Contacts
- [ ] **P2 · S · partial: the vCard FN (`name.full`) is missing or stale on form, "add sender" and
import writes (#430).**
- [x] **P2 · S · partial: the vCard FN (`name.full`) is missing or stale on form, "add sender" and
import writes (#430).** Fixed in `a734d0a`.
- Where: `src/screens/ContactFormScreen.tsx:288-293`, `src/lib/vcard.ts:575-603`.
- Web: `a210b22e`.
- **Suspected.**
- [ ] **P3 · S · missing: no sort by last name (#963).**
- [x] **P3 · S · missing: no sort by last name (#963).** Fixed in `1827748`.
- Where: `src/stores/contacts-store.ts:923-925`.
- Web: `fb24ef5d`.
- **Suspected.**
- [ ] **P3 · S · missing: a new contact made while viewing address book X lands in the default
book.**
- [x] **P3 · S · missing: a new contact made while viewing address book X lands in the default
book.** Fixed in `c888e95` (groups: `a3193eb`).
- Where: `src/screens/ContactsScreen.tsx:238,396`.
- Web: `494adb9b`.
- **Suspected.**
### Files
- [ ] **P2 · S · diverges: sharing with other users is never offered.**
- [x] **P2 · S · diverges: sharing with other users is never offered.** Fixed in `caea3d0`.
- Where: `src/api/files.ts:30-35` and `src/components/calendar/CalendarShareSheet.tsx:86` gate
on `principals:owner`, which Stalwart advertises nowhere. The session only has `principals`
and `principals:availability`.
@@ -631,44 +678,47 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
- Repro: the per-file menu shows Preview/Share (OS), Download, Move, Duplicate, Rename,
Delete, and no "Share with…".
- **Verified.**
- [ ] **P3 · M · missing: no in-app file preview.**
- [x] **P3 · M · missing: no in-app file preview.** Fixed in `409cdb1` (images, text, and PDFs on
iOS; Android PDFs, audio and video still go to another app).
- Tapping a file downloads it and opens the OS "Open with" chooser.
- **Verified.**
- Also see B13 (500-node cap). WOPI editing (#425) is listed under open questions.
### Filters and vacation
- [ ] **P2 · S · missing: "Keep" is still written as `keep;`.**
- [x] **P2 · S · missing: "Keep" is still written as `keep;`.** Fixed in `f63d739`.
- Saving filters on the phone therefore undoes the webmail fix for allow-list rules.
- Where: `src/lib/sieve/generator.ts:131-132`.
- Web: `62465e1e` (#1027).
- **Suspected.**
- [ ] **P3 · M · missing: no filters or vacation for shared/group accounts.**
- [x] **P3 · M · missing: no filters or vacation for shared/group accounts.** Fixed in `a86cd1d`.
- Where: `src/api/sieve.ts:20-23`, `src/api/vacation.ts:26-27`.
- **Suspected.**
### Settings, themes and i18n
- [ ] **P2 · M · partial: locale catalogs are stale.**
- [x] **P2 · M · partial: locale catalogs are stale.** Fixed in `55f1ec4`, `008bff2`, `67935d2`.
- Last synced 2026-08-29 (`990cd84`): 2,959 keys against the webmail's 3,065. The German
update changed 59 values.
- `email_viewer.forward_as_attachment` is still English in about 25 locales.
- The 567 RN-only keys are English in every locale (`src/i18n/index.ts:98-100`).
- Fix: run `scripts/sync-locales.mjs` and translate the RN overlay.
- **Suspected.**
- [ ] **P3 · S · partial: the Reading pane hard-codes English.**
- [x] **P3 · S · partial: the Reading pane hard-codes English.** Fixed in `2511df1`.
- Labels such as "Message Spacing", "Read Receipts" and "Always send" don't go through `t()`
(`src/components/settings/ReadingSettings.tsx:305-327`).
- **Verified.**
- [ ] **P3 · S · diverges: plural forms probably fall back to English rules.**
- [x] **P3 · S · diverges: plural forms probably fall back to English rules.** Fixed in `e6549d3`.
- Where: `src/i18n/format.ts:18-47`. Hermes on Android has no `Intl.PluralRules`, so Russian
shows "2 писем".
- **Suspected.**
- [ ] **P3 · M · missing: only 4 of the webmail's 7 built-in themes, and no "Flat fields".**
- [ ] **P3 · M · missing: only 4 of the webmail's 7 built-in themes, and no "Flat fields".** Partly:
`7964e64` adds Roundcube Elastic and Aurora Glass; "Flat fields" is not ported (a product
decision).
- Where: `src/theme/builtin-themes.ts`.
- Web: `ab67d9b0`, `lib/theme-flat-fields.ts`.
- **Suspected.**
- [ ] **P3 · M · missing: no settings search.**
- [x] **P3 · M · missing: no settings search.** Fixed in `46e285b`.
- Web: `lib/settings-search.ts`.
- **Suspected.**
- [ ] **P3 · L · diverges: native still uses Lucide icons; the webmail moved to Tabler (`d7548a4b`).**
@@ -677,27 +727,28 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
### Push
- [ ] **P1 · S · diverges: tapping a group-mailbox notification opens the message against the wrong
account.**
- [x] **P1 · S · diverges: tapping a group-mailbox notification opens the message against the wrong
account.** Fixed in `8618abf`.
- The tap payload carries only the registry account, not the JMAP account.
- Where: `src/lib/push-background-task.ts:448-460`, `App.tsx:85-89`.
- Web: `b4f12b67` (#839).
- **Suspected.**
- [ ] **P2 · S · diverges: `emailPush` is never read back, so the subscription is re-patched on
every launch.**
- [x] **P2 · S · diverges: `emailPush` is never read back, so the subscription is re-patched on
every launch.** Fixed in `ccaa24e`.
- `using` also lacks the emailpush capability.
- Where: `src/api/push.ts:11` (`PushSubscription/get` with default properties).
- Web: `lib/jmap/client.ts` (explicit properties).
- **Suspected.**
- [ ] **P2 · S · diverges: revoking a device or disabling one account doesn't stick.**
- [x] **P2 · S · diverges: revoking a device or disabling one account doesn't stick.** Fixed in
`a3d3af7`.
- Setup re-runs for the active account on every launch (HEAD `App.tsx:447-453`).
- **Suspected.**
- UnifiedPush (#44, #48) exists only as uncommitted work in this tree. Also see B18.
### Security
- [ ] <a id="s1"></a>**S1 · P0 · S · missing: the GHSA-w38p fix (read-receipt header injection) was
never ported.**
- [x] <a id="s1"></a>**S1 · P0 · S · missing: the GHSA-w38p fix (read-receipt header injection) was
never ported.** Fixed in `cb6ddd6`.
- Where: `src/lib/mdn.ts:77-81` (`encodeHeaderWord` passes pure-ASCII values, CR/LF
included, through raw), `:111-113`, `:140-141`.
- Web: `lib/mdn.ts` (`stripHeaderControls`, `headerAtom`; `53a6670e`).
@@ -709,7 +760,9 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
- With B7 this becomes zero-click: the malicious message only needs to sit next to one the
user opens.
- **Verified.**
- [ ] <a id="s2"></a>**S2 · P0 · S–M: releases are signed with the public React Native debug key.**
- [x] <a id="s2"></a>**S2 · P0 · S–M: releases are signed with the public React Native debug key.**
Fixed in `6f0abb4`, `746572c`, `5237848`; the release key, rotation lineage and secrets still have
to be created before the next release (`docs/android-release.md`).
- Where: `android/app/build.gradle:103-125`; `.github/workflows/release-android.yml:69-91` falls
back to it silently when `ANDROID_KEYSTORE_BASE64` is empty; `android/app/build.gradle:98`
(`versionCode 1`).
@@ -721,24 +774,27 @@ wrongly). Items already listed under Bugs are only cross-referenced here.
- Fix: a private key, a workflow that fails without it, v3 key rotation for existing
installs, and a `versionCode` derived from `VERSION`.
- **Verified.**
- [ ] **P2 · S · diverges: "Sync trusted senders to address book" trusts every contact in every
book.**
- [x] **P2 · S · diverges: "Sync trusted senders to address book" trusts every contact in every
book.** Fixed in `d89e331`.
- Remote images and tracking pixels then load for any contact, including a spoofed From.
- The toggle switches itself on the first time the Content & Senders pane is opened on a
contacts-capable account (`src/components/settings/ContentSendersSettings.tsx:54-57`).
- Where: `src/components/EmailBodyView.tsx:533-544,562-566`.
- Web: `components/email/email-viewer.tsx` (only the Trusted Senders book counts).
- **Suspected.**
- [ ] **P2 · S · missing: replying or forwarding loads the remote images the viewer blocked.**
- [x] **P2 · S · missing: replying or forwarding loads the remote images the viewer blocked.** Fixed
in `235f4ea`.
- The editor page has no CSP (`src/components/RichTextEditor.tsx:223-253`,
`src/lib/editor-html.ts:29-35`).
- **Suspected.**
- [ ] **P2 · S · diverges: the webmail-to-app sign-in hand-off puts the password in the
`bulwarkmobile://…#password=` fragment.**
`bulwarkmobile://…#password=` fragment.** Open: needs a one-time code exchange on the webmail
side; no app change in this pass.
- Where: `src/lib/oauth.ts:139-146`. The webmail side is `app/(main)/[locale]/login/page.tsx`.
- **Suspected.**
- [ ] **P3 · S · diverges: list and notification favicons are fetched from icons.duckduckgo.com on
the device.**
the device.** Partly: `95c24a4` makes notifications honour the Sender Favicons switch; with it on
(still the default) the phone fetches from DuckDuckGo.
- This leaks sender domains and the device IP to a third party.
- Where: `src/lib/avatar-utils.ts:138-139`.
- Web: server-side `/api/favicon`.
@@ -753,34 +809,38 @@ spot-check covered 35 ticks plus the ones re-tested on the device.
### Ticked, but broken
- [ ] `docs/parity/03-email-viewer.md:107`, read receipts: sent unsanitized, and sent for unseen
neighbour panes (S1, B7). **Verified.**
- [ ] `docs/parity/02-mail-list-folders.md:112` and `docs/parity/03-email-viewer.md:15`, pager and
- [x] `docs/parity/03-email-viewer.md:107`, read receipts: sent unsanitized, and sent for unseen
neighbour panes (S1, B7). **Verified.** Fixed in `cb6ddd6`, `5f3f584`.
- [x] `docs/parity/02-mail-list-folders.md:112` and `docs/parity/03-email-viewer.md:15`, pager and
wrong-message fixes: the body is right, but actions still hit the wrong account and message
(B3, B5). **Verified.**
- [ ] `docs/parity/05-calendar.md:268`, task edit/toggle: completing fails (B12). **Verified.**
- [ ] `docs/parity/08-settings-push-i18n-ui.md:297`, deep links, mailto and share targets: SENDTO,
shared files and `+` addresses all fail (B9–B11). **Verified.**
- [ ] `docs/parity/07-filters-vacation-files.md:177` ("Verified at parity: `FileNode/get
(B3, B5). **Verified.** Fixed in `5c7301b`, `9c31de2`.
- [x] `docs/parity/05-calendar.md:268`, task edit/toggle: completing fails (B12). **Verified.**
Fixed in `dcdda5e`.
- [x] `docs/parity/08-settings-push-i18n-ui.md:297`, deep links, mailto and share targets: SENDTO,
shared files and `+` addresses all fail (B9–B11). **Verified.** Fixed in `cd4ce9d`, `06e8942`,
`ee652d3`.
- [x] `docs/parity/07-filters-vacation-files.md:177` ("Verified at parity: `FileNode/get
ids:null`"): capped at 500 (B13). Its claim that `/query` drops folders has been outdated since
Stalwart 0.16.6. **Verified.**
- [ ] `docs/parity/02-mail-list-folders.md:244`, search all folders (#788): scope is one account
(#1082 above). **Verified.**
- [ ] `docs/parity/02-mail-list-folders.md:187`, `$junk`/`$notjunk` flip: the viewer's buttons
still do a plain move. **Suspected.**
- [ ] `docs/parity/01-auth-accounts.md:57` and `docs/parity/09-jmap-core-sync-security.md:99`,
redirect with a dropped Authorization header (B15). **Suspected.**
- [ ] `docs/parity/04-composer-send.md:12`, draft autosave: loses text offline, and iOS swipe-down
bypasses the dialog (B16). **Suspected.**
- [ ] `docs/parity/04-composer-send.md:224`, reschedule: drops Cc and Bcc (B19). **Suspected.**
- [ ] `docs/parity/05-calendar.md:261`, reminders: cancelled when the view moves (B17).
**Suspected.**
- [ ] `docs/parity/06-contacts.md:259`, trusted-senders book: trusts every contact (Security).
**Suspected.**
- [ ] `docs/parity/08-settings-push-i18n-ui.md:139` and `:154`, push types and the `emailPush`
filter: never read back, and breaks for ACL-shared accounts (B18). **Suspected.**
Stalwart 0.16.6. **Verified.** Fixed in `4eb8c47`.
- [x] `docs/parity/02-mail-list-folders.md:244`, search all folders (#788): scope is one account
(#1082 above). **Verified.** Fixed in `ee50418`.
- [x] `docs/parity/02-mail-list-folders.md:187`, `$junk`/`$notjunk` flip: the viewer's buttons
still do a plain move. **Suspected.** Fixed in `5e42c67`.
- [x] `docs/parity/01-auth-accounts.md:57` and `docs/parity/09-jmap-core-sync-security.md:99`,
redirect with a dropped Authorization header (B15). **Suspected.** Fixed in `859770b`.
- [x] `docs/parity/04-composer-send.md:12`, draft autosave: loses text offline, and iOS swipe-down
bypasses the dialog (B16). **Suspected.** Fixed in `92b42e4`.
- [x] `docs/parity/04-composer-send.md:224`, reschedule: drops Cc and Bcc (B19). **Suspected.**
Fixed in `e67947a`, `7e598c5`.
- [x] `docs/parity/05-calendar.md:261`, reminders: cancelled when the view moves (B17).
**Suspected.** Fixed in `4b903ed`.
- [x] `docs/parity/06-contacts.md:259`, trusted-senders book: trusts every contact (Security).
**Suspected.** Fixed in `d89e331`.
- [x] `docs/parity/08-settings-push-i18n-ui.md:139` and `:154`, push types and the `emailPush`
filter: never read back, and breaks for ACL-shared accounts (B18). **Suspected.** Fixed in
`ccaa24e`, `d8046a8`.
- [ ] `docs/parity/02-mail-list-folders.md:217`, date-locale: the tick claims a `dateLocale` setting
that doesn't exist. **Suspected.**
that doesn't exist. **Suspected.** Open: there is still no `dateLocale` setting.
### Unticked, but done or obsolete
@@ -789,16 +849,16 @@ spot-check covered 35 ticks plus the ones re-tested on the device.
| Item | Where it landed |
|---|---|
| `01:109` remove a non-active account | `6ae21d5`, `490355c` |
| `01:114` switcher error state | |
| `01:129` account cap | now 10, `src/lib/account-utils.ts:4` |
| `01:139` unified shared-account filter | |
| `02:163` permanent-delete confirm | `src/screens/EmailThreadScreen.tsx:361-386` |
| `02:168` "never" mark-as-read | |
| `04:256` mailto/share intents | Android; broken per B9–B11 |
| `06:200` add sender to contacts | |
| `06:281` auto-trust reply recipients | |
| `07:164` quota | shown in Account settings |
| `09:131` contact state changes for shared accounts | |
| `01:114` switcher error state | `6ae21d5` |
| `01:129` account cap | now 10, `src/lib/account-utils.ts:4` (`6ae21d5`) |
| `01:139` unified shared-account filter | `6ae21d5` |
| `02:163` permanent-delete confirm | `src/screens/EmailThreadScreen.tsx:361-386` (`d2ed27f`) |
| `02:168` "never" mark-as-read | `d2ed27f` |
| `04:256` mailto/share intents | Android; broken per B9–B11 (`5802cae`; fixed in `cd4ce9d`, `06e8942`, `ee652d3`) |
| `06:200` add sender to contacts | `d2ed27f` |
| `06:281` auto-trust reply recipients | `5b72c4d` |
| `07:164` quota | shown in Account settings (`490355c`) |
| `09:131` contact state changes for shared accounts | `26a34d5` |
- **Obsolete (drop them):**
- `05:91` natural-language event input: the webmail has no parser.
- `06:294` print contact: not applicable on mobile.
@@ -815,12 +875,12 @@ spot-check covered 35 ticks plus the ones re-tested on the device.
| Issue | Status after this audit |
|---|---|
| #57 scheduled sending unsupported | Root cause found and reproduced (B8). |
| #54 contact photos not used as avatars | Reproduced (Viewer, "sender avatars"). |
| #57 scheduled sending unsupported | Root cause found and reproduced (B8). Fixed in `e28e8b4`. |
| #54 contact photos not used as avatars | Reproduced (Viewer, "sender avatars"). Fixed in `eca3871`. |
| #50 crash opening any mail | Not reproduced on 0.1.62; likely fixed by `f228257`. Ask the reporter, then close. |
| #47 account security not accessible | Fixed: Security → Account Security works on 0.16.23. Close. |
| #46 email preview shows CSS | Not reproduced: HTML-only mail with `<style>` shows clean previews, and the viewer routes by type (`06742ef`). Ask for a sample, then close. |
| #44, #48 push without FCM / UnifiedPush | Uncommitted work in this tree; not in any release. |
| #44, #48 push without FCM / UnifiedPush | Uncommitted work in this tree; not in any release. Committed in `ee04e95`; not released yet. |
| #3 TLS client auth | Partial: Android has a certificate picker (Settings → Security); iOS doesn't. |
| #58 encryption, #52 widgets, #51 IMAP/SMTP, #34 sync adapters, #1 settings sync | Feature requests; see open questions. |
@@ -842,7 +902,7 @@ No open webmail issue is about the native app. The six mobile-titled ones (#813,
This must be decided before any store listing, because changing it later means a new app.
- **Read receipts and the pager:** with neighbour pages pre-rendered, should "Always send" wait
for the page to be visible for some time, or apply only to the message that was actually
opened?
opened? Decided in `5f3f584`: only the page on screen auto-sends, once per message per session.
- **Settings sync (#1):** needs a server-side store, which neither client has today.
- **Scope of the feature requests:** encryption (#58), widgets (#52), IMAP/SMTP (#51) and native
sync adapters (#34).