Compare commits

...

2 commits

Author SHA1 Message Date
Adriano Caloiaro
b16f9f7095
docs: restructure README to lead with Nix setup and improve flow 2026-07-20 18:12:51 -06:00
Adriano Caloiaro
4633758f0c
docs: document mbsync coexistence gotcha 2026-07-20 18:12:47 -06:00

166
README.md
View file

@ -2,50 +2,15 @@
Real-time mail delivery for maildirs via Fastmail's JMAP push API. 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. 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` for a full sync, then takes over from that point.
## How it works ## Prerequisites
On first run, or when the saved JMAP state is stale, mbrts falls back to `mbsync` for a full sync, then takes over from that point. 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 ## Setup
Create a Fastmail API token at **Settings → Security → API Tokens**. ### Nix / home-manager
### Config file
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"
```
Each account requires exactly one token source:
| Field | Description |
|---|---|
| `password_command` | Shell command that prints the token to stdout |
| `token_file` | Path to a file containing the token |
| `token_env` | Name of an environment variable containing the token |
Optional fields: `mbsync_account` (mbsync channel/group name; defaults to `name`), `log_level` (top-level: `debug`, `info`, `warn`, `error`).
### CLI flags
Every config file option has an equivalent flag. Flags override the config file. A single account can be configured entirely on the command line without a config file:
```
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.
### Nix
Add mbrts as a flake input: Add mbrts as a flake input:
@ -56,21 +21,11 @@ inputs.mbrts = {
}; };
``` ```
The flake exposes: Add the overlay and module, then configure the service:
| Output | Description |
|---|---|
| `packages.*.default` | The `mbrts` binary |
| `overlays.default` | Adds `pkgs.mbrts` |
| `homeManagerModules.default` | The `services.mbrts` home-manager module |
### home-manager module
Add the overlay and module to your home-manager configuration, then configure the service:
```nix ```nix
# in your flake outputs # in your flake overlays
overlays = [ inputs.mbrts.overlays.default ]; inputs.mbrts.overlays.default
# in your home-manager module list # in your home-manager module list
inputs.mbrts.homeManagerModules.default inputs.mbrts.homeManagerModules.default
@ -88,23 +43,55 @@ services.mbrts = {
}; };
``` ```
The module generates a config file and runs mbrts as a `systemd` user service. Logs are available via `journalctl --user -u mbrts -f`. The module generates a config file and runs mbrts as a systemd user service. Logs: `journalctl --user -u mbrts -f`.
Module options: **Module options:**
| Option | Type | Default | Description | | Option | Type | Default | Description |
|---|---|---|---| |---|---|---|---|
| `enable` | bool | — | Enable the service | | `enable` | bool | — | Enable the service |
| `package` | package | `pkgs.mbrts` | Package to use | | `package` | package | `pkgs.mbrts` | Package to use |
| `logLevel` | enum | `"info"` | `debug`, `info`, `warn`, or `error` | | `logLevel` | enum | `"info"` | `debug`, `info`, `warn`, or `error` |
| `accounts` | list | `[]` | List of account submodules (see config file fields above) | | `accounts` | list | `[]` | List of account submodules (see fields below) |
## Build **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 . 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 ## aerc
Configure the account source as `maildir://` so aerc picks up delivered messages immediately via inotify: Configure the account source as `maildir://` so aerc picks up delivered messages immediately via inotify:
@ -112,3 +99,70 @@ Configure the account source as `maildir://` so aerc picks up delivered messages
```ini ```ini
source = maildir:///home/you/.mail/Personal 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:** mbsync tracks synced messages by IMAP UID in its own state database. It knows nothing about messages mbrts delivered directly to the Maildir. Because of this, mbsync re-downloads messages that mbrts has already written, creating duplicates.
**The fix:** give mbsync two separate channels per account — one for the scheduled sync (flags and deletions only), and one for mbrts to call on-demand as a fallback when its JMAP state is missing or stale.
```
# scheduled channel — only pushes local changes upstream
Channel Personal
Far :Personal-remote:
Near :Personal-local:
Sync Flags Delete
# pull channel — only used by mbrts on fallback
Channel Personal-pull
Far :Personal-remote:
Near :Personal-local:
Sync New
```
Point mbrts at the pull channel via `mbsync_account`:
```yaml
accounts:
- name: Personal
maildir_path: /home/you/.mail/Personal
password_command: "pass show fastmail/api-token"
mbsync_account: Personal-pull
```
Any scheduled mbsync timer then runs `mbsync Personal` (flags/deletions only), while mbrts calls `mbsync Personal-pull` on fallback (new messages only).
### With home-manager
Restrict the auto-generated channel to flags and deletions via `extraConfig.channel`, then append the pull-only channel via `programs.mbsync.extraConfig`:
```nix
# per-account: restrict scheduled sync to flags/deletions
accounts.email.accounts.Personal.mbsync = {
enable = true;
extraConfig.channel.Sync = "Flags Delete";
# ... other mbsync options
};
# global: define a pull-only channel for mbrts fallback
programs.mbsync.extraConfig = ''
Channel Personal-pull
Far :Personal-remote:
Near :Personal-local:
Sync New
Create Near
SyncState *
'';
# point mbrts at the pull channel
services.mbrts.accounts = [
{
name = "Personal";
maildirPath = "/home/you/.mail/Personal";
passwordCommand = "pass show fastmail/api-token";
mbsyncAccount = "Personal-pull";
}
];
```