Skip to content

Publish the .debs as a signed apt repo on GitHub Pages - #12

Merged
kn4oqw-clint merged 3 commits into
packaging/nfpm-drop-armv6from
feat/apt-repo
Jul 22, 2026
Merged

Publish the .debs as a signed apt repo on GitHub Pages#12
kn4oqw-clint merged 3 commits into
packaging/nfpm-drop-armv6from
feat/apt-repo

Conversation

@kn4oqw-clint

Copy link
Copy Markdown
Contributor

Publishes the nfpm .debs from #11 as a GPG-signed static apt repository on GitHub Pages, using aptly. Validated end-to-end on real armhf hardware.

Stacked on #11 (packaging/nfpm-drop-armv6) — base this PR there, not main. It consumes the waypoint-stack-debs-<arch> artifacts and the packaging/ configs that #11 adds. Merge #11 first, then retarget this to main.

Design decisions

aptly state / rollback model — reconstruct from the published tree (not a state branch). aptly's database is never persisted. Each publish recreates a throwaway repo, re-adds every .deb from the previously published pool/, adds the new .debs, snapshots, and republishes. An aptly repo holds multiple versions of a package, so pool/ accumulates every version ever shipped and the indices list them all — apt can install or downgrade to any of them. The published dists/+pool/ tree is the single source of truth; losing the aptly db costs nothing. This is simpler than persisting a boltdb in a side branch and has no way to silently drop a prior version.

Two signing keys, two scopes (documented in the README):

Key Type Scope
Waypoint archive key GPG RSA 4096 Signs only the apt Release/InRelease.
RFC-0013 release key minisign Trust root for waypointd + reference data.

The GPG key was generated for this repo (fingerprint 1523 696F 24FB 1B2A 9524 3013 41D9 59A8 25C3 D240). Public key committed dearmored as waypoint-archive-keyring.gpg (also served from Pages); private key + passphrase set as the APT_SIGNING_KEY / APT_SIGNING_PASSPHRASE Actions secrets (both set on the repo).

Pages deploy: peaceiris/actions-gh-pages to an orphan gh-pages branch — a branch is trivially checkout-able, which is exactly what the "recover previous pool/" step needs; orphaning keeps the branch from growing unbounded since pool/ already carries the full history forward.

What's here

  • scripts/publish-apt.sh — aptly add → snapshot → publish, GPG-signed, carries prior pool/ forward.
  • .github/workflows/publish-apt.ymlworkflow_run on a successful main "Build stack", or workflow_dispatch with a run id → download artifacts → publish → deploy Pages. (actionlint clean.)
  • apt/waypoint.sources (deb822 client source for the image PR), apt/index.html (install page served at the Pages root), waypoint-archive-keyring.gpg.
  • README "Installing from the apt repo" section (keyring + sources + update/install, downgrade, key scopes). Also removes a stale armv6/debootstrap paragraph that a main merge reintroduced (it referenced the deleted armv6-base.sh).

Local validation (aptly 1.6.2)

  • First publish, then a second publish (--previous) adding a +wp2 repackage: pool/ and the armhf Packages index retain both wp1 and wp2. Signature verifies (gpgv against the committed keyring) — good signature on both InRelease and detached Release.gpg.
  • Full 3-arch publish: binary-amd64, binary-arm64, binary-armhf each list 12 packages; 34 pool files (12×3 − 2 dup arch: all metapackages).

Bench validation (pi-star@172.16.50.13, wpsd, armhf/trixie)

Served the signed tree from the box's own localhost (my host and the Pi are on different subnets), configured /usr/share/keyrings/waypoint-archive-keyring.gpg + /etc/apt/sources.list.d/waypoint.sources.

Signed apt-get update:

Get:1 http://127.0.0.1:8899 bookworm InRelease [10.8 kB]
Get:2 http://127.0.0.1:8899 bookworm/main arm64 Packages
Get:4 http://127.0.0.1:8899 bookworm/main armhf Packages
PASS: InRelease verified, no NO_PUBKEY / insecure warnings

apt-get install waypoint-stack (metapackage 0.2.0) — all 12 packages install, daemons run:

waypoint-stack 0.2.0 + all daemons installed
MMDVM-Host   MMDVM-Host version 20260528 git #
DMRGateway   DMRGateway version 20260323 git #
dstargateway DStarGateway v20260323-- Copyright (C) Geoffrey Merck F4FXL / KC3FRA and Contributors
M17Gateway   M17Gateway version 20250607 git #

Downgrade — daemon level (two versions retained; apt-cache madison lists wp2 and wp1):

before: waypoint-nxdnparrot 0~git18b4e9a+wp2
  The following packages will be DOWNGRADED: waypoint-nxdnparrot
after:  waypoint-nxdnparrot 0~git18b4e9a+wp1
PASS

Downgrade — whole stack (0.2.0 → 0.1.0):

0 upgraded, 0 newly installed, 2 downgraded
Setting up waypoint-nxdnparrot (0~git18b4e9a+wp1) ...
Setting up waypoint-stack (0.1.0) ...
after: stack=0.1.0  nxdnparrot=wp1   PASS

Restore: purged all packages, removed the sources file + keyring, stopped the local server, removed temp files. Final state: 0 waypoint packages, 8 waypointd services running, MMDVM-Host live on /dev/ttyAMA0.

One finding worth flagging

Under apt's new 3.0 solver (trixie), a stack-level downgrade (apt-get install waypoint-stack=0.1.0) needs the changed daemon named explicitly (… waypoint-nxdnparrot=<wp1>) — the solver won't auto-downgrade an already-installed newer dependency to satisfy an older metapackage. Daemon-level downgrade (the task's requirement) works directly. The README documents the daemon-level command; the metapackage pins exact versions, so in normal operation a daemon bump ships with a matching metapackage bump (demonstrated above with waypoint-stack 0.2.0).

To go live (follow-ups, need repo-admin / merge order)

  1. Merge Package the stack as .debs with nfpm; drop ARMv6 #11, retarget this PR to main.
  2. Enable GitHub Pages (source: gh-pages branch) — currently has_pages: false. First workflow run creates the branch.
  3. build.yml must run on main for the workflow_run trigger to fire (or dispatch manually with a run id).

Publish the built .debs as a GPG-signed static apt repository (bookworm,
component main, arches armhf/arm64/amd64).

scripts/publish-apt.sh drives aptly: create a throwaway repo, carry every
.deb from the previously published pool/ forward, add the new .debs, snapshot,
and publish with a detached+inline GPG signature (Release/Release.gpg +
InRelease). The published dists/ + pool/ tree is the only state that matters
-- aptly's own database is never persisted. Because an aptly repo holds
multiple versions of a package, pool/ accumulates every version ever
published and the indices list them all, so apt can install or roll back to
any prior version. That is the apt-side rollback story.

Signing uses a dedicated GPG archive key, used ONLY for the apt
Release/InRelease -- separate from the RFC-0013 minisign release key, which
stays the trust root for waypointd and reference data. apt's trust model
requires GPG, hence the second key. The public key is committed dearmored as
waypoint-archive-keyring.gpg and is served from the Pages site so images and
docs can fetch it; the private key lives only in CI secrets.

apt/waypoint.sources is the deb822 client source (consumed by the image in a
later PR); apt/index.html is the install/downgrade snippet page served at the
Pages root.

Signed-off-by: Clint Chance <clintchance@gmail.com>
Publish the apt repo to GitHub Pages (gh-pages branch) after each successful
"Build stack" run on main, or on manual workflow_dispatch with a build run id.

The job downloads the three per-arch .deb artifact sets, imports the signing
key from the APT_SIGNING_KEY / APT_SIGNING_PASSPHRASE secrets into a throwaway
GNUPGHOME (asserting it matches the committed keyring), recovers the previous
gh-pages tree so pool/ versions carry forward, runs publish-apt.sh, copies the
keyring + sources + install page into the site, and deploys with
peaceiris/actions-gh-pages using an orphan branch to keep gh-pages bounded.

Signed-off-by: Clint Chance <clintchance@gmail.com>
Add an "Installing from the apt repo" section: keyring fetch, the deb822
sources file, apt-get update/install. Document that the repo intentionally
retains prior versions in pool/ and show the daemon downgrade command
(apt-get install <pkg>=<version>). Add a two-key table making the scopes
explicit -- the GPG archive key signs only the apt Release/InRelease, while
the RFC-0013 minisign key remains the trust root for waypointd and reference
data -- and point at the publish script and workflow.

Also drop a stale paragraph that a main merge reintroduced: it described the
removed armv6 debootstrap base and referenced armv6-base.sh, which no longer
exists after the ARMv6 pipeline was dropped.

Signed-off-by: Clint Chance <clintchance@gmail.com>
@kn4oqw-clint
kn4oqw-clint merged commit 19bcccd into packaging/nfpm-drop-armv6 Jul 22, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant