Commit Graph
231 Commits
Author SHA1 Message Date
Linus Rath c4d9a918bf feat: let admins preset and lock where a new search looks #1485 2026-11-01 17:44:37 +01:00
Linus Rath 52188f54d6 Merge branch 'feat/enterprise-pack' into feat/enterprise-pack-f 2026-11-01 13:50:22 +01:00
Linus Rath e9b235bab2 Merge branch 'main' into feat/enterprise-pack
# Conflicts:
#	components/admin/admin-shell.tsx
#	lib/admin/outgoing-hosts-registry.ts
#	lib/admin/reachable-hosts.ts
#	scripts/lite/build.mjs
#	stores/policy-store.ts
2026-11-01 13:46:59 +01:00
Linus Rath eb26219b3c feat: send telemetry error counts within a minute when an admin turns on instant mode 2026-11-01 12:55:58 +01:00
Linus Rath 6428c2b880 fix: answer from the device only when the mail server is really gone, and finish the first offline sync #523 2026-11-01 12:07:30 +01:00
Linus Rath 0401352e6e Merge branch 'feat/enterprise-pack-d'
# Conflicts:
#	components/email/thread-conversation-view.tsx
#	lib/admin/outgoing-hosts-registry.ts
2026-11-01 10:48:21 +01:00
Linus Rath 1aed3eaf59 Merge branch 'feat/enterprise-pack-e' 2026-11-01 10:42:46 +01:00
Linus Rath 6e931a731c fix: keep the server-side device policies on when a login's session or the policy can't be read 2026-11-01 09:44:03 +01:00
Linus Rath 9c54a4037c test: account for the docs links of the Notices, Devices and out-of-office template pages in the outgoing host list 2026-11-01 09:09:06 +01:00
Linus Rath a7b343a726 test: account for the docs links and feed URL checks of the new organisation pages in the outgoing host list 2026-11-01 09:06:39 +01:00
Linus Rath adbec718ef feat: let admins keep sender and subject out of push notifications, enforced by the server
The push preview route now reads the device policy of the login it
serves (its scopes worked out from the login's own JMAP session, every
non-personal account counted as a group) and under
`pushWithoutContent` asks the mail server for the message's ids only,
so sender, subject and text never reach the notification. The service
worker shows "New mail" for such an answer. A policy that can't be read
counts as on.

The three existing push preview route tests now mock the config manager
with the default policy: without it the route can't read a policy in the
test environment and holds the text back, which those tests didn't mean
to cover. No assertion changed.
2026-11-01 08:40:02 +01:00
Linus Rath e73a1b375f feat: let link-check plugins reach an admin-set service with no third-party traffic
The plugin host tests compared api.host.features with an object of two
flags; they now include the new noThirdPartyTraffic flag.
2026-11-01 08:39:35 +01:00
Linus Rath ebb33619a0 Merge branch 'feat/enterprise-pack-a' 2026-11-01 07:36:59 +01:00
Linus Rath eb8fb84867 fix: let admins name msip_labels as the extra label header and refuse names the server won't take
The labels-section test's expected error text changes with the message, which now says why a name is refused.
2026-11-01 06:54:50 +01:00
Linus Rath 870881c57e fix: fetch a company calendar once for everyone at a time and pause after a failure 2026-11-01 06:51:17 +01:00
Linus Rath 0fcdc2ece4 fix: reach a company calendar on a private address only when its host names the intranet 2026-11-01 06:35:56 +01:00
Linus Rath 3bd8e83d60 feat: show company calendars set by the admin 2026-11-01 05:41:34 +01:00
Linus Rath 59f8e462cd feat: let admins define classification labels with presets 2026-11-01 05:07:33 +01:00
Linus Rath e66383de5b feat: let admins set an address for phishing reports 2026-11-01 04:33:38 +01:00
Linus Rath aa88e1b279 Merge branch 'main' into feat/observability 2026-11-01 04:21:12 +01:00
Linus Rath 123018f322 feat: add the policy sections and admin pages for the enterprise features 2026-11-01 04:20:02 +01:00
Linus Rath 83a19f3329 docs: point code comments at the monitoring guide where the website has it 2026-11-01 03:49:54 +01:00
Linus Rath d49839435b feat: send OpenTelemetry traces when an OTLP endpoint is configured
With `OTEL_EXPORTER_OTLP_ENDPOINT` or
`OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` set (and neither
`OTEL_SDK_DISABLED=true` nor `OTEL_TRACES_EXPORTER=none`), Bulwark
loads the OpenTelemetry SDK at startup and exports Next's own spans
plus a client span for every call to Stalwart, WOPI, translation, the
plugin proxy and OAuth over OTLP/HTTP. Calls to Stalwart carry a
`traceparent` header, so Stalwart's spans join the trace. All other
settings come from the standard `OTEL_*` variables.

Every span is scrubbed before export: URLs keep their origin only,
request paths become their route group, query strings, user agents and
client addresses are dropped, and addresses or credentials in other
attributes are removed. Without an endpoint the SDK is never loaded.
The endpoint is listed under the hosts that stay reachable with no
third-party traffic, and System > Monitoring shows whether tracing runs.
2026-11-01 03:25:28 +01:00
Linus Rath e580ed24da feat: send anonymous error counts with the usage stats when the admin also agrees to it
A second switch under System > Usage stats, "Also send anonymous error
counts", or `BULWARK_TELEMETRY_ERRORS=on|off`. Off by default, off
whenever the heartbeat is off or no third-party traffic is on, and
switching the heartbeat off switches it off too. With it on the
heartbeat becomes schema 2: the v1 fields unchanged plus `errors`, up to
50 fingerprints of the last complete UTC day with the error name, the
top frames in Bulwark's bundles (path, line, column, debug ID), the
version, a bucketed count, on how many of the last 7 days it occurred,
and a message only when it is a fixed error code. Without the consent
the heartbeat stays exactly schema 1. The admin preview shows the v2
payload as soon as the switch is flipped.
2026-11-01 03:10:41 +01:00
Linus Rath 481c4b6030 feat: serve Prometheus metrics and split health into live and ready
- `/api/metrics` in Prometheus text format, off until `METRICS_TOKEN`
  or `METRICS_TOKEN_FILE` is set (404 before, 401 without the bearer
  token): requests per route group with status class and latency,
  sign-in outcomes, calls to Stalwart, WOPI, translation, the plugin
  proxy and OAuth, browser errors, errors today, process memory,
  event-loop lag, uptime and build info. Labels come from fixed lists.
  Each replica serves its own.
- `/api/health/live` answers while the process runs;
  `/api/health/ready` checks the config, the shared store, writable
  data dirs and Stalwart's session resource. `/api/health` and
  `/api/ready` are unchanged.
- the Docker image gets a HEALTHCHECK on the live endpoint
- adds prom-client, and the OpenTelemetry packages the tracing step
  loads on demand
2026-11-01 02:59:40 +01:00
Linus Rath cba1109444 feat: report browser and server errors to an error tracker the admin sets
Admins had logs and nothing else: client errors only reached the
browser console. An admin can now set a Sentry-protocol address (DSN)
for Bugsink, GlitchTip or Sentry under System > Monitoring, or with
`ERROR_REPORTING_DSN`, `ERROR_REPORTING_DSN_FILE`,
`ERROR_REPORTING_SAMPLE_RATE` and `ERROR_REPORTING_ENVIRONMENT`.
Bulwark Lite reads the same from `errorReporting` in policy.json.
Everything stays off while no address is set.

- browsers report errors from the error boundaries, window.onerror,
  unhandled rejections and failed JMAP requests (method and error type
  only), with route and JMAP method breadcrumbs and the chunks' debug IDs
- the server reports what Next hands to onRequestError and what routes
  log with logger.error
- every report is scrubbed first: quoted text, addresses, URLs, file
  names, tokens and the rest after the first line are removed, and the
  server scrubs what browsers post to /api/errors again
- reports are deduplicated and rate-limited per session and process
- errors are counted per day for "errors today" on the admin page
- startup messages and audit entries go through the logger, so
  `LOG_FORMAT=json` covers them
2026-11-01 02:50:19 +01:00
Linus Rath 692f965f20 fix: start with ADMIN_PASSWORD when the admin config is mounted read-only #1066 2026-11-01 02:40:25 +01:00
Linus Rath d781e8762e Merge branch 'feat/multi-instance' 2026-11-01 01:43:15 +01:00
Linus Rath eb56ea441f fix: list the User-Agent contact link as a non-request host 2026-11-01 01:22:17 +01:00
Linus Rath ac29a94fc3 fix: cap the plugin packages kept for approval review and sweep broken kept-version folders #1066 2026-11-01 01:04:51 +01:00
Linus Rath dcecd50b7f fix: keep up to 256 rendered branding images per kind, enough for many domains #1066 2026-11-01 01:03:33 +01:00
Linus Rath 38bc06f2d3 fix: answer 503 for plugin lists and marketplace installs while the shared store is down #1066
directory-keys-shared.test.ts: the store-down case now expects the store error instead of an 'invalid' signature, since a store outage no longer counts as a bad signature.
2026-11-01 01:03:13 +01:00
Linus Rath d78ce02f78 fix: refuse a plugin install that can't hold the registry lock and a bundle that doesn't match its entry #1066 2026-11-01 01:01:19 +01:00
Linus Rath 750f3114c1 fix: keep an old plugin's bundle hash when the shared store hiccups while it is fixed #1066 2026-11-01 00:58:33 +01:00
Linus Rath b39e3e27ac fix: cache a plugin signature only when it verifies for the cached bundle #1066 2026-11-01 00:57:21 +01:00
Linus Rath d9f4bdd9be fix: keep serving a plugin unsigned when its first signature can't be stored 2026-11-01 00:55:51 +01:00
Linus Rath 256855b294 fix: refuse to sign plugins on an instance whose signing key differs from the others #1066 2026-11-01 00:54:57 +01:00
Linus Rath 5efccb65e0 fix: show a re-uploaded logo or icon at once instead of after a restart 2026-11-01 00:24:07 +01:00
Linus Rath fc7d5617c2 feat: serve uploaded branding from the shared store on every instance #1066 2026-11-01 00:20:24 +01:00
Linus Rath 51e7999e3c feat: sign plugin bundles with one key on every instance #1066 2026-11-01 00:16:42 +01:00
Linus Rath 5e4b6465d1 Merge branch 'feat/multi-instance' into feat/multi-instance-ext 2026-11-01 00:13:14 +01:00
Linus Rath e3936287fd feat: share plugin approvals and plugin settings between instances #1066 2026-11-01 00:13:09 +01:00
Linus Rath e75834314d fix: show plugin approval requests and decisions to every part of the server at once 2026-11-01 00:13:08 +01:00
Linus Rath 2944d2797f fix: keep every key when several of a plugin's settings are saved at once 2026-11-01 00:12:26 +01:00
Linus Rath 5e73fcbcc1 feat: append lines in batches to the shared log and clear it #1066 2026-11-01 00:08:39 +01:00
Linus Rath 8b40cfca90 fix: update the plugin frame origins in the CSP right after an install, also on other instances #1066 2026-11-01 00:06:52 +01:00
Linus Rath ff08fe50fe fix: say when an admin sign-out or password change couldn't be stored instead of answering ok or wrong password #1066 2026-11-01 00:05:15 +01:00
Linus Rath bcb333d40c feat: share installed plugins and themes between instances #1066 2026-11-01 00:04:15 +01:00
Linus Rath 740a892590 fix: answer 503 for admin and setup saves while the shared store is down #1066
Config, policy, branding, OAuth setup, password and setup wizard writes
used to answer 500 when the shared store couldn't be reached, and the
wizard's token check failed outside any handler. They answer 503 with
Retry-After now, through the helper that already turned a read-only
refusal into 409. The step-session-secret test's wizard mock gains
wizardSessionRefusal, which the setup routes now call.
2026-11-01 00:02:57 +01:00
Linus Rath 46f47b6b8f fix: answer 409 instead of 500 for every admin save a read-only config refuses #1249 2026-10-31 23:59:00 +01:00