The registries¶
The service serves artifacts back as registries: docker pull and
apt install work directly against it, with no registry of your
own to run. Read-serving over the same immutable blobs.
$ docker pull saggar.dev/val/web:staging
$ echo "deb [trusted=yes] https://saggar.dev/apt/val staging main" \
| sudo tee /etc/apt/sources.list.d/saggar.list
$ sudo apt update && sudo apt install my-debian-package
(The [trusted=yes] spelling is today’s; release signing for the apt
front-end is not built yet.)
Addresses¶
OCI refs are versions (<job-id> tags), channels
(staging/rc/release) or digests; tags are an image’s
versions plus its channel labels, sorted:
docker pull saggar.dev/[<owner>/]<name>:<ref>
A multi-arch docker group is served through the same refs: the group’s image index answers for its group-id tag and the channel labels, each per-arch manifest answers for its own digest, and the per-arch job-id tags keep serving their plain manifests; one pull address for the whole matrix (see Fan out: formats and architectures).
apt suites are channels; the indexes publish the architectures
amd64, arm64, armhf, i386, riscv64 and the
arch-independent all:
deb [trusted=yes] saggar.dev/apt/[<owner>] <suite> main
HTTP routes¶
At the server root (machine-facing; foreign clients hardcode these).
OCI (the Docker Registry API v2):
GET /v2/
GET|HEAD /v2/[<owner>/]<name>/manifests/<ref>
GET|HEAD /v2/[<owner>/]<name>/blobs/<digest>
GET /v2/[<owner>/]<name>/tags/list?n=<max>
GET /v2/ is the protocol handshake (docker-distribution-api-version:
registry/2.0). ?n= caps the tag listing (OCI pagination).
apt (dist indexes + pool):
GET /apt/[<owner>/]dists/<suite>/Release
GET /apt/[<owner>/]dists/<suite>/main/binary-<arch>/Packages[.gz]
GET /apt/[<owner>/]pool/<artifact-id>/<file>
Owner-scoped paths¶
Registry paths carry the owner: this is what makes two projects
named web possible.
$ docker pull saggar.dev/val/web:staging
$ echo "deb [trusted=yes] saggar.dev/apt/val staging main" | …
The flat forms (…/web:staging, …/apt staging main) keep
resolving for artifacts created before accounts existed. Those rows
own no project, and they are the only things the flat paths serve, so
the two spellings never collide. Everything submitted after the
accounts upgrade is reachable under its owner’s path exclusively.
Auth and visibility¶
The registries require an account; there is no open-registry mode. Every pull names a subject, and reads follow the project ladder exactly like the API: a project’s artifacts are pullable by its members, by anyone once the project is public, and by no one else. Denials are indistinguishable from unknown names.
Any live sgt-… token authenticates, full or pull-only. The
credential is read the way registry clients present it:
Authorization: Basic(whatdocker loginand apt’sauth.confsend): the password part is the token, the username cosmetic;Authorization: Bearer;X-Auth-Token(curl and the CLI).
No credential (or a bad one) answers 401 with
WWW-Authenticate: Basic realm="saggar". That header is what makes
docker start its login dance and apt fall back to auth.conf.
Client setup:
$ docker login saggar.dev -u val -p sgt-…
# /etc/apt/auth.conf.d/saggar.conf (chmod 600)
machine saggar.dev login val password sgt-…
apt sends those credentials automatically; apt update and apt
install work unchanged.
Pull-only tokens¶
For credentials that live in ~/.docker/config.json or
auth.conf.d (long-lived, machine-readable), mint a pull-only
token: POST /api/v1/auth/tokens with {"scope": "registry"}. It
authenticates on the registry front-ends and is refused everywhere
else; a leaked pull credential cannot read jobs or artifacts through
the API (see The management API).