MailSalonSync – replace offlineimap or mbsync with JMAP support, written in Go

QBasicBitch1 pts0 comments

Starstreak/MailSalonSync - Cerberus Games Git: Welcome to Cerberus Gaming - Fuck you, I'm coding

This website requires JavaScript.

Starstreak/MailSalonSync

Watch

Star

Fork

You've already forked MailSalonSync

Code

Issues

Pull requests

Projects

Releases

Packages

Wiki

Activity

Actions

No description

4 commits

1 branch

0 tags

75 KiB

Go

99.7%

Makefile

0.3%

main

Find a file

HTTPS

Download ZIP<br>Download TAR.GZ<br>Download BUNDLE

Open with VS Code

Open with VSCodium

Open with Intellij IDEA

Exact

Exact

Union

RegExp

Starstreak

7c782760bf

Fix imports

2026-08-20 03:51:32 +00:00

cmd/MailSalonSync

Fix imports

2026-08-20 03:51:32 +00:00

internal

Fix imports

2026-08-20 03:51:32 +00:00

config.example.json

New program

2026-08-19 08:40:06 -04:00

go.mod

New program

2026-08-19 08:40:06 -04:00

LICENSE

New program

2026-08-19 08:40:06 -04:00

logo.avif

New artwork

2026-08-19 08:43:54 -04:00

mailsalon.svg

New artwork

2026-08-19 08:43:54 -04:00

Makefile

New program

2026-08-19 08:40:06 -04:00

README.md

Update README.md

2026-08-19 12:44:49 +00:00

README.md

MailSalonSync

MailSalonSync is a small, Maildir-first replacement for the common OfflineIMAP workflow, written in Go. It can mirror configured folders from either IMAP or JMAP accounts, propagate deletions in both directions, handle multiple accounts and mailbox-to-directory mappings, and submit raw RFC 5322 messages with JMAP.

While it works, an interactive terminal gets a Charm Bubble Tea/Bubbles spinner with the current account, mailbox, operation, and running add/delete counts. When stdout is redirected, it falls back to normal line-oriented status output.

Features

Multiple accounts in one config file.

IMAP and JMAP accounts can be mixed.

Any number of remote mailbox -> local Maildir mappings per account.

Remote messages are stored as normal Maildir files (new/, cur/, tmp/).

Stable per-message state survives Maildir moves from new/ to cur/ and normal Maildir flag suffix changes.

Remote deletion removes the corresponding local Maildir file.

With propagate_deletes: true, deleting the local Maildir file propagates back to the server.

IMAP deletion uses UIDPLUS UID EXPUNGE when available. A broad EXPUNGE is refused by default on servers without UIDPLUS.

JMAP deletion removes the message from the mapped mailbox; if that is its final mailbox, the Email is destroyed.

JMAP send accepts a complete RFC 5322 message from a file or stdin.

Secrets can come from environment variables or commands such as pass, so they do not need to live in the config file.

IMAP supports implicit TLS, STARTTLS, and explicit plaintext mode.

JMAP session/API/upload/download endpoints are required to use HTTPS.

Ctrl-C or q in the interactive status UI cancels the underlying sync/send operation.

Build

The project targets Go 1.23 and uses the Go-1.23-compatible Charm v1 packages.

go mod tidy<br>go build -o MailSalonSync ./cmd/MailSalonSync<br>The only non-standard-library dependencies are Charm Bubble Tea and Bubbles.

Commands

Sync every configured account:

MailSalonSync -config ~/.config/MailSalonSync/config.json sync<br>Sync one account, or a comma-separated subset:

MailSalonSync -config ~/.config/MailSalonSync/config.json sync -account personal-imap<br>MailSalonSync -config ~/.config/MailSalonSync/config.json sync -account personal-imap,work-jmap<br>Validate the configuration:

MailSalonSync -config ~/.config/MailSalonSync/config.json check-config<br>Print an example configuration:

MailSalonSync example-config<br>Send a raw message via JMAP:

MailSalonSync -config ~/.config/MailSalonSync/config.json \<br>jmap-send -account work-jmap -file message.eml<br>Or make it act like a simple sendmail-style sink:

cat message.eml | MailSalonSync -config ~/.config/MailSalonSync/config.json \<br>jmap-send -account work-jmap<br>Configuration

The default config path is $XDG_CONFIG_HOME/MailSalonSync/config.json, falling back to ~/.config/MailSalonSync/config.json.

See config.example.json for a complete two-account example.

A mailbox mapping has two fields:

{ "remote": "INBOX", "local": "INBOX" }<br>local is relative to the account's local_root, unless it is an absolute path. Use "." to use local_root itself as the Maildir.

For JMAP, a remote mailbox can be selected three ways:

{ "remote": "role:inbox", "local": "INBOX" }<br>{ "remote": "Archive/Projects", "local": "Projects" }<br>{ "remote": "id:server-mailbox-id", "local": "Special" }<br>role: is usually the most robust choice for Inbox, Sent, Drafts, Trash, and similar special-use mailboxes because it does not depend on a localized display name.

IMAP account

"name": "personal-imap",<br>"protocol": "imap",<br>"local_root": "~/Mail/personal",<br>"propagate_deletes": true,<br>"mailboxes": [<br>{ "remote": "INBOX", "local": "INBOX" },<br>{ "remote": "Archive", "local": "Archive" }<br>],<br>"imap": {<br>"address": "imap.example.com:993",<br>"username": "me@example.com",<br>"password_env": "PERSONAL_IMAP_PASSWORD",<br>"security": "tls"<br>security may be tls,...

config mailsalonsync jmap imap account json

Related Articles