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 <vibe@mistral.ai>
This commit is contained in:
commit
1690aed3dc
1 changed files with 399 additions and 0 deletions
399
README.md
Normal file
399
README.md
Normal file
|
|
@ -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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue