Debian (APT)
APT repository with dists/ and pool/ layout; indexes and Release files are generated by a background worker and optionally GPG-signed.
| Format id | deb |
| Repo URL | HOST/repos/REPO (suite dists/<distribution>/, packages pool/) |
| Repository types | hosted, proxy, group (details) |
Client setup
internal/clientsetup produces the sources.list entry and the upload command:
echo "deb [signed-by=/etc/apt/keyrings/REPO.gpg] HOST/repos/REPO stable main" | sudo tee /etc/apt/sources.list.d/REPO.list
sudo apt update
curl -u USERNAME:<TOKEN> -F file=@my-package_1.0_amd64.deb -F distribution=stable -F component=main \
HOST/api/v1/repositories/REPO/debsThe same snippet first fetches HOST/repos/REPO/public.key into /etc/apt/keyrings/REPO.gpg. The server writes public.key with the repository metadata whenever a signing key applies to the repository (the repository's own key, the global active key, or the GPG_KEY_PATH file; see Signing), so the URL works the same way as for RPM repositories:
curl -fsSL HOST/repos/REPO/public.key | sudo gpg --dearmor -o /etc/apt/keyrings/REPO.gpgWithout a signing key public.key, InRelease and Release.gpg return 404; mark the source [trusted=yes] only on networks you control. A key that is activated (or removed) after packages were uploaded re-signs the repository straight away.
For private repositories apt also needs credentials, for example in /etc/apt/auth.conf.d/ for the host (login USERNAME, password the token).
Upload API
POST /api/v1/repositories/REPO/debs (/packages is accepted too and dispatches to the Debian handler for deb repositories). Needs write permission on REPO (RBAC).
| Form field | Default | Notes |
|---|---|---|
file | required | the .deb |
distribution | stable | ^[A-Za-z0-9][A-Za-z0-9._\-]*$ |
component | main | same pattern |
- Package name, version and architecture come from the
.debcontrol file and must match^[a-z0-9][a-z0-9+.\-]+$,^[A-Za-z0-9][A-Za-z0-9.+~:\-]*$and^[a-z0-9][a-z0-9\-]*$. An unreadable.debis400 invalid deb package. - Maximum 500 MiB (
413 file exceeds maximum size limit); quota is enforced (quotas). - The same SHA-256 in the repository, or an existing
name_version_arch.deb(epoch ignored), is409 CONFLICTwithdata.duplicate: true; packages are immutable. - Success:
{"success": true, "data": {id, name, version, architecture, sha256, blob_id, size, distribution, component}}. DELETE /api/v1/repositories/REPO/debs/<id>deletes one package by id (only if it belongs toREPO).- Uploads to a non-
debrepository are400; to a proxy repository405. - There is no
/repos/REPO/uploadroute:POSTorPUTthere answers404with a message namingPOST /api/v1/repositories/REPO/debs.
Generated layout
Served relative to HOST/repos/REPO:
| Path | Notes |
|---|---|
dists/<dist>/Release | Origin/Label openartifact, Suite/Codename = distribution, MD5Sum, SHA1, SHA256 |
dists/<dist>/InRelease, Release.gpg | only when signing is configured, otherwise 404 |
dists/<dist>/<comp>/binary-<arch>/Packages, .gz, .xz | one per component and architecture |
pool/<comp>/<prefix>/<package>/<file>.deb | package download |
- A debounced worker (500 ms after an upload or delete) regenerates the indexes of the repository. Until it has run,
ReleaseandPackages*are streamed from the database instead. - Architecture
allpackages are listed in every real architecture index; a distribution with onlyallpackages getsbinary-all. - Generated files with identical content (for example the empty
Packagesof two repositories) share one stored blob. A superseded blob is deleted only once no repository's metadata and no package references it, and every run re-checks that the blobs of its committed set still exist, so concurrent regenerations of different repositories cannot leave an index without its file. - Package files are looked up by file name, honour quarantine, and are served with
Content-Disposition.
Signing
Set GPG_ENABLED=true, GPG_KEY_PATH and GPG_PASSPHRASE to sign InRelease and Release.gpg (signing). Without a key apt refuses the repository unless the source line is marked [trusted=yes]. Release.gpg is a standard ASCII-armored detached signature that gpgv and apt verify (gpgv --keyring KEYRING Release.gpg Release); before 0.5.0 it was armored twice and only InRelease was usable. It is rewritten the next time the repository's indexes are regenerated (any upload or delete). Individual .deb files are not signed: apt verifies them through the checksums in the signed Release.
Proxy
A Debian proxy repository mirrors dists/ and pool/ from upstream_url. .deb and .udeb files below a pool/ directory (the Debian archive layout, where a file name is never reused) are immutable once cached; the same files in a flat repository, and everything else (indexes, Release) is served from cache until cache_ttl_seconds (default 1 hour) and then revalidated. The upstream's signed InRelease is passed through unchanged, so clients must trust the upstream's key. Negative caching and serve-stale: hosted, proxy, group.
Group
A Debian group publishes merged Release, InRelease, Release.gpg and Packages.gz/Packages.xz for each distribution at least one member has, signed with the server key, and serves pool/ files from the member that owns them. Use it like a hosted repository: deb [signed-by=/etc/apt/keyrings/REPO.asc] HOST/repos/GROUP bookworm main, with the key from HOST/repos/GROUP/public.key. Merging rules, regeneration and limits: Deb and RPM groups.