You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
pkghaus-archive-keyring takes over the manually fetched trust anchor:
key rotations then arrive as ordinary signed package updates, with no
further manual step.
Versioning
One upstream release is built three times - once per suite, against that
suite's libraries - so the version carries a suite qualifier instead of the
filename:
Suite
Example version
stable (trixie)
0.23.3-1~haus13+1
testing
0.23.3-1~testing1
unstable
0.23.3-1
The qualifiers order below the plain version (~haus13+1 < ~testing1 < 0.23.3-1), so upgrading your system from stable towards unstable upgrades
these packages rather than fighting them. ~haus is this archive's own token,
shaped after Debian's ~bpo backports convention but deliberately not squatting
it - these packages do not come from Debian backports, and the token doubles as
provenance in dpkg -l.
The archive carries one version per package per suite: each package's newest
tag in packages. Older versions are
rebuildable from their tags but not retained.
How it works
packages holds the packaging: one
directory per package, each with a debian/ directory and a
package.conf, built by
action-debian-build's
builder images. Its packages.txt is the enrolment list, and tags are
namespaced by package (croc/v11.3.5-1).
Each validated release triggers the
ingest, which compares each package's newest
tag against the archive, builds only what is missing (natively per
architecture, no emulation), and includes it into an
aptly repository. Each build leg checks that tag
out and runs the same action the packages repository runs on its own tags, so
a package that ships debian/tests/ has those tests run here too. That
matters: the archive builds a tag whenever it first needs it, sometimes weeks
later, against whatever testing and unstable have become since.
Published pool files are immutable: the plan only ever adds missing
versions, and a version already in the archive is never rebuilt.
dists/ and pool/ are objects in an R2 bucket, served
through a Cloudflare Worker. The human-facing tree - the pool's listing pages,
the news log, the keyring - lives on the archive
branch and is served by the same Worker as static assets. Both answer under
apt.pkg.haus; nothing is on GitHub Pages any more.
Every package publishes how it was built and what it was built from, at
buildinfos.pkg.haus. `buildinfo-pool//
/` holds dpkg's `.buildinfo`, naming every build dependency and its
version, a `.source` naming the upstream commit and the compiler that ran, and
the source package itself. A record is only ever published beside its `.dsc`
and tarballs - alone it is useless, because `debrebuild` would fall back to
debsnap, which has never heard of this archive. aptly cannot carry any of them
- its pool is addressed by package identity - so they are written beside it
rather than through it, the way Debian keeps buildinfos.debian.net separate
from the archive. They are not on this host: the layout is a source pool, not
an archive path, and the Worker here is the one that answers `apt update`.
Those records are what make the archive verifiable with debrebuild rather
than only readable, and verify/rebuild.sh is the whole procedure. Given a
.buildinfo, it resolves every build dependency from snapshot.debian.org at
the versions recorded, unpacks the .dsc published beside it, rebuilds, and
compares all four checksums:
Proven end to end against a freshly published record:
checking mandown_1.0.5.2-2~haus13+1_amd64.deb: size... sha256... md5... sha1... all OK, and the rebuilt .deb byte-identical to the one the archive
serves.
The records are kept forever. So are the source tarballs, until the bucket
approaches its budget: one generation across the fleet is 119 MB and additive
growth is 836 MB a year, against a pool flat at 623 MB and a 10 GB free tier,
so scripts/prune-source-tarballs.sh deletes nothing for the next several
years. When it does, it goes oldest first and never touches a tarball a
published version needs. A version leaving the pool is not the trigger: the
.deb it describes is still installed on machines and still in apt caches,
and that is exactly what a rebuild is checked against.
aptly's database lives on the aptly branch. Its package
pool does not: at the bucket's root prefix aptly only reads a package file it
cannot already find published, so each run keeps just what it built.
The archive Worker
worker/ is pkghaus-archive: pool/ and dists/ are objects
in an R2 bucket that only it can read, so it is what makes apt.pkg.haus an archive
rather than a bucket. It also writes the download counters, because it is the
one point every download passes through.
It lives here rather than alongside the page that displays those counters
because this is the repository that owns the archive: the same pipeline that
publishes objects to the bucket deploys the thing that serves them.
The /stats page is a separate Worker in
pkghaus/stats, reading the same database
on a more specific route. Writer here, reader there, so a bad deploy of a page
cannot take the archive down.
cd worker && npm ci && npm test
License
Copyright 2026 pkg.haus
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Buy us a coffee?
If you feel like buying us a coffee (or a beer?), donations are welcome: