fumi/README.md

5.6 KiB
Raw Blame History

文 fumi

License: Apache-2.0 AI-DECLARATION: auto

A Rust client for Smol Mail: a minimalist, end-to-end encrypted mail protocol over a Noise-secured TCP connection. fumi derives an identity from one 32-byte master secret, seals and opens mail, and talks to a mailbox; it is the counterpart to bunshin, the server.

Status

Both carriers are implemented: the TCP carrier (SPEC.md 1.1) and the Reticulum carrier of RNS.md (1.2), the latter behind the rns cargo feature (nix build .#rns). fumi interoperates in both directions with the reference stack — smolmail.py/smolmaild.py over TCP and smolmail_rns.py/smolmaild_rns.py over Reticulum — and with bunshin over both carriers, including the cross-transport case upstream section 15 promises: send over TCP, fetch over the mesh, one mailbox.

Addressing

alice@example.org[:1961]                                        short form, resolved via the server
smol://alice@example.org/mfrggzdfzt...                          self-certifying, 52-char base32 identity
smol+rns://alice@8f2c1d0a7b6e5f4c3d2a1b0e9f8c7d6a               short form over Reticulum (rns feature)
smol+rns://alice@8f2c1d0a7b6e5f4c3d2a1b0e9f8c7d6a/mfrggzdfzt... self-certifying over Reticulum

Usage

One master per store, selected by the global --key and --db flags; a second identity is a second pair of files.

fumi [--key identity.key] [--db fumi.db] [--timeout 30] <command>

# identity
fumi keygen [--force]                      create a master secret
fumi whoami                                address, public key, fingerprint, rotation index
fumi restore <address>                     recover local state from the master alone
fumi rotate                                advance the rotation index, sign and push the certificate

# servers and contacts
fumi trust <host> <key-b32> [--force]      pin a server's Noise static key
fumi register <address> [--invite TOKEN]   bind this identity to a username
fumi resolve <address>                     look up a contact's key, walk the chain, pin it
fumi import <smol-uri>                     add a contact from a self-certifying address
fumi contacts                              known keys and how each was learned

# accept tokens
fumi accept <address>                     issue a token, admit to the main tier, sync the set
fumi block <address>                       withdraw it, sync the set

# mail
fumi send <address> [--subject S] [--body TEXT | --file F | -] [--header K:V] [--reply-to ID] [--anonymous] [--no-pad]
fumi fetch [--keep] [--reset]             retrieve, verify, store, acknowledge
fumi delete <id>...                       remove from the server explicitly
fumi list [--sent] [--requests]
fumi read <id> [--sent]

# RNS carrier (built with --features rns / nix build .#rns); addresses select
# it by scheme, so these flags apply only to smol+rns:// dials:
fumi --rns-udp <host:port> --rns-udp-forward <host:port> [--rns-storage DIR] <command>

On the mesh the destination hash is the pin (RNS.md 13.4): there is no trust, no static key, and a client creates no Reticulum identity (13.8). The --rns-udp-forward target is effectively required for a client, which speaks first; it should point at an rnsd or at the server's UDP interface. Requests carry an explicit 30-second timeout (13.5), links are torn down when a session ends (13.10), and a resend is safe because ids are derived.

Mail is stored sealed and opened on demand; there is no plaintext at rest. A first contact is learned by trust on first use and marked unverified when the server's key was not pinned; a key change is accepted only when a signed rotation chain leads from the key held to the one offered, and is surfaced rather than applied silently.

Build

nix build              # or: nix develop -c cargo build
nix develop -c cargo test

bunshin and the reference clients make a complete test rig: register against a local server, exchange mail in both directions, then rotate and fetch the mail that the superseded key still receives.

Modules

File Contents
src/crypto.rs base32, HKDF, HMAC, SHA-256, the Ed25519→X25519 map with SPEC.md §2's checks
src/address.rs parsing for all four address forms, username validation, fingerprints
src/account.rs master, seed_n derivation, accept-key and tokens, rotation certificates
src/message.rs envelope seal/open (§5.1–§5.3), message id (§5.4), frontmatter (§5.5)
src/transport.rs Transport trait, bind values, operation and status constants
src/tcp.rs Noise_NX initiator, len u32 || op u8 || body framing, static-key pinning
src/rns/ the Reticulum carrier behind the rns feature: FFI over shim/, path discovery, one link per session
src/client.rs the six operations, AUTH, chain walking, token sync, fetch pipeline
src/store.rs SQLite: state, contacts, accepted, tokens, seen ids, inbox, sent

Unit tests pin every derivation against vectors generated from smolmail.py, including a full envelope reproduced byte for byte.

References