feat: implement Smol Mail server in Rust with nix flake deployment
This commit is contained in:
commit
71ad7e04c0
14 changed files with 2592 additions and 0 deletions
67
README.md
Normal file
67
README.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# 分身 bunshin
|
||||
|
||||
A Rust implementation of the [Smol Mail](https://code.randogoth.com/randogoth/smolmail) server: a minimalist, end-to-end encrypted mail protocol over a Noise-secured TCP connection. `bunshin` implements the server side only — receiving, storing and serving sealed mail — not the client.
|
||||
|
||||
The server never sees plaintext, sender identities or any private key. It learns only which mailbox an envelope is for, its size, and when it arrived.
|
||||
|
||||
The flake's main purpose is turnkey deployment on a NixOS host: import `nixosModules.default`, point it at a key, and `nixos-rebuild switch`.
|
||||
|
||||
## Deploying on NixOS
|
||||
|
||||
Add bunshin as a flake input and import the module:
|
||||
|
||||
```nix
|
||||
{
|
||||
inputs.bunshin.url = "https://code.randogoth.com/randogoth/bunshin";
|
||||
|
||||
outputs = { self, nixpkgs, bunshin, ... }: {
|
||||
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
|
||||
modules = [
|
||||
bunshin.nixosModules.default
|
||||
{
|
||||
services.bunshin = {
|
||||
enable = true;
|
||||
keyFile = "/var/lib/bunshin/server.key"; # provisioned out of band, see below
|
||||
openFirewall = true;
|
||||
inviteTokenFile = "/run/secrets/bunshin-invite"; # or inviteToken directly
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
`services.bunshin` also takes `host`, `port`, `dataDir`, `maxEnvelope`, `quota`, `retentionDays`, `rateConnections` and `rateSends`; see `flake.nix` for defaults. The module renders a `systemd` unit that runs `bunshin serve` under `DynamicUser`; it does not generate a key.
|
||||
|
||||
Before the first deploy, generate the server's static key once (from a dev shell or `nix run`) and place it at the configured `keyFile`:
|
||||
|
||||
```
|
||||
nix run "https://code.randogoth.com/randogoth/bunshin" -- keygen --key server.key
|
||||
```
|
||||
|
||||
`keygen` writes the server's static X25519 key (used for the Noise handshake, distinct from any user's Ed25519 identity) and prints its public key in base32. Publish that public key through a trusted channel — clients pin it, and a mismatch aborts the handshake.
|
||||
|
||||
## Building
|
||||
|
||||
Requires a Rust toolchain (stable, edition 2021) and a C compiler — `rusqlite`'s `bundled` feature compiles SQLite from source rather than linking a system copy, so no separate SQLite install is needed.
|
||||
|
||||
```
|
||||
cargo build --release
|
||||
cargo test
|
||||
```
|
||||
|
||||
The binary lands at `target/release/bunshin`. With Nix, skip the toolchain setup entirely: `nix build` produces the same binary at `./result/bin/bunshin`.
|
||||
|
||||
Running directly, outside the NixOS module:
|
||||
|
||||
```
|
||||
bunshin keygen --key server.key
|
||||
bunshin serve --key server.key --db mail.db --host 0.0.0.0 --port 1961
|
||||
```
|
||||
|
||||
`serve` accepts `--max-envelope`, `--quota`, `--retention-days`, `--invite-token`, `--rate-connections` and `--rate-sends` to control size limits, per-mailbox quota, message retention, registration gating and abuse control. Run `bunshin serve --help` for defaults.
|
||||
|
||||
## Status
|
||||
|
||||
Implements SPEC.md version 1 in full: `AUTH`, `RESOLVE`, `SEND`, `FETCH`, `DELETE` and `REGISTER` (including invite tokens and key rotation chains). Verified end-to-end against the Python reference client over a live Noise connection, plus a raw-protocol test suite covering the rejection paths (auth failures, unknown users, malformed and oversized envelopes, rate limiting).
|
||||
Loading…
Add table
Add a link
Reference in a new issue