commit 1690aed3dc01ef90e6384e4a31077d6fb9a52dc2 Author: randogoth Date: Mon Sep 28 11:21:58 2026 +0300 feat: add fumi client implementation plan for Smol Mail 1.2 Add comprehensive plan for Rust client implementation including: - TCP transport (v1.1) support - RNS transport (v1.2) support via FFI with microReticulum - Architecture with transport abstraction - Module structure and dependencies - CLI command design - Testing strategy - Implementation priority Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe diff --git a/README.md b/README.md new file mode 100644 index 0000000..0524410 --- /dev/null +++ b/README.md @@ -0,0 +1,399 @@ +# Fumi - Smol Mail Client (Rust) + +A Rust implementation of the Smol Mail client protocol, compatible with version 1.2 (including RNS transport). Fumi is the client counterpart to [bunshin](../bunshin), the Smol Mail server implementation. + +## Overview + +Smol Mail is a minimalist, end-to-end encrypted mail protocol. This client implementation supports: + +- **Full protocol v1.1**: All operations over TCP with Noise_NX_25519_ChaChaPoly_SHA256 +- **Full protocol v1.2**: Reticulum Network Stack (RNS) transport for mesh networking +- **Shared codebase**: Single binary supports both transports (via feature flags) +- **Interoperability**: Works with reference Python implementations and bunshin server + +## Protocol Compatibility + +| Feature | v1.1 (TCP) | v1.2 (RNS) | +|---------|------------|------------| +| AUTH | ✅ | ✅ | +| RESOLVE | ✅ | ✅ | +| SEND | ✅ | ✅ | +| FETCH | ✅ | ✅ | +| DELETE | ✅ | ✅ | +| REGISTER | ✅ | ✅ | +| Accept Tokens | ✅ | ✅ | +| Key Rotation | ✅ | ✅ | +| Message Encryption | ✅ | ✅ | + +## Address Formats + +### TCP Transport (v1.1) +``` +smol://alice@example.com[:1961] +smol://alice@example.com/abcdefghijklmnopqrstuvwxyz234567 # self-certifying +``` + +### RNS Transport (v1.2) +``` +smol+rns://alice@8f2c1d0a7b6e5f4c3d2a1b0e9f8c7d6a +smol+rns://alice@8f2c1d0a7b6e5f4c3d2a1b0e9f8c7d6a/abcdefghijklmnopqrstuvwxyz234567 +``` + +The RNS address uses the server's 32-character hex destination hash as the host. + +--- + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ fumi client │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ CLI (main.rs) │ │ +│ │ keygen, import, trust, register, send, │ │ +│ │ fetch, delete, resolve, show │ │ +│ └──────────────────────────────┬──────────────────────────┘ │ +│ │ │ +│ ┌──────────────────────────────▼──────────────────────────┐ │ +│ │ Client Core (client.rs) │ │ +│ │ - Session management with servers │ │ +│ │ - Message composition and parsing │ │ +│ │ - Envelope sealing/unsealing │ │ +│ └──────────────────────────────┬──────────────────────────┘ │ +│ │ │ +│ ┌──────────────────────────────▼──────────────────────────┐ │ +│ │ Account (account.rs) │ │ +│ │ - Master secret management │ │ +│ │ - Identity derivation (rotation chains) │ │ +│ │ - Token generation │ │ +│ └──────────────────────────────┬──────────────────────────┘ │ +│ │ │ +│ ┌─────────────────┐ ┌─────────────────────────────┐ │ │ +│ │ TCP Transport │ │ RNS Transport │ │ │ +│ │ (tcp_transport) │ │ (rns_transport + FFI) │ │ │ +│ └────────┬────────┘ └──────────┬───────────────────┘ │ │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ Transport Trait (transport.rs) │ │ +│ │ - connect(address) : Establish session │ │ +│ │ - request(op, body) : Send frame, get response │ │ +│ │ - bind_values() : Get auth binding values │ │ +│ │ - close() : Clean up connection │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ Store (store.rs) │ │ +│ │ - SQLite local mailbox │ │ +│ │ - Accounts, identities, tokens, messages │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Implementation Plan + +### Phase 1: Core Client (TCP Only) + +#### Dependencies + +```toml +[package] +name = "fumi" +version = "0.1.0" +edition = "2021" +description = "Smol Mail client" + +[dependencies] +snow = "0.9" # Noise protocol +ed25519-dalek = "2" # Ed25519 signatures +x25519-dalek = "2" # X25519 key agreement +sha2 = "0.10" # SHA-256 +rand_core = "0.6" # Randomness +hmac = "0.12" # HMAC-SHA256 +clap = "4" # CLI parsing +log = "0.4" # Logging +env_logger = "0.11" # Logger setup +anyhow = "1" # Error handling +rusqlite = "0.32" # SQLite local store +hex = "0.4" # Hex encoding +data-encoding = "2" # Base32 encoding +``` + +#### Modules + +1. **crypto.rs** - Cryptographic primitives + - Base32 encoding/decoding + - HKDF-SHA256 + - HMAC-SHA256 + - Identity derivation + - Accept token generation + - Message ID computation + +2. **address.rs** - Address parsing + - Parse TCP addresses: `smol://user@host[:port][/identity]` + - Parse RNS addresses: `smol+rns://user@desthash[/identity]` + - Validate usernames + - Generate self-certifying URIs + +3. **account.rs** - Account management + - Master secret storage + - Identity key derivation with rotation + - Current identity tracking + - Token generation for correspondents + +4. **transport.rs** - Transport trait + - Define `Transport` trait with connect/request/close + - Define `TransportBindValues` for auth bindings + - Define operation and status constants + +5. **tcp_transport.rs** - TCP transport + - Noise_NX handshake + - Frame reading/writing + - Server key pinning (optional) + +6. **client.rs** - Core client logic + - Session management + - REGISTER operation + - AUTH operation + - RESOLVE operation + - SEND operation (envelope creation) + - FETCH operation + - DELETE operation + - Message sealing/unsealing + +7. **store.rs** - Local SQLite store + - Account storage + - Trusted server keys + - Accept tokens + - Outbound message queue + - Inbound message storage + +8. **main.rs** - CLI + - keygen: Generate new identity + - import: Import existing identity + - accounts: List accounts + - trust: Trust server key (TCP) + - register: Register username + - send: Send message + - fetch: Fetch messages + - delete: Delete messages + - resolve: Resolve username + - show: Display message info + +--- + +### Phase 2: RNS Transport + +Same approach as bunshin's RNS integration: + +1. **FFI with microReticulum** (recommended for production) + - Use C++ microReticulum via FFI bindings + - Official implementation, full compliance + - Complex but most reliable + +2. **Python IPC** (quick validation) + - Call `smolmail_rns.py` as subprocess + - Share data via files or pipes + - Fast to implement + +3. **Native Rust** (future) + - Wait for mature Rust RNS crate + - Or implement minimal subset + +--- + +## Key Differences from Server (bunshin) + +| Aspect | bunshin (server) | fumi (client) | +|--------|------------------|---------------| +| Noise role | Responder (has static key) | Initiator (no static key) | +| Session | Accepts connections | Initiates connections | +| Trust | Server key pinned by client | Client pins server key | +| Auth | Verifies client signatures | Signs requests | +| Rate limiting | Enforces limits | Respects limits | + +--- + +## CLI Commands + +```bash +# Identity management +fumi keygen # Generate new identity +fumi keygen --name alice # Generate with name +fumi import --name alice -m ABCD... # Import master secret +fumi accounts # List accounts + +# Server trust (TCP only) +fumi trust smol://alice@example.com ABCD... # Trust server key + +# Account registration +fumi register smol://alice@example.com --username alice +fumi register smol://alice@example.com --username alice --invite TOK... + +# Messaging +fumi send smol://bob@example.com --subject "Hello" --body "World" +fumi send smol://bob@example.com --file message.txt +fumi fetch smol://alice@example.com +fumi delete smol://alice@example.com --ids ABCD... +fumi resolve smol://alice@example.com --username bob +fumi show ABCD... # Show message details +``` + +--- + +## Project Structure + +``` +fumi/ +├── Cargo.toml # Project configuration +├── Cargo.lock +├── src/ +│ ├── main.rs # CLI entry point +│ ├── lib.rs # Library exports +│ ├── client.rs # Core client logic +│ ├── account.rs # Account/identity management +│ ├── address.rs # Address parsing +│ ├── store.rs # Local SQLite store +│ ├── crypto.rs # Cryptographic primitives +│ ├── transport.rs # Transport trait +│ ├── tcp_transport.rs # TCP transport implementation +│ └── error.rs # Error types +├── tests/ +│ └── integration/ # Integration tests +├── flake.nix # Nix flake (optional) +└── README.md # This file +``` + +--- + +## Implementation Priority + +### Phase 1: TCP Client (High Priority) + +| # | Module | Description | Dependencies | +|---|--------|-------------|--------------| +| 1 | crypto.rs | Base32, HKDF, HMAC, identity derivation | sha2, hmac, data-encoding | +| 2 | address.rs | Address parsing and validation | regex, anyhow | +| 3 | account.rs | Account and identity management | ed25519-dalek, rand_core | +| 4 | transport.rs | Transport trait and types | None | +| 5 | tcp_transport.rs | TCP/Noise transport | snow, std::net | +| 6 | client.rs | Core operations (REGISTER, AUTH, etc.) | All above | +| 7 | store.rs | SQLite local storage | rusqlite | +| 8 | main.rs | CLI | clap, all above | +| 9 | Tests | Unit and integration tests | All above | + +### Phase 2: RNS Support (Medium Priority) + +| # | Module | Description | Dependencies | +|---|--------|-------------|--------------| +| 1 | rns/ffi.rs | FFI bindings to microReticulum | libffi | +| 2 | rns/transport.rs | RNS transport implementation | ffi.rs | +| 3 | rns/mod.rs | RNS module exports | transport.rs | +| 4 | Update transport.rs | Add RNS feature flag | None | +| 5 | Update client.rs | Handle RNS-specific logic | rns module | +| 6 | Update main.rs | Add RNS CLI commands | rns module | +| 7 | Tests | RNS integration tests | All above | + +--- + +## Testing Strategy + +### Unit Tests +- Address parsing (TCP and RNS) +- Identity generation and rotation +- Message sealing and unsealing +- Envelope format validation +- Token generation and verification + +### Integration Tests +- Register with bunshin server (TCP) +- Send and receive messages (TCP) +- Key rotation interoperability +- Accept token usage +- Error response handling + +### Interoperability Tests +- Against `smolmail.py` reference client +- Against `smolmaild.py` reference server +- Against `smolmail_rns.py` reference client +- Against `smolmaild_rns.py` reference server + +--- + +## Compatibility Matrix + +| Client \ Server | bunshin (TCP) | bunshin (RNS) | smolmaild.py | smolmaild_rns.py | +|------------------|---------------|---------------|---------------|------------------| +| fumi (TCP) | ✅ Full | ❌ No | ✅ Full | ❌ No | +| fumi (RNS) | ❌ No | ✅ Full | ❌ No | ✅ Full | + +Both transports share the same account and store, so a user can use both transports with the same identity. + +--- + +## Key Implementation Notes + +### Message Sealing (SPEC.md S5) + +1. Generate ephemeral X25519 keypair +2. Convert recipient Ed25519 public key to X25519 (per SPEC.md S2) +3. Perform X25519 key agreement +4. Derive two 32-byte keys using HKDF-SHA256: + - `k1 = HKDF(shared, "", "smolmail/1 msg", 64)[0:32]` + - `k2 = HKDF(shared, "", "smolmail/1 msg", 64)[32:64]` +5. Build payload with frontmatter (unencrypted): version, sender, timestamp +6. Build body with: version, timestamp, body_length, body (padded to 1024-byte boundary) +7. Encrypt body with ChaCha20-Poly1305 using k1 +8. Build envelope: magic ("SMOL"), version, recipient, ephemeral_pub, payload + +### Transport Differences + +#### TCP (v1.1) +- Noise_NX handshake (client is initiator, server has static key) +- Frames: u32 length || u8 op || body (split across Noise messages) +- Binding values: Noise handshake hash and server static public key +- Server pinning: Client verifies server's Noise static key + +#### RNS (v1.2) +- Reticulum link establishment +- Frames: u8 op || body (RNS delimits messages) +- Binding values: SHA-256("smolmail/1 bind" || dest_hash || link_id) and SHA-256("smolmail/1 bind" || dest_hash) +- Server pinning: Destination hash IS the server's identity (no separate pinning) + +--- + +## Next Steps + +1. **Implement Phase 1 (TCP Client)** + - Start with crypto, address, and account modules + - Then transport and tcp_transport + - Then client core operations + - Finally store and CLI + - Test against bunshin server + +2. **Implement Phase 2 (RNS Support)** + - Research microReticulum C API + - Create FFI bindings + - Implement RNS transport + - Add tests with reference implementations + +3. **Polish and Package** + - Documentation + - Nix flake integration (optional) + - Performance optimization + - Error handling improvements + +--- + +## References + +- [Smol Mail SPEC.md](https://code.randogoth.com/randogoth/smolmail/src/branch/main/SPEC.md) +- [Smol Mail RNS.md (v1.2)](https://code.randogoth.com/randogoth/smolmail/src/branch/main/RNS.md) +- [smolmail.py - Reference TCP client](https://code.randogoth.com/randogoth/smolmail/src/branch/main/smolmail.py) +- [smolmail_rns.py - Reference RNS client](https://code.randogoth.com/randogoth/smolmail/src/branch/main/smolmail_rns.py) +- [bunshin - Rust server implementation](../bunshin) +- [microReticulum - C++ RNS implementation](https://github.com/attermann/microReticulum)