Skip to content

pimalaya/neverest

Repository files navigation

Logo

📫 Neverest

CLI to synchronize, backup and restore emails

Matrix Mastodon

Caution

Neverest is in active development and currently shipped as v1.0.0-rc. Expect breaking changes between releases until stabilization.

Important

This README documents Neverest v1, which is not yet released. If you are running v0.1, refer to the v0.1.0 README instead. The MIGRATION.md guide walks v1 users through the breaking changes.

Table of contents

Features

  • Remote backend support: IMAP, JMAP
  • Local (filesystem) backend support: m2dir specs
  • Simple auth support for IMAP: anonymous, login, plain, oauthbearer, xoauth2, scram-sha-256
  • HTTP auth support for JMAP: basic, bearer, raw header
  • TLS support:
    • Rustls with ring crypto
    • Rustls with aws crypto (requires rustls-aws feature)
    • Native TLS (requires native-tls feature)
  • Discovery support (wizard only):
  • Mailbox filters (include / exclude / all), applied symmetrically to both sides
  • Per-side permissions gating create / delete on mailboxes and messages, plus update on flags
  • Per-side connection pools with one client per worker
  • Incremental cache at $XDG_CACHE_HOME/neverest/<account>/state.json
  • Dry-run mode (-d) prints the patch the sync would apply without touching either side
  • JSON output via --json

Tip

Neverest is written in Rust and uses cargo features to gate backend support. The default feature set is declared in Cargo.toml.

Installation

Pre-built binary

Neverest is not yet released, therefore the only way to get a pre-built binary is to check out the releases GitHub workflow and look for the Artifacts section.

Note

Such binaries are built with the default cargo features. If you need specific features, please use another installation method.

Cargo

cargo install --locked --git https://github.com/pimalaya/neverest.git

With only IMAP + m2dir support:

cargo install --locked --git https://github.com/pimalaya/neverest.git \
  --no-default-features \
  --features imap,m2dir,rustls-ring

Nix

If you have the Flakes feature enabled:

nix profile install github:pimalaya/neverest

Or run without installing:

nix run github:pimalaya/neverest

Sources

git clone https://github.com/pimalaya/neverest
cd neverest
nix run

Configuration

Run neverest. With no configuration file on disk the wizard asks for an account name and an email address, runs provider discovery (PACC, then Thunderbird Autoconfiguration, then RFC 6186 SRV), prompts for IMAP or JMAP credentials based on what discovery returned, asks for a local m2dir store root for the other side, then writes the result to disk.

A persistent configuration is loaded from the first valid path among:

  • $XDG_CONFIG_HOME/neverest/config.toml
  • $HOME/.config/neverest/config.toml
  • $HOME/.neverestrc

Override the path with -c <PATH> or NEVEREST_CONFIG=<PATH>; multiple paths can be passed at once, separated by :. The first one is the base and the rest are deep-merged on top.

See config.sample.toml for a documented template covering every supported field. An existing account can be re-prompted later with neverest configure (or neverest configure -a <account> to target a non-default account): the wizard reuses the current values as defaults instead of re-running discovery.

Usage

Initializing an account

Before the first sync each account must be initialized once:

neverest init [-a|--account <NAME>]

The account flag is optional: when omitted, the account marked default = true in the configuration is used.

This opens both sides (IMAP CAPABILITY / JMAP session GET / m2dir store creation) so credential and network errors surface up front, then writes an empty cache snapshot at $XDG_CACHE_HOME/neverest/<account>/state.json. The presence of that file is the single source of truth for "this account is initialized"; sync refuses to run when it is missing and init refuses to run when it is present.

Running a sync

neverest sync [-a|--account <NAME>]

Sync walks every mailbox surviving the filter, diffs the two sides against the cached snapshot, applies the resulting hunks through per-side connection pools, then prints a report covering created / updated / deleted mailboxes, flags and messages. Pass -d / --dry-run to print the patch without applying it.

Pass --reset to drop the cached state before running. Without --include-mailbox, the entire snapshot plus every IMAP / JMAP state token is cleared; with --include-mailbox, only the listed mailboxes are wiped. The first post-reset sync rebuilds the snapshot via a full re-list, equivalent to first-sync semantics.

Mailbox filters and per-side permissions

Mailbox filters declared in the configuration apply symmetrically to both sides. They can be overridden per invocation with -m / --include-mailbox, -x / --exclude-mailbox, or -A / --all-mailboxes (the three flags are mutually exclusive). Matching is ASCII case-insensitive: INBOX matches inbox, but non-ASCII characters (umlauts, Cyrillic, accents) must be spelled exactly as the server reports them.

Per-side permissions live under each side's backend table and gate what the sync engine is allowed to mutate on that side:

[accounts.example]
left.m2dir.root = "~/.Mail/example"
left.m2dir.mailbox.create = false
left.m2dir.mailbox.delete = false
left.m2dir.flag.update = true
left.m2dir.message.create = true
left.m2dir.message.delete = false

right.imap.server = "imap.example.com"
right.imap.message.delete = false

All five permissions default to true. Setting any of them to false makes the engine treat the side as read-only for that operation; planned hunks that would violate the policy are dropped from the patch and surfaced in the report.

Migrating from Maildir

Neverest does not ship an in-tree Maildir converter: keyword storage is not standardized across Maildir consumers (info-section letters, dovecot-keywords, X-Keywords / X-Label headers, …), so any local migration would silently lose or mangle flags depending on which tool wrote the source tree.

The recommended path for users coming from mbsync, OfflineIMAP or a Dovecot Maildir layout is to point Neverest at a fresh m2dir root and resync from the authoritative IMAP/JMAP server. Flags re-converge cleanly and the new cache reflects the actual server state.

Tip

You can also use m2m to convert your Maildir structure into a m2dir one, which is more adapted for synchronization. But since no standards exist for managing custom flags in Maildir, it is still recommended to resync from IMAP/JMAP.

Checking a configuration

neverest check [-a|--account <NAME>]

Opens both sides and asks each one to list mailboxes. The operation itself is cheap; the value is in surfacing the credential, network or config errors that would otherwise only show up during a real sync.

Social

Sponsoring

nlnet

Special thanks to the NLnet foundation and the European Commission that have been financially supporting the project for years:

If you appreciate the project, feel free to donate using one of the following providers:

GitHub Ko-fi Buy Me a Coffee Liberapay thanks.dev PayPal