Commit Graph
1416 Commits
Author SHA1 Message Date
Linus Rath 423fe28b0b Merge branch 'main' into feat/do-ci-fix 2026-11-03 19:50:46 +01:00
Linus Rath ac2f7cba2b test: allow keystore as the one bare plugin permission, since it has no actions and the S/MIME manifest uses the name 2026-11-03 19:49:56 +01:00
Linus Rath 529d334496 Merge branch 'fix/ui-bug-hunt' 2026-11-03 19:44:46 +01:00
Linus Rath 1e9afb733a fix: let the Docker image build see the script its Next config imports 2026-11-03 19:19:35 +01:00
Linus Rath 5849f9dc42 Merge branch 'main' into feat/desktop-offline 2026-11-03 19:07:04 +01:00
Linus Rath 151cedf992 Merge branch 'feat/do-app-lock' into feat/desktop-offline 2026-11-03 18:46:05 +01:00
Linus Rath deb73cc6e2 Merge branch 'feat/do-native-ui' into feat/desktop-offline 2026-11-03 18:28:49 +01:00
Linus Rath 335e1edc10 Merge branch 'feat/desktop-offline' into feat/do-app-lock
# Conflicts:
#	components/settings/general-settings.tsx
#	hooks/use-calendar-alerts.ts
#	lib/desktop/app-lock-state.ts
#	lib/desktop/policy-schema.ts
#	lib/desktop/types.ts
2026-11-03 17:52:30 +01:00
Linus Rath 3d5b02b75f fix: stop a page reporting once consent goes, and let nothing on disk aim the desktop app's telemetry
A review found that an open page kept sending to a DSN after consent was
taken back, that a state file or config file could name where telemetry
goes, and that `BULWARK_TELEMETRY=off` did nothing on the desktop build.
A due heartbeat now waits a random 30 s to 10 min, a refusal from the
collector isn't retried, crash files go at sign-out and account switch,
and server builds parse stacks as they did.

Existing assertions changed in the desktop telemetry tests: the catch-up
wait is now a range, the endpoint comes from the shell, and the platform
is `desktop` against the collector's patched branch.
2026-11-03 17:38:04 +01:00
Linus Rath 3f264367af fix: harden the app lock: forget in an order that can't leave a sign-in without a PIN, hear wipes and clear subjects while locked, and refuse obvious PINs 2026-11-03 17:08:02 +01:00
Linus Rath 383e579eec feat: run the app to the top of the window, with the window's controls in its own toolbars
The desktop app and an installed PWA had a row across the whole window
with the title and the window controls. That row is gone. The rail,
sidebar, list and reader run to the top of the window in their own
colours, and the controls sit in the top row of whatever is rightmost.

- `lib/window-controls-overlay.ts` finds and marks those rows before
  paint, and they reserve the controls' width through
  `--titlebar-right-inset`
- the rows and the rail are the drag area, everything clickable is
  `no-drag`, and an invisible band lets a page with no row still move
- settings and the admin shell get a row only under the overlay, Pro's
  tab strip and Mountain View's bar match the controls' height
- Alt floats the menu over the top left, in `window-menu-bar.tsx`
- skeletons carry the same marks, so nothing shifts when the app loads
2026-11-03 16:52:42 +01:00
Linus Rath 03cdf69bf2 feat: let desktop users opt in to usage info and crash reports, each with its own switch 2026-11-03 15:55:17 +01:00
Linus Rath fbda1e3401 fix: keep the Docker build within Node's default heap #1510 2026-11-03 15:40:09 +01:00
Linus Rath 2cbb84fe1b fix: keep a plugin off when it is turned off during its first start 2026-11-03 15:03:59 +01:00
Linus Rath 306b249c07 chore: merge the offline and desktop base into the app lock branch 2026-11-03 14:40:25 +01:00
Linus Rath ba45169aae test: fix the types in the admin shell and plugin dialog frame tests 2026-11-03 14:15:49 +01:00
Linus Rath 7aac946a9d feat: merge managed deployment policies and enforcement
# Conflicts:
#	lib/desktop/types.ts
#	locales/ar/common.json
#	locales/ca/common.json
#	locales/cs/common.json
#	locales/da/common.json
#	locales/de/common.json
#	locales/en/common.json
#	locales/es/common.json
#	locales/et/common.json
#	locales/fa/common.json
#	locales/fr/common.json
#	locales/he/common.json
#	locales/hu/common.json
#	locales/it/common.json
#	locales/ja/common.json
#	locales/ko/common.json
#	locales/lv/common.json
#	locales/mn/common.json
#	locales/nb/common.json
#	locales/nl/common.json
#	locales/pl/common.json
#	locales/pt/common.json
#	locales/ro/common.json
#	locales/ru/common.json
#	locales/sk/common.json
#	locales/th/common.json
#	locales/tr/common.json
#	locales/uk/common.json
#	locales/vi/common.json
#	locales/zh-TW/common.json
#	locales/zh/common.json
2026-11-03 14:08:20 +01:00
Linus Rath 52a2b458a0 feat: merge decrypting once per message and keeping external content off unverified mail 2026-11-03 13:38:40 +01:00
Linus Rath a74543c137 feat: take back certificate grants at sign-out and account switch 2026-11-03 13:24:11 +01:00
Linus Rath fb3c82eed5 feat: lock the app with a PIN, a passkey or Windows Hello, with mail out of memory and the local store closed
The lock is off by default, and an OS policy can require it and set the
timeout. It locks after a time without use counted across all windows and
tabs, on OS screen lock in the desktop app, and from Lock now.

Locking saves open composers, stops the outbox at its next message, closes
the store and stops its worker with every key, then reloads the page, so
nothing of the old page stays in reach. The new page holds the app back at a
Suspense boundary until someone unlocks. Pop-outs get no session while
locked, push notifications name nobody, and subjects leave plain storage.

One random lock key wraps each account's key as a layer. The PIN wraps the
lock key with PBKDF2 at 600,000 iterations, with a wait after wrong tries
that a reload doesn't reset. A passkey with PRF and the desktop's OS storage
wrap it too. Details and limits are in the decisions file.
2026-11-03 13:08:37 +01:00
Linus Rath e20aded171 fix: keep restored desktop composers off sent mail and on their own From, check links from the native menu, and fetch no dictionary without asking
A restored composer opened whatever the id named. Sending keeps the id, so its autosave would have destroyed the Sent copy, Send mailed it twice and Discard deleted it. It now needs the login in its address and a real draft, keeps its From with several accounts, and stays on the list if the server doesn't answer. A message from a bulwark:// link no longer marks itself read or sends a receipt. Native menus speak all 30 languages. Spellcheck dictionaries come from Google only after a yes, never in no-third-party mode.
2026-11-03 12:53:40 +01:00
Linus Rath 426078e6ef Merge branch 'feat/desktop-offline' into feat/do-native-ui 2026-11-03 12:26:59 +01:00
Linus Rath 2edd6b5ea7 fix: hold forced policies against spoofed plugins, recommended values and stopped servers
A forced value could be got around from inside the app. Only a forced
value may now decide what's allowed: the keys that do are forced-only,
so a recommended one no longer acts as forced. With a plugin list a
package can name any id, so uploads are refused and only managed
plugins can match. A server taken off AllowedJmapServers stops its
accounts without signing out: client, push and sync stop, the switcher
drops them, restore and refresh answer 403 with the cookie kept, and
listing it again resumes with no sign-in. OAuth and SSO starts check
the list too.

- `policy.onWipeOfflineData` is gone from the bridge: main runs the wipe
- `KeystoreAllowedOrigins` added, and `POLICY_CONSUMERS` names who acts
  on each key or who still owes it
- a Bulwark server's name must be plain after parsing, a wildcard in the
  auth lists needs a domain of two labels
2026-11-03 12:06:21 +01:00
Linus Rath b635986506 chore: merge drafts and kept attachments into the settings branch
One attachment scope: the kept-attachments code, which takes chunks with a
file and spares pinned mail. A draft only on this device stays through a
tightened window. Mail's budget still gives way to what Files keeps.
2026-11-03 12:04:47 +01:00
Linus Rath aebce87776 Merge branch 'feat/do-native-ui' into feat/do-app-lock 2026-11-03 12:01:57 +01:00
Linus Rath efa6fa4ae4 Merge branch 'feat/do-store-settings' into feat/do-policies 2026-11-03 11:44:34 +01:00
Linus Rath 2c08b10fef feat: merge the plugin keystore capability 2026-11-03 11:41:30 +01:00
Linus Rath fb6ff162ff Merge branch 'feat/desktop-offline' into feat/do-policies 2026-11-03 11:40:53 +01:00
Linus Rath 1d4587f7ea fix: stop a stuck certificate call holding mail rendering, and let a server's certificate access be taken back 2026-11-03 11:31:14 +01:00
Linus Rath 4044351611 test: keep what was chosen for a store that waits for a new sign-in 2026-11-03 10:51:36 +01:00
Linus Rath fb0eeca9c5 fix: put the focus on the desktop menu strip once its names are on screen 2026-11-03 10:49:25 +01:00
Linus Rath fcf31df928 fix: keep one policy object per change so the login page renders in the desktop app 2026-11-03 10:45:45 +01:00
Linus Rath e0abc7fa18 chore: merge the reviewed store, pop-outs and desktop core into the settings branch
Eviction now goes by the base's signal: `storeLost` and the `store-reset`
notice, with `bulwark-store-opened` as the marker and `prune-files` for files
with no key. The storage page and the lost count hang off that. What a user
picks is stored under `bulwark-offline-scope`, the key `offlineScopeFor`
reads, and `email-snapshot` follows the base's rule. `OfflineEnabled` is read
from the desktop bridge when it's there.
2026-11-03 10:40:47 +01:00
Linus Rath eb365938a2 test: line the merged branches' tests up with the store's wipe rules
The token route now answers 503 when the provider can't be reached, in every build, and a refused renewal for a background account now removes it as evicted, which keeps its data unless the server said invalid_grant. Two assertions from the desktop and pop-out branches change for that. Also drops a duplicate import from the same merge.
2026-11-03 10:26:00 +01:00
Linus Rath 6e262bf1c5 feat: move offline mode onto the encrypted store, with a merged outbox and conflicts
# Conflicts:
#	app/api/auth/token/route.ts
#	lib/offline/offline-mode.ts
#	stores/account-store.ts
#	stores/auth-store.ts
2026-11-03 10:21:34 +01:00
Linus Rath a9b6a05841 feat: enforce the desktop app's OS policies for plugins, mail servers and offline mail 2026-11-03 10:15:15 +01:00
Linus Rath 2db3c4b217 feat: let privileged plugins sign and decrypt with the OS certificate store in the desktop app 2026-11-03 09:58:48 +01:00
Linus Rath 97aa9e61b5 fix: stop the Pro shell flashing its skeleton when a dialog closes 2026-11-03 09:21:52 +01:00
Linus Rath b001cbf109 feat: show what each account keeps on the device and let it be changed and cleared
The offline section of Settings > Backup & reset now shows, per account, the
space each part takes, the last sync, a first sync's progress with the time
left, the window and attachment choices, and clearing the account. All
conflicts, calendar and contacts too, are listed there and counted in the
banner.

- the organisation's caps beat the user's choice and the desktop default, and
  every path that takes a scope goes through `effectiveScope`
- the desktop app keeps mail without asking, says so once, and stays off for
  an account where the user turned it off
- `navigator.storage.persist()` is asked on turning it on, and a refusal
  warns with a way to ask again; Safari gets the 7-day hint when not installed
- a store the browser deleted is noticed, says what was lost and syncs again
- a full disk says so, with what to do
- those who had offline mode keep it, with the 30 days they had
- Keep offline in the Files app's menu
2026-11-03 08:57:41 +01:00
Linus Rath 3b1fa089aa test: give the stand-in desktop bridge its version and platform 2026-11-03 08:54:05 +01:00
Linus Rath 9a992e3ddd fix: close a plugin dialog on Escape while the focus is in one of its fields 2026-11-03 08:53:45 +01:00
Linus Rath 2b48047bcc feat: merge the webmail side of the desktop app
# Conflicts:
#	lib/desktop/types.ts
#	locales/ar/common.json
#	locales/ca/common.json
#	locales/cs/common.json
#	locales/da/common.json
#	locales/de/common.json
#	locales/en/common.json
#	locales/es/common.json
#	locales/et/common.json
#	locales/fa/common.json
#	locales/fr/common.json
#	locales/he/common.json
#	locales/hu/common.json
#	locales/it/common.json
#	locales/ja/common.json
#	locales/ko/common.json
#	locales/lv/common.json
#	locales/mn/common.json
#	locales/nb/common.json
#	locales/nl/common.json
#	locales/pl/common.json
#	locales/pt/common.json
#	locales/ro/common.json
#	locales/ru/common.json
#	locales/sk/common.json
#	locales/th/common.json
#	locales/tr/common.json
#	locales/uk/common.json
#	locales/vi/common.json
#	locales/zh-TW/common.json
#	locales/zh/common.json
#	stores/account-store.ts
2026-11-03 08:48:10 +01:00
Linus Rath 7bbd148ff5 fix: keep the mail list where it was when coming back to the Pro mail tab 2026-11-03 08:46:12 +01:00
Linus Rath b79781cc94 fix: never lose a draft moving between windows and keep credentials off the window channel
Review of the pop-out work found ways to lose text, a wrong sign-out and
changes reaching ordinary browser tabs.

- Drafts: a closing window hands text over only to a main window holding
  its login, and asks instead when the last save failed, the browser is
  offline, a save is running or plugins could still have a say. A failed
  rescue goes to "Continue draft". The opening window lets go only once
  the composer is on screen, also for Edit draft in a message window.
  Send options travel with the draft. Closing during a send asks first.
- Sessions: answers are encrypted for the window that asked, and only a
  window holding the login answers. A renewal queued on the lock can be
  called off by a sign-out, signs nobody out once its account is gone, and
  takes the cached token after waiting (Rauthy `nbf`, #552).
- Ordinary tabs run none of the host. No lock is held for life.
- A message window hides after a refused session and waits for its
  folders; a composer stays on screen with its text. Another tab dropping a
  login it couldn't restore no longer closes pop-outs.
- Plain-text mail stays selectable in the desktop app. Folder save renames
  reserved and shell names and skips one refused name. All display-mode
  checks know the overlay. The attachment bar keeps to one row.

The earlier test that accepted text being gone after a failed rescue now
asserts that it reaches the "Continue draft" slot.
2026-11-03 08:37:10 +01:00
Linus Rath 706550daf4 fix: keep local mail and the outbox when an automatic sign-out wasn't a real refusal of the login, for 30 days at most 2026-11-03 08:33:03 +01:00
Linus Rath c76ba0fee2 feat: let organisations cap what devices keep offline 2026-11-03 08:11:43 +01:00
Linus Rath 46766ac8aa fix: let a server build pass its guard, and let the desktop app save drafts and forget a server on sign-out 2026-11-03 07:57:35 +01:00
Linus Rath 81032027c6 fix: try a plugin once more when a busy machine makes it miss its start limit 2026-11-03 07:50:38 +01:00
Linus Rath a365ba9041 Merge branch 'feat/desktop-offline' into feat/do-desktop-core 2026-11-03 07:34:32 +01:00
Linus Rath dccf4e8b9f chore: merge feat/desktop-offline into the windows branch 2026-11-03 07:31:32 +01:00