Files
mesh-catalog/modules/betterbird/README.md
T
jschoubben 3629089ced
mesh/merge-gate pass: builds new: modules/betterbird, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
betterbird: never lose an unsent message, never answer an address, and keep the test apart (review of #155)
A restart ended Betterbird with whatever a composer held; now it refuses while a Msgcompose window is
open unless discard_unsent says the operator agreed, and answers within the runtime's 30 s. A
preference's value could carry an address, a user%40domain@host URI, a host or the home; those are
masked, recent-folder, account and cloud-file preferences and every add-on preference but the theme's
id are withheld, and a request for everything is refused. A log kept the token after Bearer or Basic.
The reminder test's Betterbird counted as the operator's and left its profile behind when it ended on
its own; it is now left out everywhere and its unit removes the profile when it stops.
2026-10-09 12:22:47 +02:00

8.4 KiB
Raw Blame History

betterbird

Betterbird, the mail and calendar client of g14 and shanks (a Thunderbird fork, with the operator's Exchange calendar through the Owl add-on), as a module (novox/hq issue 345, ADR 0208). It requires x11-display, so it is assigned only where a display server is held on the same machine.

It exists because a symptom had no tools: on a workstation, a calendar reminder appeared as a transparent window with nothing in it (2026-10-09). Nothing in the mesh could say which window that was, how the compositor treats it, or which preferences Betterbird ran with. The diagnosis is in hq issue 345.

Owns

what where
the module's directory ~/.config/betterbird/
the module's user.js, filled from the setting prefs ~/.config/betterbird/user.js

Nothing else. It holds no seat and adds no start.

  • No package. g14 and shanks both run betterbird-bin 153.1.0esr-1, installed explicitly from the AUR: pacman counts it as foreign. The node-engine installs packages from the official repositories only, so the module does not declare it, and installing and upgrading Betterbird stay the operator's (an AUR helper). betterbird_status says the version and that it is from the AUR; betterbird_check says when it is missing.
  • No start. Betterbird is started by the operator from the launcher (on the workstation measured, it runs in the login session's scope, started by hand, with no autostart entry and no line in i3's configuration). betterbird_restart starts it as the transient user unit mesh-betterbird, so its output then goes to the journal.
  • The profile is Betterbird's (ADR 0182): ~/.thunderbird/ holds the mail, the address books, the calendars' caches, the logins and the keys. The module declares none of it.

The user.js: how a preference is declared

Betterbird reads user.js in its profile when it starts, and a preference set there wins over the one it saved in prefs.js. The profile's directory has a random name per machine (<random>.default-default), which a manifest cannot name. So:

  1. the node-engine writes ~/.config/betterbird/user.js from the module's setting prefs, one line of user_pref("name", value); statements (default: a comment, no preference);
  2. the module's tools copy that file whole into the profile: betterbird_user_js with apply, or betterbird_restart, which places it before it starts Betterbird again;
  3. Betterbird reads it at that start.

The profile is found as the one the running Betterbird holds open; with none running, as the installation's default in profiles.ini ([Install…] Default), else the profile marked Default=1. On the workstation measured these differ: Profile1 is marked Default=1, and the installation's default, the one in use, is the other.

A user.js the module did not write is never overwritten. The module's begins with the line // Betterbird's user.js (module betterbird; any other is the operator's, and apply refuses with what to do. The tools refuse a declared file that holds anything but comments and user_pref statements, or that sets a preference whose name can hold a credential.

The file is trusted (it carries no "trusted": false): a preference can weaken how the mail client checks a server, so the setting is changed only at the controller's terminal, never through a verb (novox/hq issue 339).

Tools

They are served by the node's runtime as the operator account (ADR 0175). No answer carries mail, a calendar entry, an account, an address or a credential.

tool does
betterbird_status (r) whether it runs (pid, since, scope, children), the package and version and that it is from the AUR, where its output goes, the profile and how it was found, the user.js (declared, placed, in effect), the add-ons the operator installed (ids), and the preferences that decide how its windows and notifications look
betterbird_prefs (r) named preferences or prefixes (ending in . or *, at most 200; an empty name, * or . is refused): value and source (user.js, prefs.js or the default). Withheld without its value: a name that can hold a credential (password, token, oauth, signon, …); one that names an account, server, calendar, recent folder or person (mail.server., mail.identity., mail.last_msg…, messenger.account., mail.cloud_files., calendar.registry., …); and every extensions.* but the theme's id (extensions.activeThemeID). In every value answered, the home is ~, and a mail address, a user%40domain@host URI and a host name are masked
betterbird_user_js (r; a with apply) the declared user.js against the profile's; apply places it
betterbird_log (r) the last lines of the journal of mesh-betterbird (or of the reminder test), addresses and credentials masked (the value after password=, token: and the like, and the token after Bearer/Basic). A Betterbird the module did not start prints elsewhere, and the answer says where
betterbird_restart (a) ends Betterbird (asked to quit, forced after 10 s), places the user.js where allowed, starts it as mesh-betterbird. Refused while a composer window is open (X instance Msgcompose), because the unsent message would be lost, unless discard_unsent is true, which only the operator's yes justifies. It answers within the runtime's 30 s: a start not yet seen after 4 s is said in note, and betterbird_status shows it
betterbird_check (r) installed; one running; profile found; the user.js on the machine, valid, placed, in effect; its output read
betterbird_reminder_test (a) a reminder on demand without the operator's calendar (below)

A reminder on demand

betterbird_reminder_test starts a second Betterbird with --no-remote in a throw-away profile under /run/user/<uid>/mesh-betterbird-reminder-test/, as the user unit mesh-betterbird-reminder-test (ended after 10 minutes at most). The profile holds the module's user.js and one calendar: a local .ics file with one event, "Mesh reminder test", whose reminder is due after seconds from the call (default 40). carry copies named preferences of the operator's profile into it (a theme, a colour switch), under the same limits as betterbird_prefs. Then:

  • xorg_windows with class Calendar answers the reminder window: role AlarmWindow, type Dialog, its depth (32: a visual with alpha, which the compositor blends), its map state;
  • xorg_screenshot with that window shows it as the screen shows it.

The unit removes its profile when it ends, however it ends (ExecStopPost): at its 10 minutes, on stop, or when the test window is closed. The test Betterbird is never counted as the operator's: its command line names its own profile, and status, check, restart and the user.js leave it out.

The running Betterbird, its profile and its calendars are not touched: --no-remote keeps the two apart. profiles.ini and installs.ini are not rewritten either, measured on the workstation (2026-10-09): their content and modification time (June 2023) were the same before a --profile start, while it ran, and after it ended.

The reminder window, as measured (2026-10-09, workstation, i3 and picom with glx)

X class / instance eu.betterbird.Betterbird / Calendar
role, type AlarmWindow, _NET_WM_WINDOW_TYPE_DIALOG (i3 floats it)
depth 32 (ARGB visual), like every Betterbird window there
size 600×300, kept in the profile's xulstore.json

A window of depth 32 that its program has not drawn is transparent under a compositor, not black: that is why the symptom looks the way it does. Why Betterbird did not draw it is hq issue 345's question.

What changes when it is assigned

g14 shanks
files ~/.config/betterbird/ and its user.js (no preference) the same
the profile untouched until betterbird_user_js with apply or betterbird_restart the same; the profile has no user.js today, so apply may place one
Betterbird keeps running as started keeps running as started (since 2026-10-04, its output on the session's pipe nobody reads: betterbird_check names it until betterbird_restart)

Relies on

  • A display server on the same machine (x11-display, ADR 0208 §3).
  • xorg for xorg_windows and xorg_screenshot, which look at Betterbird's windows. This module carries no window code of its own.
  • systemd-run and journalctl of the account's user manager.