The Ur Project

Documenting the craft of stacking things so they remain stacked. For a very long time.

pkg-repos.d(5)

NAME

pkg-repos.d - repository drop-in configuration for pkg

SYNOPSIS

/etc/pkg/repos.d/*.toml

/etc/pkg/*.root.json

DESCRIPTION

Every repository this machine uses is one small TOML file under /etc/pkg/repos.d. One file per repository, so adding one, parking one or dropping one is a single file, and never an edit in the middle of somebody else's config.

How the files are read

Only files ending in .toml are read. They're read in filename order, which is why the usual name starts with a number. That order is only cosmetic, though. The real precedence is worked out afterwards from consume edges and priorities; pkg-chain(7) explains how.

A missing directory isn't an error. It means "no configured repositories", which is what lets pkg --repo or pkg --repo-url stand on their own.

Every pkg command loads and checks this directory before it does anything else. pkg --repos-dir points one invocation at a different directory, and --repo-url skips it entirely.

Do I write these by hand?

You can, and it works. But pkg-repo(1) is the supported way. It works out the name, fetches and checks the trust anchor, picks a priority, checks that the resulting chain still loads, and writes both files atomically. Writing the files yourself means doing all of that yourself.

KEYS

Unknown keys are an error, not a warning.

name = "STRING"
Required. The repository's name: what the chain and error messages call it, and what other drop-ins put in consumes. Must not be empty. Two enabled drop-ins with the same name are refused. A disabled one next to an enabled one is fine; that's one way to park a copy.
url = "STRING"
Base URL of a remote repository, for example http://host:8082/Common. Metadata is expected under <url>/metadata/. Needs tuf_root. Can't be combined with path.
tuf_root = "PATH"
Path to the TUF trust anchor for this repository: one of its N.root.json files. Required whenever url is set, because remote manifests need a trust anchor. See TRUST ANCHORS for where it should come from, and which root to pin.
path = "PATH"
Filesystem path to a local repository root. Can't be combined with url. Its metadata is read unverified, so it takes no tuf_root.
priority = INTEGER
Chain priority. Higher sorts earlier among repositories that no consume edge relates. Default 0. A consumed repository can never outrank its consumer, whatever the priorities say.
enabled = true|false
Whether the repository takes part. Default true. A disabled drop-in keeps its URL, anchor, priority and edges, so parking a repository costs nothing to undo.
consumes = ["NAME", ...]

Repositories this one sits on top of. Default: empty.

This is only a fallback. A repository that attests its own consume edge in its signed metadata (metadata/provisional_pins.json) outranks whatever the local configuration guesses. The key is there for repositories that publish no edge.

Every name listed must be configured. An edge to a repository that isn't there is a dangling consume, and that refuses every later pkg command.

stream = "STRING"

The product line the repository said it belonged to when it was added. Optional. pkg-repo(1) writes it so pkg repo list and the mixed-stream check on pkg repo add work without a network fetch. It's a record, not an authority: at runtime the stream comes from the repository's own signed pin file, and a chain that spans two streams is warned about.

A pkg older than this key rejects any drop-in that carries it, because unknown keys are an error. An image ships the client and its config together, so in practice that order takes care of itself.

provenance_only = true|false
Optional, default false. When true, pkg never loads the repository as part of the chain, whether it's enabled or not and even if it sits in /etc/pkg/repos.d. Nothing is installed or upgraded from it. The only way to use it is to name the file with pkg provenance --with-extra-repo. This is for a build floor such as Buildroot-N.R: its packages are provenance inputs and must never end up on a machine. A pkg older than this key rejects a drop-in that carries it.

URL OR PATH, NEVER BOTH

Setting both url and path, or neither, is refused. So is url without tuf_root, and so is path with one. An anchor on a local path would promise a check that never runs.

These are hard errors when the directory is loaded. You'll see them on the very next pkg command, not later as a package quietly resolving from the wrong place.

FILE NAMING CONVENTION

pkg repo add writes:

/etc/pkg/repos.d/<priority>-<lowercase-name>.toml
The drop-in, with the priority zero-padded to two digits.
/etc/pkg/<lowercase-name>.root.json
The trust anchor, one directory above repos.d. repos.d holds *.toml and nothing else.

That's the same layout an image bake writes, so a machine installed from an image and a machine configured by hand look the same. Nothing enforces it, though. The chain comes from what's inside the files, not from their names.

TRUST ANCHORS

The anchor is your root of trust for a remote repository. Everything the client accepts from it -- metadata, manifests, payload digests -- verifies back to that one file.

Why can't it come over the network?

Because an anchor can't be authenticated by fetching it. The bytes come from the very server they're supposed to authenticate. These base URLs are routinely plain HTTP, so anyone in the middle hands back their own root, and every check after that verifies happily against it.

So the root has to arrive already trusted: with the installer, on removable media, baked into a release image, or checked against a fingerprint you got out-of-band. pkg repo add --fingerprint is the authenticated path. Without it, add is trust on first use, says so, and wants an interactive answer that --yes won't give. See pkg-repo(1).

Which root goes in the file?

The newest one. A client walks forward from the anchor, one version at a time, each new root signed by the one before it. It never walks backwards. That's why pkg repo add pins the newest root it can reach and never 1.root.json: a machine anchored at an old root would trust whatever a long-retired root key signs as its successor.

Composing an image is the one place that looks further back. When pkg-compose(1) uses a configured repository and names no signing key for it, it trusts every key that repository's chain ever enrolled to sign packages, since packages signed by an earlier key are still published. So it walks the chain from 1.root.json, but uses that walk only when it passes through this anchor byte for byte. A mirror without the older roots, or a chain that misses the anchor, leaves the walk starting at the anchor, like everywhere else.

EXAMPLES

A two-repository machine

/etc/pkg/repos.d/10-base.toml

name = "Base" url = "http://repo.example:8082/Base" tuf_root = "/etc/pkg/base.root.json" priority = 10 enabled = true

/etc/pkg/repos.d/20-common.toml

name = "Common" url = "http://repo.example:8082/Common" tuf_root = "/etc/pkg/common.root.json" priority = 20 enabled = true consumes = ["Base"]

Common sits above Base. If Common attests the same edge in its own metadata, the consumes line here is redundant, but harmless.

A repository in a stream

name = "Sandcastle-Common" url = "http://repo.example:8082/Sandcastle/Common" tuf_root = "/etc/pkg/sandcastle-common.root.json" priority = 20 enabled = true consumes = ["Sandcastle-Base"] stream = "Sandcastle"

A local mirror

name = "Mirror" path = "/srv/mirror/Common" priority = 30 enabled = true

A parked repository

name = "Staging" url = "http://staging:8082/Common" tuf_root = "/etc/pkg/staging.root.json" priority = 90 enabled = false

Bring it back without re-fetching or re-pinning the anchor:

pkg repo enable Staging

Checking a hand-edit before it bites

Any command loads and checks the whole directory first, so this is a cheap syntax and chain check:

pkg repo list --all

A dangling edge, a cycle, a duplicate name, a missing tuf_root or an unknown key all fail here, naming the file.

Adding a repository properly

pkg repo add http://repo.example:8082/Common --dry-run pkg repo add http://repo.example:8082/Common \ --fingerprint 71de...02c4

Pointing one invocation somewhere else entirely

pkg --repos-dir /tmp/alt-repos.d list --all pkg --repos-dir /nonexistent --repo /srv/local list --all

SEE ALSO

pkg(1), pkg-repo(1), pkg-compose(1), pkg.toml(5), pkg-chain(7), pkg-repository-layout(7)

docs/design/repo.md, docs/design/trust.md, docs/design/federation.md