Commit Graph
33 Commits
Author SHA1 Message Date
Linus Rath a80083abd6 fix: let the lite build through with the gated bimi logo endpoint 2026-10-30 01:02:09 +01:00
Linus Rath 50b3ddccec feat: let admins keep the refresh token valid on sign-out so Stalwart keeps the consent #1388 2026-10-29 20:47:18 +01:00
Linus Rath 5a8810b007 fix: let the Lite build reference the document template endpoint 2026-10-29 00:12:35 +01:00
Linus Rath 96797597c5 Merge branch 'main' into feat/org-features 2026-10-28 15:14:34 +01:00
Linus Rath 0c5ed0c606 fix: show the lite 404 page in the language of its address, right to left for arabic, hebrew and persian 2026-10-28 13:09:30 +01:00
Linus Rath 010e9fb6d9 feat: fence a Lite build made with no third-party traffic to its own hosts #903 2026-10-28 04:30:42 +01:00
Linus Rath 9ee767f576 fix: show a translated 404 to a signed-in user who mistypes a URL instead of the login page 2026-10-28 03:52:40 +01:00
Linus Rath 6994dc2ce6 fix: send a Lite link without a language to the same page in the reader's language instead of a bare 404 2026-10-28 03:46:40 +01:00
Linus Rath 553f1e9149 feat: add a switch that stops all third-party traffic #903 2026-10-28 02:19:06 +01:00
Linus Rath bb6191e665 fix: say in the Lite install guide that Stalwart 1.0 keeps the running bundle when a download fails 2026-10-26 20:28:21 +01:00
Linus Rath 5ac55ef2ab fix: ship the bundle manifest Stalwart 1.0 needs to install Bulwark Lite as an Application 2026-10-26 20:27:18 +01:00
Linus Rath aa860e5a85 fix: sign in to Bulwark Lite with password and TOTP on Stalwart 1.0 2026-10-26 20:27:16 +01:00
Linus Rath a3f396df80 fix: point the Lite install guide for Stalwart at the release tag that exists and say what a failed download does 2026-10-26 20:22:41 +01:00
Linus Rath cf2c27157d Merge branch 'main' into feat/stalwart-1.0
# Conflicts:
#	components/contacts/contacts-app.tsx
#	components/email/__tests__/composer-discard.test.tsx
#	components/email/calendar-invitation-banner.tsx
#	components/email/email-composer.tsx
#	components/mail/mail-app.tsx
#	components/pro/pro-compose-tab-body.tsx
#	components/pro/pro-email-tab-body.tsx
#	components/ui/__tests__/avatar.test.tsx
#	components/ui/avatar.tsx
#	hooks/use-mailbox-drop.ts
#	lib/__tests__/calendar-alerts.test.ts
#	lib/__tests__/jmap-calendar-tasks.test.ts
#	lib/__tests__/jmap-send-threading.test.ts
#	lib/demo/demo-client.ts
#	lib/demo/fixtures/calendars.ts
#	lib/jmap/client-interface.ts
#	lib/jmap/client.ts
#	lib/web-push.ts
#	stores/__tests__/contact-store.test.ts
#	stores/__tests__/email-store-thread-key.test.ts
#	stores/calendar-store.ts
#	stores/contact-store.ts
#	stores/email-store.ts
2026-10-26 10:14:12 +01:00
Linus Rath 4f942417c8 fix: show pdf attachment previews in safari #1301 2026-10-20 21:08:01 +02:00
Linus Rath 41a0716c50 Merge branch 'main' into feat/stalwart-1.0 2026-10-16 01:41:48 +02:00
Linus Rath 3bbf0fa796 fix: let the lite build pass with the plugin proxy and secrets routes in the bundle 2026-10-14 18:20:58 +02:00
Linus Rath 3da46b5a0d feat: remember every sign-in unless the admin shows the remember me box 2026-10-13 13:13:56 +02:00
Linus Rath c633185cda Merge branch 'main' into feat/stalwart-1.0
# Conflicts:
#	CONTRIBUTING.md
#	LICENSE
#	README.md
#	app/api/calendar-agenda/route.ts
#	app/api/wopi/files/[fileId]/contents/route.ts
#	app/api/wopi/launch/route.ts
#	components/calendar/calendar-app.tsx
#	components/calendar/calendar-day-view.tsx
#	components/calendar/calendar-month-view.tsx
#	components/calendar/calendar-week-view.tsx
#	components/calendar/event-modal.tsx
#	components/contacts/contact-detail.tsx
#	components/email/email-composer.tsx
#	components/email/email-list.tsx
#	components/files/file-browser.tsx
#	components/files/files-app.tsx
#	components/files/folder-tree-sidebar.tsx
#	components/filters/filter-rule-modal.tsx
#	components/mail/mail-app.tsx
#	hooks/use-mailbox-drop.ts
#	integration/run-tests.sh
#	lib/__tests__/calendar-agenda-route.test.ts
#	lib/__tests__/jmap-draft-lifecycle.test.ts
#	lib/__tests__/web-push.test.ts
#	lib/account-state-manager.ts
#	lib/jmap/client-interface.ts
#	lib/jmap/client.ts
#	lib/jmap/types.ts
#	lib/message-list-order.ts
#	lib/sieve/generator.ts
#	lib/sieve/parser.ts
#	lib/telemetry/payload.ts
#	lib/thread-utils.ts
#	lib/unified-mailbox.ts
#	lib/web-push.ts
#	lib/wopi/token.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/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/tr/common.json
#	locales/uk/common.json
#	locales/zh-TW/common.json
#	locales/zh/common.json
#	scripts/lite/lib.mjs
#	stores/__tests__/calendar-store-synthetic-ids.test.ts
#	stores/__tests__/email-store-shared-folder-keywords.test.ts
#	stores/__tests__/email-store-unified-actions.test.ts
#	stores/__tests__/file-store.test.ts
#	stores/email-store.ts
#	stores/file-store.ts
#	stores/message-list-tabs-store.ts
2026-10-12 08:35:08 +02:00
lucamzanon 9560a8b74f feat: optional encrypted backup of the account list
Users with several accounts can save the list of accounts in their browser
(address, server, label, colour) on the webmail server and bring it back on
another device. The list is encrypted in the browser with a password the user
chooses (PBKDF2-SHA-256, 600,000 iterations, AES-256-GCM, owner bound as
additional data); the server stores only the envelope, one file per owner
under SETTINGS_DATA_DIR/account-archives. No mailbox passwords are included.

Off by default behind the admin feature gate accountArchiveEnabled, pinned
off in Lite. Every method of /api/account-archive needs a session cookie of
the owner, matched without regard to case, whose credential is checked
against the mail server on each call; listing included.

Restored accounts this browser lacks are listed as awaiting sign-in: they
survive a reload, and opening one leads to the sign-in form with the address
filled in. Accounts already present get their archived label and colour.
2026-10-09 10:54:38 +02:00
Linus Rath 1446fd39dc fix: link the mobile app from every account with a sign-in of its own
"Link Mobile App" only appeared for OAuth sessions, its step-up could
only go through the identity provider (which failed without an OAuth
client and bounced password+TOTP users to the provider's login page),
and it handed the phone the desktop's own refresh token. Revoking that
token on sign-out, or a rotating provider, signed the phone out too; a
confidential client stopped the phone at its first token expiry; and an
identity provider on another host was refused by the app.

The step-up now mints the phone's own grant:

- Password and password+TOTP accounts confirm the current password (and
  code) in the dialog. The server uses it once to get an OAuth grant
  from Stalwart's structured login for the app's own client, retries
  with the webmail's client when the server insists on a registered one,
  and falls back to an app password ("Bulwark mobile (paired <date>)")
  or, on servers that offer neither, the verified password. Failed
  attempts are limited per account so the webmail's address cannot be
  banned by guessing through it.
- SSO accounts keep the provider round trip, now with `offline_access`
  where offered; its fresh tokens become the phone's grant instead of
  being thrown away, and must belong to the account being paired.
- The account and server always come from the slot's verified session
  context, never from the request.

Grants live 5 minutes server-side, codes 2 minutes, both single use; the
store is pinned on `globalThis`. Redeem answers `invalid_code`,
`expired_code` or `used_code`, and the desktop polls a separate status
id to replace the QR with "Signed in on your phone". A phone whose
client is confidential, or whose provider is on another host, renews
through `/api/auth/pair/token` with a sealed refresh token, so current
app builds work unchanged. Signing out discards a pending grant but no
longer affects a paired phone.

The dialog shows for every account (not in Lite), with a copyable
sign-in link, a TOTP field when needed, and the app's own "Scan a
sign-in code" wording.
2026-09-27 04:16:18 +02:00
Linus Rath eb7b6f8dab fix: point Stalwart installs at a tagged Lite release and publish checksums
The Stalwart bundle's README told admins to use
`releases/latest/download/bulwark-lite-stalwart.zip` as `resourceUrl`.
Stalwart re-downloads that URL on its own schedule and checks no hash or
signature, so whatever the newest release held would run as script on
the Stalwart origin next to the web admin.

The README now installs from the tagged release, explains updating by
moving `resourceUrl` to the next tag, and describes the floating URL as
an opt-in with that risk spelled out. Release runs upload a `.sha256`
next to each Lite zip.
2026-09-26 17:30:47 +02:00
Linus Rath 6ff94d439e fix: pin Lite pages to their own inline scripts and guard the Stalwart bundle
The static export's header policy has to allow inline scripts (the
hydration scripts differ per page and one header cannot list them all),
so any injected inline script ran. The Stalwart bundle had no policy at
all: Stalwart sends no security headers for an Application, and nothing
stopped another site from framing it.

- Postbuild gives every page of the static target a `<meta>` CSP that
  allows exactly the page's own inline scripts; enforced together with
  the host's header, it takes away `'unsafe-inline'`. `verify` fails a
  page whose inline scripts are not all pinned.
- The Stalwart entry document carries a `<meta>` CSP (no plugins, eval,
  foreign scripts or moved `<base>`; its shells are rewritten at runtime,
  so hashes are not possible there) and refuses to run inside a frame
  of another origin.
- The nginx and Caddy examples send the same CSP as `_headers`.
2026-09-26 17:27:00 +02:00
Linus Rath 1faa9bc795 docs: point the Lite bundle README at HTTPS and the update action
A live install of the Stalwart bundle on 1.0 and 0.16.23 showed four
gaps in the README it carries:

- It didn't say where the web admin's update action is. It is under
  **Actions > Update applications** on both versions.
- On a plain `http://` address other than `localhost` the browser has
  no Web Crypto API, so the PKCE sign-in can't run and the webmail
  falls back to a password session for the tab: "Remember me" does
  nothing and single sign-on can't work. It now asks for HTTPS.
- The local build command left out `--in-place`, without which the
  build refuses to run outside CI.
- The closing list named single sign-on as missing from this bundle,
  although the README's own section above describes how it works here.
2026-09-25 23:32:00 +02:00
Linus Rath d9ffe8a76e fix: reach directly shared files and keep others' files on a failed copy
Fixes from the review of the Files work (1cf9944b..a9a25b94).

- A file shared on its own, or one below a folder its owner took back,
  could not be reached. Once Stalwart 1.0's placeholder folders were
  left out it became a root of "Shared with me", which listed folders
  only, and search opened it on the drive root. 0.16 lists such files
  as roots too. Each owner's files shared on their own are now listed
  on one page under "Shared with me" ("Files from <name>"), where they
  open, preview and download like any other file and nothing can be
  added; two of the same name get a number. Search opens a shared hit
  where it is listed, and links into a shared drive name the node by id
  (`/files?shared=<owner>:<id>`), so a shared folder or page also
  survives a reload. A Files page that is already open takes the link
  from search directly, since Next keeps it mounted when only the query
  changes.
- A copy that fails part-way destroys only the nodes it made, the
  deepest first and without `onDestroyRemoveChildren`, one call per
  level because 0.16 refuses a folder destroyed in the same call as
  its children. It used to destroy the new folder with everything
  below it, so whatever someone put there during a long copy was
  deleted for good. A folder holding such nodes now stays, and the
  error says part of the copy was left in place.
- The document editor opens read-only on 0.16 for a sharee without the
  rename right. 0.16.6 to 0.16.23 refuse any change a sharee makes
  without Modify, so every save of such a document failed with a 500.
  1.0, told apart by its `maxFileNodeDepth`, still asks for
  `mayModifyContent` alone.
- Ctrl+C with only symlinks selected empties the clipboard instead of
  keeping an earlier cut for the next paste.
- A paste reads each account's file tree once, not once per pasted
  item for the listing and 1.0's depth check.
- The Stalwart bundle's README says that on older 0.16 releases and
  the 1.0 pre-releases a failed update also drops the cached copy, and
  the next restart or reload of the Applications downloads it again,
  not only a later `UpdateApps`. The static README says a restart
  applies the CORS setting on the current 1.0 pre-releases, where
  `ReloadSettings` fails with the stock spam-filter rules.
2026-09-25 22:16:19 +02:00
Linus Rath a9a25b9403 docs: correct the Lite bundle's update, pinning and CORS instructions
- The Stalwart bundle's README said a failed download keeps the
  previous bundle online. That holds on 0.16.23 only: older 0.16
  releases and the 1.0 pre-releases unmount the Application until a
  later `UpdateApps` succeeds, and on every version a restart after the
  cached copy expired, with `resourceUrl` unreachable, leaves it
  unmounted. The README now says so and tells admins to check the URL
  first.
- The pin URL used a `v` prefix that release tags don't have (404).
- The static README told admins to set `[http] permissive-cors`, a TOML
  key Stalwart 0.16 and later don't read. It now names the
  `Http.usePermissiveCors` setting, with the web admin path, the
  `stalwart-cli` commands and the JMAP call.
- The stated requirement is Stalwart 0.16.6 (0.16.19 for
  `oauthClientId`).
2026-09-25 19:20:41 +02:00
Linus Rath c96814db4c fix: hide the lite server field once config.json sets jmapServerUrl #1087 2026-09-23 18:19:26 +02:00
Linus Rath 106bc07f04 feat: add OpenID Connect login to the Stalwart Lite bundle 2026-09-21 23:45:08 +02:00
Linus Rath a4afc407e5 feat: publish Bulwark Lite as a container image #1081 2026-09-21 22:56:26 +02:00
Linus Rath 77ef60caae feat: resolve deep links to the local instance 2026-09-20 19:59:37 +02:00
Linus Rath fdfcb1711e feat: add Bulwark Lite build for Stalwart Applications 2026-09-19 00:53:53 +02:00
Linus Rath 2d4a562432 fix: harden Bulwark Lite login, deep-link replay and settings gating 2026-09-18 22:16:57 +02:00
Linus Rath 5de98bc04b feat: add Bulwark Lite static export build 2026-09-17 23:54:33 +02:00