A Rust implementation of the Smol Mail server: a minimalist, end-to-end encrypted mail protocol http://smol.place
Find a file
2026-09-29 09:52:09 +03:00
shim feat: implement RNS transport (Smol Mail 1.2) over microReticulum 2026-09-28 15:52:37 +03:00
src feat: add debug-level per-op, session and link metrics 2026-09-29 09:52:09 +03:00
.gitignore feat: implement Smol Mail server in Rust with nix flake deployment 2026-09-26 11:55:53 +03:00
AI-DECLARATION.md docs: license project under Apache-2.0 2026-09-28 22:36:59 +03:00
build.rs feat: implement RNS transport (Smol Mail 1.2) over microReticulum 2026-09-28 15:52:37 +03:00
Cargo.lock feat: implement RNS transport (Smol Mail 1.2) over microReticulum 2026-09-28 15:52:37 +03:00
Cargo.toml docs: license project under Apache-2.0 2026-09-28 22:36:59 +03:00
flake.lock feat: implement RNS transport (Smol Mail 1.2) over microReticulum 2026-09-28 15:52:37 +03:00
flake.nix feat: add debug-level per-op, session and link metrics 2026-09-29 09:52:09 +03:00
LICENSE docs: license project under Apache-2.0 2026-09-28 22:36:59 +03:00
README.md feat: add debug-level per-op, session and link metrics 2026-09-29 09:52:09 +03:00
RNS.md feat: implement RNS transport (Smol Mail 1.2) over microReticulum 2026-09-28 15:52:37 +03:00
THIRD_PARTY_NOTICES.md docs: generalize third-party notices wording 2026-09-28 22:51:04 +03:00

分身 bunshin

License: Apache-2.0 AI-DECLARATION: auto

A Rust implementation of the Smol Mail server: a minimalist, end-to-end encrypted mail protocol over a Noise-secured TCP connection, with an optional Reticulum (RNS) mesh carrier behind the rns build feature. 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:

{
  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, requestsQuota, retentionDays, requestsRetentionDays, maxTokens, rateConnections, rateSends and rateTokens; see flake.nix for defaults. The module renders a systemd unit that runs bunshin serve under DynamicUser; it does not generate a key.

For the RNS carrier, build packages.rns, set services.bunshin.package to it, and enable services.bunshin.rns with its keyFile; see RNS.md for the protocol and the remaining options.

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.

RNS carrier

The rns feature (built by packages.rns, wired to the module through services.bunshin.rns) serves the same mailbox over the Reticulum Network Stack alongside TCP: bunshin serve --rns runs both carriers in one process against one database. The mesh address is the smolmail.server destination hash — there is no key to pin, the address is the pin.

bunshin rns-keygen --key server.rns.key
bunshin serve --rns --key server.key --rns-key server.rns.key --db mail.db --rns-udp 0.0.0.0:4242

rns-keygen writes the 64-byte Reticulum identity and prints the destination hash; publish smol+rns://<user>@<hash> as the server's address. RNS-specific limits (--rns-max-envelope, --rns-fetch-budget, --rns-max-links, --rns-rate-link-requests, --rns-rate-link-bytes) default to mesh-friendly 32 KiB caps; the UDP interface is the one supported transport interface so far. See RNS.md.

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. The rns feature additionally needs cmake, ninja and a microReticulum checkout pointed at by MICRORETICULUM_SOURCE_DIR — the flake's dev shell provides all of it.

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, --requests-quota, --retention-days, --requests-retention-days, --max-tokens, --invite-token, --rate-connections, --rate-sends and --rate-tokens to control size limits, the mailbox's two quota tiers, their retention, the accept-token cap, registration gating and abuse control. Run bunshin serve --help for defaults.

--verbose (or services.bunshin.verbose in the NixOS module) raises logging to debug level, which adds server-side metrics on both carriers: per-operation timing, request and response sizes, handshake duration, per-session summaries, and RNS link events. The default info level stays quiet on success.

Status

Implements SPEC.md version 1.1 in full: AUTH, RESOLVE, SEND, FETCH, DELETE and REGISTER, including accept tokens and the main/requests tier split, fetch cursors, 32-byte message ids, REGISTER proof of possession, and dual-signed key rotation chains.

With the rns feature, implements the version 1.2 RNS transport: the same six operations over Reticulum Links to an announced smolmail.server destination, with link-bound AUTH/REGISTER signatures, per-link request and byte limits, and a concurrent-link cap. One server process can serve both carriers over one store.

License

Licensed under the Apache License, Version 2.0 (LICENSE). Third-party dependency licenses are listed in THIRD_PARTY_NOTICES.md.