feat: port pneumatube to Rust on fumi-core

This commit is contained in:
randogoth 2026-10-05 11:25:11 +03:00
parent 5dda6567af
commit 65d52bd0e9
37 changed files with 4232 additions and 1912 deletions

View file

@ -4,50 +4,43 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
smolmail = {
url = "git+ssh://git@code.randogoth.com:2222/randogoth/smolmail.git";
fumi = {
url = "git+ssh://git@code.randogoth.com:2222/randogoth/fumi.git";
flake = false;
};
};
outputs = { self, nixpkgs, flake-utils, smolmail }:
outputs = { self, nixpkgs, flake-utils, fumi }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
python = pkgs.python313;
# smolmail.py is a `uv run --script` file with PEP 723 inline deps,
# not an installed package -- loaded by path at runtime (vendor.py),
# same as the devbox workflow's sibling checkout. smolmailEnv gives
# the `smolmail` CLI a working interpreter for one-time account
# bootstrap (keygen/trust/register) without needing uv on the host.
smolmailEnv = python.withPackages (ps: [ ps.pynacl ps.noiseprotocol ]);
smolmailCli = pkgs.writeShellScriptBin "smolmail" ''
exec ${smolmailEnv}/bin/python3 ${smolmail}/smolmail.py "$@"
'';
pneumatubePkg = python.pkgs.buildPythonApplication {
pneumatubePkg = pkgs.rustPlatform.buildRustPackage {
pname = "pneumatube";
version = "0.1.0";
pyproject = true;
src = ./.;
build-system = [ python.pkgs.setuptools python.pkgs.wheel ];
dependencies = with python.pkgs; [ pynacl noiseprotocol aiosmtpd ];
# No unit tests need the real smolmail checkout; the ones that do
# (test_smtp_server, test_lookup_server) stub the bridge instead.
nativeCheckInputs = [ python.pkgs.pytestCheckHook ];
cargoLock.lockFile = ./Cargo.lock;
# Cargo.toml path-depends on a sibling fumi checkout (../fumi/core),
# the same convention the devbox/docker workflows document. The
# build cannot see the sibling, so the flake input's checkout is
# placed inside the source and the path rewritten to match; the
# lockfile entry of a path dependency carries no checksum, so it
# stays valid for either location.
postPatch = ''
substituteInPlace Cargo.toml \
--replace-fail 'path = "../fumi/core"' 'path = "fumi/core"'
mkdir -p fumi
cp -r ${fumi}/core fumi/core
'';
# The unit tests are offline (stubs and an in-memory store).
doCheck = true;
};
in
{
packages.default = pneumatubePkg;
packages.smolmail = smolmailCli;
apps.smolmail = {
type = "app";
program = "${smolmailCli}/bin/smolmail";
};
devShells.default = pkgs.mkShell {
packages = [ python pkgs.uv ];
packages = with pkgs; [ cargo rustc rustfmt clippy sqlite ];
};
}
) // {
@ -66,16 +59,6 @@
description = "pneumatube package to run.";
};
smolmailSrc = mkOption {
type = types.path;
default = smolmail;
description = ''
smolmail checkout providing smolmail.py, loaded by path at
runtime (PNEUMATUBE_SMOLMAIL_REPO) rather than installed as
a package.
'';
};
smolmailDomain = mkOption {
type = types.str;
description = ''
@ -173,8 +156,14 @@
description = "pneumatube smolmail/SMTP gateway";
after = [ "network.target" ] ++ lib.optional cfg.postfix.enable "postfix.service";
wantedBy = [ "multi-user.target" ];
# The gateway's own smolmail account (key + store) is
# bootstrapped with the fumi CLI against these paths, the
# same way any smolmail client registers an account:
# fumi --key /var/lib/pneumatube/gateway.key \
# --db /var/lib/pneumatube/gateway.db keygen
# fumi ... trust <host> <server-key>
# fumi ... register <user>@<host>
environment = {
PNEUMATUBE_SMOLMAIL_REPO = "${cfg.smolmailSrc}";
PNEUMATUBE_SMOLMAIL_KEY = "${stateDir}/gateway.key";
PNEUMATUBE_SMOLMAIL_DB = "${stateDir}/gateway.db";
PNEUMATUBE_SMOLMAIL_DOMAIN = cfg.smolmailDomain;