feat: add container deploy path (Containerfile, self-provisioning entrypoint, Forgejo registry push)

This commit is contained in:
randogoth 2026-10-09 12:56:27 +03:00
parent fd847d035f
commit 50e5e444bc
5 changed files with 100 additions and 3 deletions

13
.dockerignore Normal file
View file

@ -0,0 +1,13 @@
/target
/result
/result-*
/cache
/config
/fumi.rns
.git
.jj
.env
*.db
*.db-wal
*.db-shm
server.key

22
Containerfile Normal file
View file

@ -0,0 +1,22 @@
# Default (non-rns) build only: microReticulum's cmake/C++ build isn't set
# up for the musl toolchain alpine gives us, mirroring packages.static in
# flake.nix. Use the Nix flake if you need the rns feature.
FROM rust:1-alpine AS builder
RUN apk add --no-cache musl-dev gcc
WORKDIR /usr/src/bunshin
COPY Cargo.toml Cargo.lock build.rs ./
COPY src ./src
RUN cargo build --release --locked
FROM alpine:3.20
RUN apk add --no-cache ca-certificates \
&& adduser -D -h /data -u 10000 bunshin
COPY --from=builder /usr/src/bunshin/target/release/bunshin /usr/local/bin/bunshin
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
USER bunshin
WORKDIR /data
VOLUME /data
EXPOSE 1961/tcp
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["serve", "--key", "/data/server.key", "--db", "/data/mail.db", "--host", "0.0.0.0", "--port", "1961"]

View file

@ -6,11 +6,29 @@ A Rust implementation of the [Smol Mail](https://code.randogoth.com/randogoth/sm
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 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`. The quickest way to run it is the container image below. For a NixOS host, the flake also provides turnkey deployment as a special case: import `nixosModules.default`, point it at a key, and `nixos-rebuild switch`.
## Deploying with a container
Pull the published image and bring up a server in one command:
```
podman run -d --name bunshin -p 1961:1961 -v bunshin-data:/data code.randogoth.com/randogoth/bunshin
```
`docker` works the same way — the image is a standard OCI image either way. The entrypoint generates `/data/server.key` on first run if it's missing, then runs `serve` against `/data/server.key` and `/data/mail.db` on `0.0.0.0:1961`; `podman logs bunshin` prints the generated public key to publish to clients. A key already in the volume is left alone, so restarts and upgrades keep the same identity.
Pass your own arguments to run `keygen` or a customized `serve` instead of the default — they take the same flags as a bare-metal install, e.g. `podman run --rm -v bunshin-data:/data code.randogoth.com/randogoth/bunshin keygen --key /data/server.key --force`.
To build the image locally instead of pulling (same non-`rns` build as the release image, see the `Containerfile` header comment):
```
podman build -t bunshin -f Containerfile .
```
## Deploying on NixOS ## Deploying on NixOS
Add bunshin as a flake input and import the module: For a NixOS host that already manages the rest of its config with Nix, import the module instead of running the container:
```nix ```nix
{ {
@ -34,7 +52,7 @@ Add bunshin as a flake input and import the module:
} }
``` ```
`services.bunshin` also takes `host`, `port`, `dataDir`, `maxEnvelope`, `quota`, `requestsQuota`, `retentionDays`, `requestsRetentionDays`, `maxTokens`, `rateConnections`, `rateSends`, `rateTokens`, `maxConnections` and `domains`; see `flake.nix` for defaults. The module renders a `systemd` unit that runs `bunshin serve` under `DynamicUser`; it does not generate a key. `services.bunshin` also takes `host`, `port`, `dataDir`, `maxEnvelope`, `quota`, `requestsQuota`, `retentionDays`, `requestsRetentionDays`, `maxTokens`, `rateConnections`, `rateSends`, `rateTokens`, `maxConnections` and `domains`; see `flake.nix` for defaults. The module renders a `systemd` unit that runs `bunshin serve` under `DynamicUser`; unlike the container entrypoint, 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](RNS.md) for the protocol and the remaining options. For the RNS carrier, build `packages.rns`, set `services.bunshin.package` to it, and enable `services.bunshin.rns` with its `keyFile`; see [RNS.md](RNS.md) for the protocol and the remaining options.

12
docker-entrypoint.sh Normal file
View file

@ -0,0 +1,12 @@
#!/bin/sh
# Generates the server key on first run so a bare `docker run` against an
# empty volume works; a key already at BUNSHIN_KEY is left untouched.
set -e
: "${BUNSHIN_KEY:=/data/server.key}"
if [ "$1" = "serve" ] && [ ! -f "$BUNSHIN_KEY" ]; then
bunshin keygen --key "$BUNSHIN_KEY"
fi
exec bunshin "$@"

View file

@ -156,6 +156,38 @@
}; };
}; };
# Builds the Containerfile image (same non-rns build the release
# binary is, see its header comment) and pushes it to this repo's
# Forgejo container registry, tagged by short commit hash and
# `latest`. Needs FORGEJO_TOKEN (a token with write:package scope)
# in the environment; run from a checkout so `git rev-parse` and
# the Containerfile build context resolve to the right place.
apps.release-container = flake-utils.lib.mkApp {
drv = pkgs.writeShellApplication {
name = "bunshin-release-container";
runtimeInputs = [ pkgs.podman pkgs.git ];
text = ''
if [ -f .env ]; then
set -a
# shellcheck disable=SC1091
. ./.env
set +a
fi
: "''${FORGEJO_TOKEN:?set FORGEJO_TOKEN (env or .env) to a Forgejo token with write:package scope}"
rev=$(git rev-parse --short HEAD)
registry="code.randogoth.com/randogoth/bunshin"
echo "''${FORGEJO_TOKEN}" | podman login code.randogoth.com -u randogoth --password-stdin
podman build -f Containerfile -t "$registry:$rev" -t "$registry:latest" .
podman push "$registry:$rev"
podman push "$registry:latest"
echo "pushed: https://code.randogoth.com/randogoth/-/packages/container/bunshin"
'';
};
};
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
packages = with pkgs; [ cargo rustc rustfmt clippy pkg-config gcc sqlite cmake ninja ]; packages = with pkgs; [ cargo rustc rustfmt clippy pkg-config gcc sqlite cmake ninja ];
env = rnsSourceEnv; env = rnsSourceEnv;