mbrts/README.md
Adriano Caloiaro f11a0926e5
fix: fallback uses mbsync --pull to avoid re-uploading mbrts-delivered mail
Also documents the flags/deletions-only scheduled-mbsync approach using
command-line operators instead of a restricted Sync directive.
2026-07-20 20:22:56 -06:00

128 lines
4.9 KiB
Markdown

# mbrts (mailbox real-time sync)
Real-time mail delivery for maildirs via Fastmail's JMAP push API.
Connects to Fastmail's JMAP EventSource stream and delivers new messages directly to a local Maildir the moment they arrive without polling. On first run, or when saved JMAP state is stale, mbrts falls back to `mbsync --pull` to backfill anything missed, then takes over from that point.
## Prerequisites
Create a Fastmail API token at **Settings → Security → API Tokens**. This is distinct from an IMAP app password — mbrts requires a proper JMAP bearer token.
## Setup
### Nix / home-manager
Add mbrts as a flake input:
```nix
inputs.mbrts = {
url = "git+https://code.adriano.fyi/me/mbrts";
inputs.nixpkgs.follows = "nixpkgs";
};
```
Add the overlay and module, then configure the service:
```nix
# in your flake overlays
inputs.mbrts.overlays.default
# in your home-manager module list
inputs.mbrts.homeManagerModules.default
# in your home-manager config
services.mbrts = {
enable = true;
accounts = [
{
name = "Personal";
maildirPath = "/home/you/.mail/Personal";
passwordCommand = "pass show fastmail/api-token";
}
];
};
```
The module generates a config file and runs mbrts as a systemd user service. Logs: `journalctl --user -u mbrts -f`.
**Module options:**
| Option | Type | Default | Description |
|---|---|---|---|
| `enable` | bool | — | Enable the service |
| `package` | package | `pkgs.mbrts` | Package to use |
| `logLevel` | enum | `"info"` | `debug`, `info`, `warn`, or `error` |
| `accounts` | list | `[]` | List of account submodules (see fields below) |
**Account fields** (each account requires exactly one token source):
| Field | Description |
|---|---|
| `name` | Account name; used for state file and default mbsync channel |
| `maildirPath` | Path to the local Maildir root |
| `passwordCommand` | Shell command that prints the bearer token to stdout |
| `tokenFile` | Path to a file containing the bearer token |
| `tokenEnv` | Environment variable containing the bearer token |
| `mbsyncAccount` | mbsync channel/group name for fallback (defaults to `name`) |
### Standalone
Build the binary:
```
CGO_ENABLED=0 go build -o mbrts .
```
By default mbrts reads `$XDG_CONFIG_HOME/mbrts/config.yaml` (e.g. `~/.config/mbrts/config.yaml`):
```yaml
accounts:
- name: Personal
maildir_path: /home/you/.mail/Personal
password_command: "pass show fastmail/api-token"
```
Every config file option has an equivalent CLI flag, and flags override the config file. A single account can be configured entirely on the command line:
```
mbrts --name Personal \
--maildir-path ~/.mail/Personal \
--password-command "pass show fastmail/api-token"
```
Run `mbrts --help` for the full flag list, or `mbrts --version` to print the version.
## aerc
Configure the account source as `maildir://` so aerc picks up delivered messages immediately via inotify:
```ini
source = maildir:///home/you/.mail/Personal
```
## Using mbrts alongside mbsync
mbrts handles inbound delivery; mbsync is still useful for pushing local changes (read flags, deletions, moves) back to Fastmail. They can coexist, but require careful configuration to avoid duplicate messages.
The problem is that mbsync tracks synced messages by IMAP UID in its own state database. It knows nothing about messages mbrts delivered directly to the Maildir. So a scheduled mbsync run that pulls new messages re-downloads mail mbrts already wrote (local duplicates), and a run that pushes new messages re-uploads that same mail to the server (remote duplicates).
The solution is to keep new-message propagation out of the scheduled run entirely, and constrain it to flag changes and deletions in both directions. mbsync's command-line operators override the channel's `Sync` directive per-invocation, so no separate config file is needed — run the scheduled sync as:
```
mbsync --gone --flags --all
```
`--gone` propagates deletions and `--flags` propagates flag changes (read/unread, etc.), each in both directions, with no `New` in either direction. Local reads and deletions flow up to Fastmail; webmail reads and deletions flow down to the Maildir; new mail is left entirely to mbrts.
mbrts's own fallback (`mbsync --pull`) covers the inbound side: on first run, or when its JMAP state is stale, it pulls any messages it missed. Point mbrts at the channel via `mbsync_account` (defaults to the account name); no manual initial sync is required.
### With home-manager
The generated systemd service runs `mbsync --all` by default, which does a full sync. Override its `ExecStart` to use the flags/deletions-only operators:
```nix
systemd.user.services.mbsync.Service.ExecStart =
lib.mkForce "${pkgs.isync}/bin/mbsync --gone --flags --all --verbose";
```
Leave the channel `Sync` directive at its default (`Full`) — both the scheduled service and mbrts's fallback override it on the command line, so it is never used as configured.