Skip to content

Zone Retriever and cascade

A zone is driven by one document: its Retriever. It lists, by name and version constraint, the recipes the zone should hold, and names the cookbook to resolve them from. The instance re-reads it at every synchronization; changing what a zone contains means changing this document — or publishing a new recipe version that an existing constraint already covers. The document format is normative on the recipe-spec site: see the Retriever specification (the Retriever kind, recipe.tobby.dev/v1alpha1). A complete commented example ships in the repository as examples/retriever.yaml.

retriever.source accepts three forms (FR-010):

Form Example When
Local file /etc/tobby/retriever.yaml The document is managed with the instance’s configuration.
HTTP(S) URL https://git.example.com/platform/retriever.yaml The document lives in a Git repository or any web server — the common GitOps arrangement.
OCI reference oci://registry.example.com/config/retriever:v1 The document travels like the content it describes — including across zones, carried by Tobby itself.

The configured source is reported as configured on the Retriever administration screen (/admin/retriever, admin role) and its API mirror GET /api/v1/retriever — alongside the declared relaxed trust scopes and the effective synchronization interval. The interval override lives on the same screen (PUT /api/v1/retriever/interval, and DELETE to return to the configured value); it persists in the state directory, survives restarts, wins over sync.interval, and is audited as a sensitive configuration change (FR-094).

The retriever administration screen: configured source, interval and its runtime override

At each cycle the instance resolves every listed recipe from the cookbook, verifies its signature against the configured trust roots (FR-033), and reconciles. Version constraints are resolved at each pass; if no published version satisfies a constraint, that entry fails and says so — the other entries carry on. One unpublishable recipe never blocks the zone.

The cascade: connected → restricted → more restricted

Section titled “The cascade: connected → restricted → more restricted”
Zone A — upstream Zone B — downstream Zone C — further down Tobby re-verifies on entry Tobby re-verifies on entry Tobby re-verifies on entry zone registry …/docker.io/bitnami/wordpress zone registry …/docker.io/bitnami/wordpress zone registry …/docker.io/bitnami/wordpress recipes recipes The same signed recipes flow down unmodified — each instance re-verifies against its own trust roots Relocated paths are invariant: the same …/docker.io/… path in every zone, however many hops

Real topologies chain zones. The upstream zone promotes into its registry; the downstream zone’s Tobby fetches from that registry even though the recipes — immutable, signed, bit-exact — keep naming the origin hosts (docker.io/...). The bridge is source substitution (FR-036):

# Downstream instance
registries:
substitutions:
docker.io: registry.upstream.example/docker.io
ghcr.io: registry.upstream.example/ghcr.io
retriever:
source: oci://registry.upstream.example/cookbook/retriever:v1

Substitution changes only the network endpoint contacted — never the computed destination path (FR-035). That invariance is what makes the cascade compose: docker.io/bitnami/wordpress relocates to <registry>/docker.io/bitnami/wordpress in every zone, however many hops it crossed, and never degenerates into reg.zone2/reg.zone1/docker.io/.... Each zone’s registry holds the same relocated paths under its own host, and each zone’s cookbook — populated by recipe propagation (FR-034) — is what the next zone’s Retriever points at. The rule and its rationale are ADR-0013; the normative grammar (host canonicalization, port encoding, substitution semantics) is RECIPE-SPEC §11.5.

Two policies deliberately read different references:

  • The registry allowlist (FR-030) and credential lookup apply to the effective host actually contacted — the substitute. That is where the bytes come from, so that is what network policy must name.
  • Trust-root scopes (FR-033) match the nominal ref written in the recipe. Signed provenance does not change because content was fetched from a closer copy.

Logs record the nominal→effective mapping on every substituted fetch, so an audit can always answer both “what was this content” and “where did these bytes come from”.

A downstream instance authenticates to its upstream like any registry client. Create a dedicated account (or token) on the upstream instance — see authentication and RBAC — with read as its only need, and hand it to the downstream instance through its credentials file (FR-004):

registries:
credentialsFile: /etc/tobby-credentials/config.json

The file is a standard dockerconfigjson payload; entries are looked up by the effective host, so the entry names the upstream registry — registry.upstream.example — not docker.io. It must live outside the store (secrets never travel on transportable media), and on Kubernetes it is a mounted Secret the chart wires for you. Write-scoped credentials follow the same path: pushing to the zone’s destination registry uses the same credentials file, keyed by the destination host.

The destination side of the same instance is its own configuration section — destination.registry, plus destination.basePath and destination.cookbook — deliberately separate from substitutions: one answers “where do I read from”, the other “where do I promote to”, and applying a read-side rewrite to a write would publish to a registry nobody named.

Next: your clusters and hosts consume what landed — connect your clients.