A decision-complete release spec for shipping Fenris as native deb + rpm packages — distribution channel, signing, compatibility matrix, packaging toolchain, CI, and package-manager upgrade semantics — recorded as a spec doc plus an ADR amending ADR 0004 delivery. No packages get built on this map.
Destination = decision spec + ADRs; execution is a separate follow-up effort.
Dependency strategy = bundled venv inside both packages (distro python3-textual too old across the matrix).
Packages become primary delivery; make install demoted to dev fallback. ADR 0004 amended on delivery/ownership only; runtime semantics (dormant install, polkit-only elevation, DB snapshot + forward-only migration) inherited verbatim in maintainer scripts.
Skills: grilling tickets load "grilling" + "domain-modeling" skills; research tickets use the "research" skill.
Decisions so far
Research: deb + rpm packaging toolchain for bundled-venv builds: nfpm wins — one nfpm.yaml (contents type: tree/config|noreplace/ghost + overrides:) builds deb+rpm from staged --copies venv at /opt/fenris; fpm active fallback but CLI-flag config drifts; dh-virtualenv deb-only + dormant since 1.2.2 (2020); hand rpm spec = duplicated file list. Debian 12 + Ubuntu 22.04/24.04 all ship textual 0.1.13 → vendoring confirmed (Fedora 40+ ships ≥0.48 but below pin 8.2.8). Findings: docs/research/deb-rpm-toolchain.md @ branch research/toolchain cca27d0, issue #34 commented + closed.
Research: Gitea 1.27 Debian + RPM package registry feasibility: Feasible — Gitea 1.27.1 at git.bongbetic.com serves deb+rpm natively: PUT …/debian/pool/{dist}/{component}/upload + …/rpm/{group}/upload (dups 409), apt via signed-by keyring + deb …/debian {dist} main, dnf via auto-generated .repo (gpgcheck=1); metadata signed by instance auto-PGP keys (InRelease + repomd.xml.asc, both gpg-verified); free-form multi-distro (tested bookworm+noble, el9 group); versions kept forever (cleanup rules web-UI only, no REST); no package↔release linkage — findings on branch research/gitea-registry in docs/research/gitea-package-registry.md, issue #35 commented + closed, test packages deleted.
Research: OBS as alternative build + distribution route: Reject OBS — offline sandbox forces same vendoring as Gitea route (textual 0.1.13 everywhere), signing is OBS's key not ours, extra account + shared-queue latency for reach we don't need; ship deb+rpm via self-hosted Gitea 1.27.1 registry (branch research/obs, docs/research/obs-route.md, commit 8bcb1fe, issue #36 closed, not pushed).
Task: Check Gitea Actions runner availability on git.bongbetic.com: Actions enabled repo+instance (has_actions:true, admin runners API 200) but zero runners registered → CI unusable, manual build fallback per #33; cheapest fix = single act_runner static binary in host-label mode on existing Gitea host.
Grilling: Lock channel + toolchain: Gitea 1.27.1 registry, single channel, OBS off the route; nfpm for both formats via one packaging/nfpm.yaml (version from pyproject.toml, file lists from staged --copies venv, entry make package); one deb → codename pools bookworm/jammy/noble, one rpm → group fenris; version <pyproject-version>-1, revision bump on rebuild; promotion = tag → make release (manual build + PUT + attach .deb/.rpm/SHA256SUMS) + dormant v*-triggered .gitea/workflows/release.yml.
Grilling: Signing + key policy: rpm payload signed with dedicated RSA-3072 key (single key, no subkeys; private key in password manager, import→sign→delete each release); .deb unsigned (apt trusts instance-signed InRelease); clearsigned SHA256SUMS; Fenris-owned packaging/fenris.repo + in-repo key (Gitea-served .repo is a gpgcheck trap — never in docs); instance-key fingerprint pinned in install docs; rotation = dual-key window, one release cycle.
Grilling: Package ownership + ADR 0004 amendment: units to /usr/lib/systemd/system (postinst daemon-reload only — dormant parity, no preset); sysusers.d + tmpfiles.d fragments with package owning /var/lib/fenris dir only (store never ghosted); /etc/fenris/fenris.conf ships as noreplace conffile (placeholder default); wrapper /usr/bin/fenris; deb remove/purge + rpm erase mapping with prerm sanctioned-disable guarded against upgrade; postinst inherits snapshot + inline-python forward-only migration (no new polkit binaries); no auto legacy import from packages. ADR 0004 amendment outline in issue.
Grilling: Migration path from make-install systems to packages: runbook-only remove-then-install (over-install forbidden — stale /etc units + /usr/local wrapper shadow package files); preinst/%pre aborts on make-install remnants (manifest.txt OR /etc/systemd/system/fenris-collect.timer), no auto-clean; no-move store/config continuity (group/sysusers + dir/tmpfiles no-ops, user fenris.conf survives as .dpkg-new/.rpmnew); reset-to-dormant, user runs fenris monitor resume; package + make install mutually exclusive per machine.
Grilling: Release cadence + stable/testing channel split: No split — single channel, split deferred until real users demand it (testing dist = permanent clutter: registry retains all versions forever, no cleanup API); on-demand releases (tag when user-visible changes/fixes accumulate, no calendar, no empty releases); plain semver (major = breaking CLI/config/unit, schema changes ride natural bump); downgrade unsupported — rollback = restore store snapshot + install old release, documented not built; fix-forward via revision bump, no RC ceremony; release = tag + registry upload + Gitea release entry with notes + SHA256SUMS, bare tags forbidden; no frequency SLA. CONTEXT.md gained Release + Rollback terms.
Task: Compose release spec + ADR amending 0004: docs/spec/release-packaging.md (decision-complete: compat matrix, channel, nfpm toolchain, signing, release mechanics, layout/ownership, maintainer scripts, migration runbook) + ADR 0007 amending 0004 on delivery/ownership; research docs merged to docs/research/; #40 open item resolved (device selector hand-edited, no new verb). Branch task/release-spec-adr, commit b005049 — map decision-complete.
Not yet specified
Dev-local make install mode (checkout venv + user-level units) for dev machines that also carry packages — deferred; only graduates if the mutual-exclusion rule bites in practice.
Stable/testing channel split — deferred until real users demand it; revisit criterion = external users asking to track pre-release builds.
COPR/PPA fallback if the chosen channel proves limiting.
GPG key rotation procedure details.
Instance auto-key lifecycle: what happens to apt clients (and our pinned fingerprint docs) if the Gitea instance is migrated/reinstalled and its metadata signing key regenerates? Revisit if/when instance migration is planned.
arm64 builds, only if real ARM hardware appears.
Out of scope
Snap, Flatpak, AppImage, Homebrew packaging (sandbox hostile to polkit + systemd timer + smartctl raw device access).
PyPI as an install path (wheel stays a build/dev artifact).
Building and publishing actual packages — execution follows this map as a fresh effort.
## Destination
A decision-complete release spec for shipping Fenris as native deb + rpm packages — distribution channel, signing, compatibility matrix, packaging toolchain, CI, and package-manager upgrade semantics — recorded as a spec doc plus an ADR amending [ADR 0004](https://git.bongbetic.com/xavierk/Fenris/src/branch/main/docs/adr/0004-install-upgrade-removal-lifecycle.md) delivery. No packages get built on this map.
## Notes
Domain: Fenris NVMe endurance monitor (Python, single runtime dep `textual>=0.40`, systemd timer + polkit + `/var/lib/fenris` observation store). Charting-session decisions already locked:
- Destination = decision spec + ADRs; execution is a separate follow-up effort.
- Dependency strategy = bundled venv inside both packages (distro `python3-textual` too old across the matrix).
- Packages become primary delivery; `make install` demoted to dev fallback. ADR 0004 amended on delivery/ownership only; runtime semantics (dormant install, polkit-only elevation, DB snapshot + forward-only migration) inherited verbatim in maintainer scripts.
- Candidate channel = Gitea Debian/RPM package registry (instance runs 1.27.1); OBS evaluated as alternative route; dedicated GPG packaging key; packages signed.
- Compat matrix candidates: Debian 12, Ubuntu 22.04/24.04, Fedora 40+; x86_64 only; Python floor bumped to 3.10 (oldest supported distro interpreter).
Skills: grilling tickets load "grilling" + "domain-modeling" skills; research tickets use the "research" skill.
## Decisions so far
- [Research: deb + rpm packaging toolchain for bundled-venv builds](https://git.bongbetic.com/xavierk/Fenris/issues/34): nfpm wins — one `nfpm.yaml` (contents `type: tree/config|noreplace/ghost` + `overrides:`) builds deb+rpm from staged `--copies` venv at /opt/fenris; fpm active fallback but CLI-flag config drifts; dh-virtualenv deb-only + dormant since 1.2.2 (2020); hand rpm spec = duplicated file list. Debian 12 + Ubuntu 22.04/24.04 all ship textual 0.1.13 → vendoring confirmed (Fedora 40+ ships ≥0.48 but below pin 8.2.8). Findings: `docs/research/deb-rpm-toolchain.md` @ branch `research/toolchain` cca27d0, issue #34 commented + closed.
- [Research: Gitea 1.27 Debian + RPM package registry feasibility](https://git.bongbetic.com/xavierk/Fenris/issues/35): Feasible — Gitea 1.27.1 at git.bongbetic.com serves deb+rpm natively: PUT `…/debian/pool/{dist}/{component}/upload` + `…/rpm/{group}/upload` (dups 409), apt via signed-by keyring + `deb …/debian {dist} main`, dnf via auto-generated `.repo` (gpgcheck=1); metadata signed by instance auto-PGP keys (InRelease + repomd.xml.asc, both gpg-verified); free-form multi-distro (tested bookworm+noble, el9 group); versions kept forever (cleanup rules web-UI only, no REST); no package↔release linkage — findings on branch `research/gitea-registry` in `docs/research/gitea-package-registry.md`, issue #35 commented + closed, test packages deleted.
- [Research: OBS as alternative build + distribution route](https://git.bongbetic.com/xavierk/Fenris/issues/36): Reject OBS — offline sandbox forces same vendoring as Gitea route (textual 0.1.13 everywhere), signing is OBS's key not ours, extra account + shared-queue latency for reach we don't need; ship deb+rpm via self-hosted Gitea 1.27.1 registry (branch `research/obs`, `docs/research/obs-route.md`, commit 8bcb1fe, issue #36 closed, not pushed).
- [Task: Check Gitea Actions runner availability on git.bongbetic.com](https://git.bongbetic.com/xavierk/Fenris/issues/37): Actions enabled repo+instance (`has_actions:true`, admin runners API 200) but zero runners registered → CI unusable, manual build fallback per #33; cheapest fix = single `act_runner` static binary in host-label mode on existing Gitea host.
- [Grilling: Lock channel + toolchain](https://git.bongbetic.com/xavierk/Fenris/issues/38): Gitea 1.27.1 registry, single channel, OBS off the route; nfpm for both formats via one `packaging/nfpm.yaml` (version from `pyproject.toml`, file lists from staged `--copies` venv, entry `make package`); one deb → codename pools `bookworm`/`jammy`/`noble`, one rpm → group `fenris`; version `<pyproject-version>-1`, revision bump on rebuild; promotion = tag → `make release` (manual build + PUT + attach `.deb`/`.rpm`/`SHA256SUMS`) + dormant `v*`-triggered `.gitea/workflows/release.yml`.
- [Grilling: Signing + key policy](https://git.bongbetic.com/xavierk/Fenris/issues/39): rpm payload signed with dedicated RSA-3072 key (single key, no subkeys; private key in password manager, import→sign→delete each release); .deb unsigned (apt trusts instance-signed InRelease); clearsigned SHA256SUMS; Fenris-owned `packaging/fenris.repo` + in-repo key (Gitea-served `.repo` is a gpgcheck trap — never in docs); instance-key fingerprint pinned in install docs; rotation = dual-key window, one release cycle.
- [Grilling: Package ownership + ADR 0004 amendment](https://git.bongbetic.com/xavierk/Fenris/issues/40): units to /usr/lib/systemd/system (postinst daemon-reload only — dormant parity, no preset); sysusers.d + tmpfiles.d fragments with package owning /var/lib/fenris dir only (store never ghosted); /etc/fenris/fenris.conf ships as noreplace conffile (placeholder default); wrapper /usr/bin/fenris; deb remove/purge + rpm erase mapping with prerm sanctioned-disable guarded against upgrade; postinst inherits snapshot + inline-python forward-only migration (no new polkit binaries); no auto legacy import from packages. ADR 0004 amendment outline in issue.
- [Grilling: Migration path from make-install systems to packages](https://git.bongbetic.com/xavierk/Fenris/issues/41): runbook-only remove-then-install (over-install forbidden — stale /etc units + /usr/local wrapper shadow package files); preinst/%pre aborts on make-install remnants (manifest.txt OR /etc/systemd/system/fenris-collect.timer), no auto-clean; no-move store/config continuity (group/sysusers + dir/tmpfiles no-ops, user fenris.conf survives as .dpkg-new/.rpmnew); reset-to-dormant, user runs fenris monitor resume; package + make install mutually exclusive per machine.
- [Grilling: Release cadence + stable/testing channel split](https://git.bongbetic.com/xavierk/Fenris/issues/43): No split — single channel, split deferred until real users demand it (testing dist = permanent clutter: registry retains all versions forever, no cleanup API); on-demand releases (tag when user-visible changes/fixes accumulate, no calendar, no empty releases); plain semver (major = breaking CLI/config/unit, schema changes ride natural bump); downgrade unsupported — rollback = restore store snapshot + install old release, documented not built; fix-forward via revision bump, no RC ceremony; release = tag + registry upload + Gitea release entry with notes + SHA256SUMS, bare tags forbidden; no frequency SLA. CONTEXT.md gained Release + Rollback terms.
- [Task: Compose release spec + ADR amending 0004](https://git.bongbetic.com/xavierk/Fenris/issues/42): `docs/spec/release-packaging.md` (decision-complete: compat matrix, channel, nfpm toolchain, signing, release mechanics, layout/ownership, maintainer scripts, migration runbook) + ADR 0007 amending 0004 on delivery/ownership; research docs merged to `docs/research/`; #40 open item resolved (device selector hand-edited, no new verb). Branch `task/release-spec-adr`, commit b005049 — map decision-complete.
## Not yet specified
- Dev-local make install mode (checkout venv + user-level units) for dev machines that also carry packages — deferred; only graduates if the mutual-exclusion rule bites in practice.
- Stable/testing channel split — deferred until real users demand it; revisit criterion = external users asking to track pre-release builds.
- COPR/PPA fallback if the chosen channel proves limiting.
- GPG key rotation procedure details.
- Instance auto-key lifecycle: what happens to apt clients (and our pinned fingerprint docs) if the Gitea instance is migrated/reinstalled and its metadata signing key regenerates? Revisit if/when instance migration is planned.
- arm64 builds, only if real ARM hardware appears.
## Out of scope
- Snap, Flatpak, AppImage, Homebrew packaging (sandbox hostile to polkit + systemd timer + smartctl raw device access).
- PyPI as an install path (wheel stays a build/dev artifact).
- Building and publishing actual packages — execution follows this map as a fresh effort.
xavierk
added this to the Wayfinder: Fenris deb + rpm release plan milestone 2026-09-02 18:31:17 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Destination
A decision-complete release spec for shipping Fenris as native deb + rpm packages — distribution channel, signing, compatibility matrix, packaging toolchain, CI, and package-manager upgrade semantics — recorded as a spec doc plus an ADR amending ADR 0004 delivery. No packages get built on this map.
Notes
Domain: Fenris NVMe endurance monitor (Python, single runtime dep
textual>=0.40, systemd timer + polkit +/var/lib/fenrisobservation store). Charting-session decisions already locked:python3-textualtoo old across the matrix).make installdemoted to dev fallback. ADR 0004 amended on delivery/ownership only; runtime semantics (dormant install, polkit-only elevation, DB snapshot + forward-only migration) inherited verbatim in maintainer scripts.Skills: grilling tickets load "grilling" + "domain-modeling" skills; research tickets use the "research" skill.
Decisions so far
Research: deb + rpm packaging toolchain for bundled-venv builds: nfpm wins — one
nfpm.yaml(contentstype: tree/config|noreplace/ghost+overrides:) builds deb+rpm from staged--copiesvenv at /opt/fenris; fpm active fallback but CLI-flag config drifts; dh-virtualenv deb-only + dormant since 1.2.2 (2020); hand rpm spec = duplicated file list. Debian 12 + Ubuntu 22.04/24.04 all ship textual 0.1.13 → vendoring confirmed (Fedora 40+ ships ≥0.48 but below pin 8.2.8). Findings:docs/research/deb-rpm-toolchain.md@ branchresearch/toolchaincca27d0, issue #34 commented + closed.Research: Gitea 1.27 Debian + RPM package registry feasibility: Feasible — Gitea 1.27.1 at git.bongbetic.com serves deb+rpm natively: PUT
…/debian/pool/{dist}/{component}/upload+…/rpm/{group}/upload(dups 409), apt via signed-by keyring +deb …/debian {dist} main, dnf via auto-generated.repo(gpgcheck=1); metadata signed by instance auto-PGP keys (InRelease + repomd.xml.asc, both gpg-verified); free-form multi-distro (tested bookworm+noble, el9 group); versions kept forever (cleanup rules web-UI only, no REST); no package↔release linkage — findings on branchresearch/gitea-registryindocs/research/gitea-package-registry.md, issue #35 commented + closed, test packages deleted.Research: OBS as alternative build + distribution route: Reject OBS — offline sandbox forces same vendoring as Gitea route (textual 0.1.13 everywhere), signing is OBS's key not ours, extra account + shared-queue latency for reach we don't need; ship deb+rpm via self-hosted Gitea 1.27.1 registry (branch
research/obs,docs/research/obs-route.md, commit 8bcb1fe, issue #36 closed, not pushed).Task: Check Gitea Actions runner availability on git.bongbetic.com: Actions enabled repo+instance (
has_actions:true, admin runners API 200) but zero runners registered → CI unusable, manual build fallback per #33; cheapest fix = singleact_runnerstatic binary in host-label mode on existing Gitea host.Grilling: Lock channel + toolchain: Gitea 1.27.1 registry, single channel, OBS off the route; nfpm for both formats via one
packaging/nfpm.yaml(version frompyproject.toml, file lists from staged--copiesvenv, entrymake package); one deb → codename poolsbookworm/jammy/noble, one rpm → groupfenris; version<pyproject-version>-1, revision bump on rebuild; promotion = tag →make release(manual build + PUT + attach.deb/.rpm/SHA256SUMS) + dormantv*-triggered.gitea/workflows/release.yml.Grilling: Signing + key policy: rpm payload signed with dedicated RSA-3072 key (single key, no subkeys; private key in password manager, import→sign→delete each release); .deb unsigned (apt trusts instance-signed InRelease); clearsigned SHA256SUMS; Fenris-owned
packaging/fenris.repo+ in-repo key (Gitea-served.repois a gpgcheck trap — never in docs); instance-key fingerprint pinned in install docs; rotation = dual-key window, one release cycle.Grilling: Package ownership + ADR 0004 amendment: units to /usr/lib/systemd/system (postinst daemon-reload only — dormant parity, no preset); sysusers.d + tmpfiles.d fragments with package owning /var/lib/fenris dir only (store never ghosted); /etc/fenris/fenris.conf ships as noreplace conffile (placeholder default); wrapper /usr/bin/fenris; deb remove/purge + rpm erase mapping with prerm sanctioned-disable guarded against upgrade; postinst inherits snapshot + inline-python forward-only migration (no new polkit binaries); no auto legacy import from packages. ADR 0004 amendment outline in issue.
Grilling: Migration path from make-install systems to packages: runbook-only remove-then-install (over-install forbidden — stale /etc units + /usr/local wrapper shadow package files); preinst/%pre aborts on make-install remnants (manifest.txt OR /etc/systemd/system/fenris-collect.timer), no auto-clean; no-move store/config continuity (group/sysusers + dir/tmpfiles no-ops, user fenris.conf survives as .dpkg-new/.rpmnew); reset-to-dormant, user runs fenris monitor resume; package + make install mutually exclusive per machine.
Grilling: Release cadence + stable/testing channel split: No split — single channel, split deferred until real users demand it (testing dist = permanent clutter: registry retains all versions forever, no cleanup API); on-demand releases (tag when user-visible changes/fixes accumulate, no calendar, no empty releases); plain semver (major = breaking CLI/config/unit, schema changes ride natural bump); downgrade unsupported — rollback = restore store snapshot + install old release, documented not built; fix-forward via revision bump, no RC ceremony; release = tag + registry upload + Gitea release entry with notes + SHA256SUMS, bare tags forbidden; no frequency SLA. CONTEXT.md gained Release + Rollback terms.
Task: Compose release spec + ADR amending 0004:
docs/spec/release-packaging.md(decision-complete: compat matrix, channel, nfpm toolchain, signing, release mechanics, layout/ownership, maintainer scripts, migration runbook) + ADR 0007 amending 0004 on delivery/ownership; research docs merged todocs/research/; #40 open item resolved (device selector hand-edited, no new verb). Branchtask/release-spec-adr, commitb005049— map decision-complete.Not yet specified
Dev-local make install mode (checkout venv + user-level units) for dev machines that also carry packages — deferred; only graduates if the mutual-exclusion rule bites in practice.
Stable/testing channel split — deferred until real users demand it; revisit criterion = external users asking to track pre-release builds.
COPR/PPA fallback if the chosen channel proves limiting.
GPG key rotation procedure details.
Instance auto-key lifecycle: what happens to apt clients (and our pinned fingerprint docs) if the Gitea instance is migrated/reinstalled and its metadata signing key regenerates? Revisit if/when instance migration is planned.
arm64 builds, only if real ARM hardware appears.
Out of scope