Linus Rath 00619751d7 fix: never send a rescheduled message twice and handle partial refusals
"Send now" and "Reschedule" at a full submission quota (Stalwart 1.0)
made room and then created the replacement again. Making room can take
a while - a prune, a walk through the change log - and the original
could go out meanwhile, so the replacement sent it a second time, with
a HOLDFOR that still counted from before the wait. The original is now
read again first: nothing is resubmitted unless it is still pending
("This message was already sent" or "no longer scheduled" otherwise,
and the undo bar lets go of it), a failed read changes nothing, and the
hold is worked out again from the time the user picked. Port of the
webmail's 65eb4b10.

- the Stalwart explanation of a full quota (Bulwark removed what it
  could, an administrator has to raise the limit) needs
  `urn:stalwart:jmap` on the submission account; another server's
  `overQuota` gets a generic sentence with its own description
- when making room finds nothing that can go, the account is not
  searched again for five minutes; each send, reschedule and automatic
  read receipt at a full quota used to run the prune, the change-log
  walk and the gets again
- unsubscribing by mail at a full quota explains the quota instead of
  only saying "Unable to unsubscribe"
- push on Stalwart 1.0 (`principals:owner` on the account) takes the
  emailpush capability as the answer to which accounts notify: a group
  with only mail no longer counts as unreachable, and a refusal
  remembered under 0.16 no longer keeps a group out of the map
- a push re-registration the user asked for at the server's
  subscription limit removes the old record first and tries once more;
  the old record counted against the limit, so it could never be
  replaced
- "Mark folder as read" on a folder and its subfolders goes on past a
  folder the server refuses and reports the refusal at the end; the
  folders and the list refresh either way
- marking, starring or tagging a selection writes every account before
  reporting a refusal and shows only the messages the server updated;
  one refusing account or message used to leave the rest unwritten,
  and what was written looked unchanged
- a new photo for a contact opened from the cache is not saved when the
  card's media can't be read from the server either; it would have
  replaced the card's other media (a logo, more photos)
2026-09-25 23:51:39 +02:00
2026-09-24 22:18:42 +02:00

Bulwark Webmail

Bulwark Mobile

Beta - work in progress. Many features are unfinished or rough. Expect bugs, missing functionality, and breaking changes between releases. Do not rely on this for primary email yet.

React Native (Expo SDK 54) client for Bulwark Webmail - a JMAP-based mail, calendar, and contacts app.

What works today

  • Sign in to any JMAP server (e.g. Stalwart)
  • Multiple accounts
  • Email list, threads, compose
  • Calendar (basic)
  • Contacts (basic)
  • Push notifications via the Bulwark relay - FCM by default, or UnifiedPush (e.g. ntfy) for devices without Google Play services
  • In-app sideload updates from GitHub Releases

What's missing or rough

  • iOS builds, but push notifications and client certificates are Android-only so far
  • S/MIME, plugins, themes - UI stubs only (filters & rules, the vacation responder and file storage are real implementations)
  • Calendar editing is partial; contacts editing is basic
  • No Play Store distribution yet (sideload APK from Releases); iOS ships via TestFlight

Run locally

npm install
npx expo start

Then press a for Android, i for iOS, or scan the QR with Expo Go.

For release APK builds and signing see docs/android-release.md. For iOS builds and TestFlight distribution see docs/ios-release.md.

License

AGPL-3.0-only. See LICENSE.

S
Description
Bulwark Mobile: React Native client for iOS and Android. Mail, calendar and contacts over JMAP. Beta.
Readme AGPL-3.0
30 MiB
Languages
TypeScript 95.9%
Kotlin 2.1%
JavaScript 0.9%
Swift 0.4%
HTML 0.4%
Other 0.2%