Merge pull request 'to-be 56: the betterbird module owns Betterbird's whole configuration, shown without a credential (issue 586, ADR 0329)' (#336) from design/586-betterbird-owns-its-configuration into main
This commit was merged in pull request #336.
This commit is contained in:
@@ -2,10 +2,12 @@
|
||||
layer: to-be
|
||||
status: designed
|
||||
code: []
|
||||
updated: 2026-10-12
|
||||
updated: 2026-10-13
|
||||
decisions:
|
||||
- "ADR 0316"
|
||||
- "ADR 0317"
|
||||
- "ADR 0326"
|
||||
- "ADR 0329"
|
||||
---
|
||||
|
||||
# 56 — The mail client, set up by the mesh
|
||||
@@ -14,12 +16,17 @@ decisions:
|
||||
upstream release, ships the Owl add-on with it through Betterbird's enterprise policy, and declares the
|
||||
operator's mail accounts without their credentials. A password or token never passes through the mesh: each
|
||||
account signs in once, at a person's first use, and Betterbird keeps the result in its own login store.**
|
||||
(ADR 0316, from issue 345.)
|
||||
(ADR 0316, from issue 345.) **The module owns Betterbird's whole configuration as settings, mesh-wide with a
|
||||
node's layer over each: the preferences, the accounts, the identities with their signatures, the CalDAV
|
||||
calendars, the CardDAV address books, the filters and the tags; its tools show the operator's own values, never
|
||||
a credential.** (ADR 0329, from issue 586.)
|
||||
|
||||
**Acceptance.** On a desktop node set up from nothing, the operator starts Betterbird from the launcher and
|
||||
finds Owl installed and the declared accounts listed; each asks for its sign-in once, and the work account is
|
||||
created in Owl's own setup. On a desktop node that ran the community package before, the same Betterbird opens
|
||||
the same profile, with its mail and logins, after the switch.
|
||||
the same profile, with its mail and logins, after the switch. Once the operator has approved the derived layers,
|
||||
every desktop node shows the same signatures, calendars, address books, filters and tags, and `betterbird_drift`
|
||||
on each says no difference.
|
||||
|
||||
The module's look-only part (status, preferences, a declared user.js, its log, a restart and a reminder test)
|
||||
is issue 345's and is not repeated here. This document adds what sets Betterbird up.
|
||||
@@ -31,11 +38,13 @@ is issue 345's and is not repeated here. This document adds what sets Betterbird
|
||||
| Betterbird, the upstream release archive, unpacked | the installation directory the community package uses | owned, whole |
|
||||
| the enterprise policy, `policies.json` | the installation's `distribution` directory | owned, whole |
|
||||
| the Owl add-on, the vendor's xpi | the installation directory | owned, whole |
|
||||
| a defaults file naming the system's spelling dictionaries | the installation's `defaults/pref` | owned, whole |
|
||||
| a defaults file naming the system's spelling dictionaries, and one naming the preferences kept at Betterbird's own default (no value of the operator's) | the installation's `defaults/pref` | owned, whole |
|
||||
| a launcher entry and a start script | under the system's local prefix | owned, whole |
|
||||
| the user.js, from `prefs` and `accounts` | the module's directory in the home, copied into the profile by its tools | owned, whole |
|
||||
| the account list in the profile's `prefs.js` | the profile | written into: the module's own keys only |
|
||||
| the profile: mail, address books, calendars, logins | the profile | found |
|
||||
| the settings as the node-engine merges them, one mergeable file per group, each naming the keys it takes (ADR 0326) | the module's directory in the home | owned, whole |
|
||||
| the user.js, composed from the settings | the profile, placed by the tools | the module's, or never touched |
|
||||
| the lists in the profile's `prefs.js`: accounts, outgoing servers, an adopted account's identities, the calendars' order | the profile | written into: the module's own keys only, only while Betterbird is closed |
|
||||
| each declared account's filter file | the account's directory in the profile | owned, whole, only while Betterbird is closed |
|
||||
| the profile: mail, calendar events, contacts, logins | the profile | found |
|
||||
|
||||
The module creates no link: where the community package linked the command, the icons and the dictionaries,
|
||||
the module places a start script, a launcher entry that names the installation directly, and the dictionaries'
|
||||
@@ -159,14 +168,26 @@ the provider's own sign-in can issue. With no credential to carry, the rule that
|
||||
|
||||
## 6. What the tools say
|
||||
|
||||
`betterbird_status` and `betterbird_check` add, never with an address, a host or a name:
|
||||
**The tools show the operator's own configuration, never a credential** (ADR 0329 §1–§3, changing ADR 0316 §16
|
||||
and ADR 0317 §3). An answer may hold an account's name, address, user name and its servers' hosts and ports; an
|
||||
identity's name, address, reply-to, organisation and signature; a calendar's or an address book's name, address,
|
||||
colour and options; a filter's conditions and actions; a tag's name and colour. These values appear only in a
|
||||
tool's answer to its caller and, for a proposed value, in the controller's proposal record; never in an event, a
|
||||
log line, a condition, a metric, the module's record per node, an issue, the knowledge base or this repository.
|
||||
The operator channel's content rule does not apply to these answers, because it withholds the addresses they
|
||||
show; in its place every answer passes the module's own credential check, which withholds a field whose name says
|
||||
password, token, secret or cookie, and the password part of any address. The login store is read only to count,
|
||||
by server, the sign-ins kept. The values reach whoever may call the module's tools, which is every login and
|
||||
agent session on the operator's nodes; the ask itself still shows a proposed value in its shape.
|
||||
|
||||
- the installed version against the pin and the newest upstream release, and whether it came from the mesh or
|
||||
from the community package;
|
||||
- whether the policy file is placed;
|
||||
- Owl's version against its pin, and whether it is active;
|
||||
- how many declared accounts are in the profile's list, beside how many other accounts exist, and how many have
|
||||
not signed in yet.
|
||||
| tool | answers |
|
||||
|---|---|
|
||||
| `betterbird_status`, `betterbird_check` | the installed version against the pin and the newest release; the policy placed; Owl's version and whether it is active; the declared accounts in the list and how many have not signed in yet; and, per kind, the declared entries that drift or are refused and the changes waiting for Betterbird to close |
|
||||
| `betterbird_accounts` | the accounts with their values and the declared account each is, Owl's marked by name and address; with `propose`, `accounts` derived and proposed, the value shown |
|
||||
| `betterbird_identities`, `betterbird_calendars`, `betterbird_address_books`, `betterbird_filters`, `betterbird_tags` | the profile's entries beside the declared ones, how each resolved, an ICS calendar by its name only |
|
||||
| `betterbird_derive` | the five kinds derived per layer, the fields another node holds differently, what was left out; with `propose`, the next layer proposed |
|
||||
| `betterbird_drift` | per kind and declared entry: holds, differs (each field with both values), lacks, or refused; and the undeclared entries |
|
||||
| `betterbird_config` | each managed preference through the locks, the setting `prefs` and Betterbird's defaults, with its drift |
|
||||
|
||||
## 7. What changes for the builder
|
||||
|
||||
@@ -178,12 +199,79 @@ Not decided here: a package repository of the mesh's own, which would also answe
|
||||
node runs from outside the official repositories. ADR 0316 weighed it, and rejected it as larger than this
|
||||
module needs.
|
||||
|
||||
## 8. The rest of the configuration (ADR 0329)
|
||||
|
||||
**The preferences are one entry each.** The setting `prefs` maps a preference's name to its value, so a node's
|
||||
layer overrides one preference, or takes one out with null, and the operator's setup lives in the mesh-wide
|
||||
layer, not in the catalogue. The module ships only its policy's locks and the names it keeps at Betterbird's own
|
||||
default (issue 586). `prefs` refuses a credential's preference, an account's (§4), and the four prefixes the
|
||||
settings below own: `calendar.registry.`, `calendar.list.`, `ldap_2.servers.` and `mailnews.tags.`, so each value
|
||||
has one owner.
|
||||
|
||||
**Five more settings**, set mesh-wide so every desktop node has the same setup, with a node's layer holding only a
|
||||
difference that node is meant to have:
|
||||
|
||||
| setting | an entry | matched in the profile by |
|
||||
|---|---|---|
|
||||
| `identities` | an identity of a declared account: name, address, reply-to, organisation, the signature's text, whether it is HTML, whether it goes above the quote; the account's first identity takes its name and address from `accounts` | the account's default, or an identity of that account with the address |
|
||||
| `calendars` | a CalDAV calendar: name, address, colour, read-only, reminders, user name, the identity that answers invitations | its address |
|
||||
| `address-books` | a CardDAV address book: name, address, user name | its address |
|
||||
| `filters` | per declared account, the ordered list of filters: name, on or off, when it runs, all or any condition, conditions, actions, a folder named by its path within the account | owned whole |
|
||||
| `tags` | per tag key: name, colour, place | its key |
|
||||
|
||||
An identity, a calendar or an address book may carry, in a node's own layer only, the optional `key` of the
|
||||
profile's entry to adopt there, as `accounts` does. The first four are **trusted**, because each names where a
|
||||
password, a reply or forwarded mail goes; `tags` names no server and sends no mail, and is set through the
|
||||
`settings` verb as well. A setting holds no line break, so a signature is a list of its lines; no setting holds a
|
||||
password, a token or a cookie, and an address whose user part carries a password is refused.
|
||||
|
||||
**Adopted as accounts are.** At every apply each declared entry is matched as §4 matches an account: recorded,
|
||||
the module's own key, a node's `key`, else one match adopted, none created under the module's key, several
|
||||
refused with the remedy. Its declared fields go into the user.js under that key. An entry leaving the setting is
|
||||
released if adopted; if created, its preferences are removed, never a calendar's events, an address book's
|
||||
contacts or a cache. An entry no setting declares stays the person's. Owl's accounts and calendars, a calendar
|
||||
subscribed by an ICS address (such an address is often itself the secret), local calendars and local address
|
||||
books are never declared.
|
||||
|
||||
**Filters are owned whole per declared account.** For an account named in `filters`, the module writes its filter
|
||||
file from the declared list, turning each folder's path into that folder's address on the node; a filter added in
|
||||
Betterbird to that account is gone at the next apply, and drift shows it first. Before its first write on a node
|
||||
the module keeps the file it replaces in its own directory in the home. When an account leaves `filters`, the
|
||||
module stops writing its file, which stays the person's as last written, and says where the kept copy is. An
|
||||
account never named keeps its file.
|
||||
|
||||
**What Betterbird rewrites is written only while it is closed:** the lists in `prefs.js` and each filter file.
|
||||
An apply while it runs writes neither, and the module's check counts, per kind, the changes that wait; the next
|
||||
start from the launcher writes them. The user.js is placed as before, since Betterbird reads it only at its start.
|
||||
|
||||
**Derived from the profiles, proposed one layer at a time.** `betterbird_derive` asks every desktop node that
|
||||
holds Betterbird for what its profile holds. An entry found on every such node goes into the mesh-wide layer
|
||||
with the value of the node the derive was called on, and the answer lists every field another node holds
|
||||
differently, which then shows as drift on that node; an entry found on some nodes only goes into those nodes'
|
||||
own layers. With `propose` it proposes the next layer for the operator's Approve: first the mesh-wide one, then
|
||||
each node's that needs one, each only after the one before it was set or declined, against the layer as it then
|
||||
stands and carrying that layer whole (ADR 0277 keeps one open ask per module and machine). It proposes nothing
|
||||
while a desktop node does not answer, because a layer derived without it would put on every node what only some
|
||||
hold. The first mesh-wide proposal also carries the operator's setup of `prefs`, which the catalogue held before.
|
||||
|
||||
## How it is checked
|
||||
|
||||
- **The catalogue's tests:** both pins carry a URL, a version and a sha256; a Betterbird pin outside Owl's range
|
||||
is refused; the generated policy file names Owl as `force_installed` from the placed file with
|
||||
`updates_disabled`, and sets `DisableAppUpdate`; `prefs` refuses the account prefixes; `accounts` refuses a
|
||||
password-like field; a declared account's user.js holds only the module's own keys.
|
||||
`updates_disabled`, and sets `DisableAppUpdate`; `prefs` refuses the account prefixes and the four of §8;
|
||||
`accounts` refuses a password-like field; a declared account's user.js holds only the module's own keys; every
|
||||
mergeable file names the keys it takes, `tags` alone untrusted; the module's defaults hold no value of the
|
||||
operator's.
|
||||
- **The controller's tests:** a key of a trusted file that names its keys is refused through the `settings` verb,
|
||||
and a key of a file nothing trusts, `tags`, is set through it.
|
||||
- **The configuration's tests, on profile fixtures:** each kind adopted, created, or refused when ambiguous, a
|
||||
`key` settling it; leaving releases or removes and never touches events; a declared filter list, forwarding
|
||||
included, written with this node's folder addresses, the replaced file kept, a file never named left byte for
|
||||
byte, one taken out left as last written; with Betterbird running nothing of `prefs.js` or a filter file is
|
||||
written and the waiting changes are counted; derived from two nodes, an entry both hold mesh-wide with the
|
||||
calling node's value and the other's differing fields listed, one on one node in its own layer, Owl's, ICS
|
||||
calendars and a passworded address left out, and one layer proposed at a time; drift names each differing field;
|
||||
no answer holds a credential.
|
||||
- **The module's tests:** writing into `prefs.js` adds and removes only the module's keys, keeps the others and
|
||||
their order, and is refused while Betterbird runs; the count of declared accounts without a kept sign-in is
|
||||
taken from the login store's server names, which are kept in the clear, without decrypting anything.
|
||||
@@ -196,9 +284,14 @@ module needs.
|
||||
the mesh, the policy placed, Owl at its pin and active, the profile in use the same as before the switch, and
|
||||
the declared accounts in the list with how many have not signed in yet; the package manager's list of foreign packages no longer names the
|
||||
community package.
|
||||
- **Live, after the operator's Approve of the derived layers:** on the node derived on, `betterbird_drift` shows no
|
||||
drift for the mesh-wide entries; on the other desktop node it first shows each field where it differs, and,
|
||||
after Betterbird is closed there and started again, none; `betterbird_check` on both says no drift and no
|
||||
waiting change.
|
||||
|
||||
## References
|
||||
|
||||
- ADR 0316, ADR 0317, ADR 0180, ADR 0182, ADR 0205, ADR 0208, ADR 0277.
|
||||
- Issue 345 (the reminder window, extended on 2026-10-12), issue 342, issue 339.
|
||||
- ADR 0316, ADR 0317, ADR 0326, ADR 0329, ADR 0180, ADR 0182, ADR 0205, ADR 0208, ADR 0277.
|
||||
- Issue 345 (the reminder window, extended on 2026-10-12), issue 586 (the whole configuration), issue 342,
|
||||
issue 339.
|
||||
- Thunderbird's enterprise policy templates (ESR 153): `ExtensionSettings`, `DisableAppUpdate`, `Preferences`.
|
||||
|
||||
Reference in New Issue
Block a user