smolmail/README.md

79 lines
4.7 KiB
Markdown

# Smol Mail
A minimalist, decentralized, end-to-end encrypted mail protocol.
Email should be simple again. Inspired by [Spartan](spartan://spartan.mozz.us/), [Misfin](gemini://misfin.org/), and [LXMF](https://github.com/markqvist/lxmf). Smol Mail reduces electronic correspondence to its essentials: an address, a key and a message. One keypair per user, five operations, one binary message format. No sprawling infrastructure, no proprietary accounts, no advertising, no tracking. Anyone can run a server, anyone can write a client, and only the intended recipient can read a message.
A server learns which mailbox a message is for, how big it is and when it arrived. Nothing else — not the sender, not the subject, not the content.
- **Minimal** — five operations, four primitives, no extensibility mechanisms.
- **Private** — messages are sealed and signed; senders are absent from the wire format.
- **Decentralized** — independent servers, no federation, no directories.
- **Portable** — identity is a keypair, not an account.
## Identity
Identity is one Ed25519 keypair. The 32-byte public key *is* the identity; the 32-byte seed is the only secret to back up. Encryption keys are derived from the same keypair, so there is exactly one thing to hold, move between servers, print on paper or scan from a screen.
Signatures prove authorship. A throwaway key per message means a stolen identity key cannot decrypt anything already sent.
## Addressing
```
alice@example.org short form, resolved via the server
smol://alice@example.org/mfrggzdfmztwq2lknnwg23tpo… self-certifying, carries its own key
```
The short form is typeable. The long form carries the key itself, so an address shared by QR code, contact file or link needs no trust in any server at all. Either way, changing servers never changes an identity.
Keys learned from a server are pinned on first use. Later changes need a rotation certificate signed by the previous key, or explicit confirmation.
Recipients learn only the sender's key, never a name: a first contact arrives as a bare fingerprint until its address is known. A client may carry its full `smol://` address in a signed `Reply-To` field so first contacts can be answered; receivers bind it only when its key matches the message's signer (SPEC.md §5.7).
## Cryptography
Ed25519 · X25519 · ChaCha20-Poly1305 · SHA-256. Four established primitives, no novel cryptography, nothing else anywhere in the protocol. Transport is TCP with a Noise handshake — no certificates, no CA, no expiry.
## What is not protected
Traffic analysis, delivery timing, mailbox size, and whether a user has an account on a given server. Run a server as a Tor onion service for metadata resistance; the transport needs no changes for it.
There is no forward secrecy on the recipient side: a seized recipient key decrypts ciphertext recorded while it was valid. Messages are signed, so they carry non-repudiation rather than deniability.
Version 1 excludes attachments, group messaging, federation, anonymous routing, multi-device synchronisation and key revocation.
## Reference server
[smolmaild.py](smolmaild.py) is a complete server in one file. It declares its own dependencies inline, so there is nothing to install:
```
uv run smolmaild.py keygen
uv run smolmaild.py serve
```
`keygen` writes the server's static key to `server.key` and prints its public key in base32. Publish that public key through a trusted channel — clients pin it, and a mismatch aborts the handshake.
`serve` listens on `127.0.0.1:1961` by default and stores mail in `mail.db`. Use `--host 0.0.0.0` to accept remote connections, and `--invite-token` to close registration. `--help` lists the size, quota, retention and rate limits.
## Reference client
[smolmail.py](smolmail.py) is a complete client in one file, with the same inline dependencies:
```
uv run smolmail.py keygen
uv run smolmail.py trust example.org <server-key>
uv run smolmail.py register alice@example.org
uv run smolmail.py resolve bob@example.org
echo "hello" | uv run smolmail.py send bob@example.org --subject Hi
uv run smolmail.py fetch
uv run smolmail.py list
uv run smolmail.py read <id>
```
`import` adds a contact from a `smol://` address without trusting any server, `contacts` lists known keys and how each was learned, and `rotate` replaces the identity with a signed certificate your contacts accept automatically.
Mail is stored sealed and opened on demand, so the local database holds no plaintext. Superseded keys are retained after a rotation, because mail already addressed to them is readable with nothing else.
## Specification
[SPEC.md](SPEC.md) — wire format, operations, trust model and conformance.