From ddd1687eca3aa8b47aadc931e00f3dc7da16f8be Mon Sep 17 00:00:00 2001 From: randogoth Date: Wed, 14 Oct 2026 16:53:44 +0300 Subject: [PATCH] feat: add changelog and semver-tagged releases --- CHANGELOG.md | 23 +++++++++++++++++++++++ README.md | 4 ++++ flake.nix | 38 +++++++++++++++++++++++++------------- src/main.rs | 2 +- 4 files changed, 53 insertions(+), 14 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..de88869 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,23 @@ +# Changelog + +All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). The version lives in `Cargo.toml`; the flake and the release apps read it from there. + +## [Unreleased] + +## [0.1.0] - 2026-10-14 + +### Added + +- Smol Mail 1.1 server over Noise_NX TCP: `AUTH`, `RESOLVE`, `SEND`, `FETCH`, `DELETE` and `REGISTER`, with accept tokens, main/requests quota tiers, fetch cursors, `REGISTER` proof of possession and dual-signed key rotation chains. +- Smol Mail 1.2 RNS carrier behind the `rns` feature, over a patched microReticulum fork, with an idle-link reaper, refusal logging and per-link limits. +- Multiple domains in one process, each with its own key, port and database. +- Sliding-window rate limits and a TCP connection cap (`--max-connections`). +- Frame header deadline against slow clients. +- Debug-level per-operation, session and link metrics (`--verbose`). +- Hostile test harness and multi-session driver. +- Nix flake with `packages.default`, `packages.rns`, a static musl `packages.static`, and a NixOS module. +- Container image with a self-provisioning entrypoint. +- `release-static` and `release-container` flake apps publishing to Forgejo. + +[Unreleased]: https://code.randogoth.com/randogoth/bunshin/compare/v0.1.0...HEAD +[0.1.0]: https://code.randogoth.com/randogoth/bunshin/releases/tag/v0.1.0 diff --git a/README.md b/README.md index 9e58b34..cda57c5 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,10 @@ bunshin serve --key server.key --db mail.db --host 0.0.0.0 --port 1961 `--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. +## Releases + +Versions follow [Semantic Versioning](https://semver.org); changes are recorded in [CHANGELOG.md](CHANGELOG.md). To cut a release, bump `version` in `Cargo.toml`, move the `Unreleased` changelog entries under the new version, commit, tag the commit `v` and push the tag, then run `nix run .#release-static` and `nix run .#release-container`. Both refuse to publish unless `HEAD` carries the matching tag; the container is pushed as `:` and `:latest`. + ## 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. diff --git a/flake.nix b/flake.nix index 29158a9..3f58db5 100644 --- a/flake.nix +++ b/flake.nix @@ -69,9 +69,12 @@ RNS_MICROSTORE_SOURCE_DIR = "${rns-microstore}"; }; + # Cargo.toml is the single source of the semver version. + version = (lib.importTOML ./Cargo.toml).package.version; + bunshin = { rns ? false }: pkgs.rustPlatform.buildRustPackage { pname = "bunshin"; - version = "0.1.0"; + inherit version; src = ./.; cargoLock.lockFile = ./Cargo.lock; nativeBuildInputs = [ pkgs.pkg-config ] @@ -97,7 +100,7 @@ # see apps.release-static. packages.static = pkgs.pkgsStatic.rustPlatform.buildRustPackage { pname = "bunshin"; - version = "0.1.0"; + inherit version; src = ./.; cargoLock.lockFile = ./Cargo.lock; nativeBuildInputs = [ pkgs.pkgsStatic.pkg-config ]; @@ -110,9 +113,10 @@ }; # Builds packages.static and publishes it as a Forgejo release on - # this repo, tagged by short commit hash (creating the release if + # this repo, tagged v (creating the release if # it doesn't exist yet, replacing the asset if it does — safe to - # rerun for the same commit). Needs FORGEJO_TOKEN (a token with + # rerun). Refuses unless HEAD carries that tag, so a release always + # matches a bumped version and its CHANGELOG entry. Needs FORGEJO_TOKEN (a token with # write:repository scope) in the environment; run from a checkout # so `git rev-parse` and the `.` flake ref resolve to the right # place. @@ -129,7 +133,11 @@ fi : "''${FORGEJO_TOKEN:?set FORGEJO_TOKEN (env or .env) to a Forgejo token with write:repository scope}" - rev=$(git rev-parse --short HEAD) + tag="v${version}" + if [ "$(git rev-parse -q --verify "$tag^{commit}" || true)" != "$(git rev-parse HEAD)" ]; then + echo "HEAD is not tagged $tag; bump Cargo.toml and CHANGELOG.md, then tag the release commit" >&2 + exit 1 + fi sha=$(git rev-parse HEAD) out=$(nix build .#static --no-link --print-out-paths) bin="$out/bin/bunshin" @@ -137,10 +145,10 @@ api="https://code.randogoth.com/api/v1/repos/randogoth/bunshin" auth=(-H "Authorization: token ''${FORGEJO_TOKEN}") - release_id=$(curl -sS "''${auth[@]}" "$api/releases/tags/$rev" | jq -r '.id // empty') + release_id=$(curl -sS "''${auth[@]}" "$api/releases/tags/$tag" | jq -r '.id // empty') if [ -z "$release_id" ]; then release_id=$(curl -sSf "''${auth[@]}" -H "Content-Type: application/json" \ - -d "$(jq -n --arg tag "$rev" --arg sha "$sha" \ + -d "$(jq -n --arg tag "$tag" --arg sha "$sha" \ '{tag_name:$tag, target_commitish:$sha, name:$tag, body:"Static musl build.", draft:false, prerelease:false}')" \ "$api/releases" | jq -r '.id') fi @@ -151,15 +159,15 @@ fi curl -sSf "''${auth[@]}" -F "attachment=@$bin;filename=bunshin" "$api/releases/$release_id/assets?name=bunshin" >/dev/null - echo "released: https://code.randogoth.com/randogoth/bunshin/releases/tag/$rev" + echo "released: https://code.randogoth.com/randogoth/bunshin/releases/tag/$tag" ''; }; }; # 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) + # Forgejo container registry, tagged with the Cargo.toml version and + # `latest`. Same tag guard as release-static. 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 { @@ -175,12 +183,16 @@ fi : "''${FORGEJO_TOKEN:?set FORGEJO_TOKEN (env or .env) to a Forgejo token with write:package scope}" - rev=$(git rev-parse --short HEAD) + tag="v${version}" + if [ "$(git rev-parse -q --verify "$tag^{commit}" || true)" != "$(git rev-parse HEAD)" ]; then + echo "HEAD is not tagged $tag; bump Cargo.toml and CHANGELOG.md, then tag the release commit" >&2 + exit 1 + fi 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 build -f Containerfile -t "$registry:${version}" -t "$registry:latest" . + podman push "$registry:${version}" podman push "$registry:latest" echo "pushed: https://code.randogoth.com/randogoth/-/packages/container/bunshin" diff --git a/src/main.rs b/src/main.rs index ed1792b..5ff02fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,7 +23,7 @@ use clap::{Parser, Subcommand}; use crate::proto::DEFAULT_PORT; #[derive(Parser)] -#[command(about = "Smol Mail server")] +#[command(version, about = "Smol Mail server")] struct Cli { #[arg(short, long, global = true)] verbose: bool,