Files
mesh-catalog/modules/distribution/internal/holder/callers.go
T
jschoubben 541c2e02d9 distribution: provide and serve no artifact-store, and answer the seats' list from the store's own files (ADR 0632 §1, §3, §4; issue 1966)
The store is found by the seats' holder and its listen registry; list says,
per repository, each manifest's digest, whether it is an index and what it
names, its tags, its size and whether it is held, so the sweep reads the
store through the seat. E15 now asks that nothing is provided or served.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 21:11:15 +01:00

132 lines
5.2 KiB
Go

// Package holder is distribution's holding of the two image seats, `container-image-registry` and
// `oci-artifact-registry`, and of the old seat `mesh-artifact-store` until its window closes (novox/hq design 51 *The
// four registry seats*, ADR 0605, ADR 0611, ADR 0617 §2). A build module publishes into a seat through `publish` and
// `publish-part`: the parts land in distribution's own object store `incoming`, are hashed, checked against the
// builder's `builder_versions` and stored in the artifact store, which listens on its node's loopback; the store's port
// is the program's, reads open, writes from its own process alone (front.go).
package holder
import (
"fmt"
"slices"
"sort"
stdio "git.novox.be/novox/mesh-sdk/go"
)
// The seats distribution holds, and the old one it holds until its window closes.
const (
ImageSeat = "container-image-registry"
ArtifactSeat = "oci-artifact-registry"
OldSeat = "mesh-artifact-store"
)
// The verbs of the seats (design 26 *A seat's definition*; design 51 *One contract*).
const (
VerbPublish = "publish"
VerbPublishPart = "publish-part"
VerbVersions = "versions"
VerbRemove = "remove"
VerbMirror = "mirror"
VerbTags = "tags"
VerbHold = "hold"
VerbRelease = "release"
)
// Seats are the seats distribution answers verbs on.
var Seats = []string{ImageSeat, ArtifactSeat}
// VerbsOf are a seat's verbs: mirror and tags on the image seat alone; hold, release and list on both of distribution's.
func VerbsOf(seat string) []string {
v := []string{VerbPublish, VerbPublishPart, VerbVersions, VerbRemove, VerbHold, VerbRelease, VerbList}
if seat == ImageSeat {
v = append(v, VerbMirror, VerbTags)
}
sort.Strings(v)
return v
}
// Callers is the holder's trusted config value `callers`: per verb, the callers that may call it, read as mesh-sdk's
// seat-neutral list per verb reads it (ADR 0627 §2). The holder checks the caller the tool runner proved and stamped on
// the request (design 25 § 12.4), never a name in the request's body.
type Callers = stdio.VerbCallers
// DefaultCallers is the list design 51 *Who may call which verb* names: publish and publish-part the build modules;
// mirror mesh-build-image; remove the builder (a tagged version) and the artifact sweep's holder (retention); hold the
// artifact sweep's holder alone; release that holder, and the operator's session only through registry_holds (Operator
// below). versions, tags and list are read by anyone on the mesh, as the store's reads are.
func DefaultCallers() Callers {
builds := []string{"mesh-build-go", "mesh-build-image", "mesh-build-node"}
return Callers{
VerbPublish: builds,
VerbPublishPart: builds,
VerbMirror: {"mesh-build-image"},
VerbRemove: {"mesh-delivery-builder", SweepHolder},
VerbHold: {SweepHolder},
VerbRelease: {SweepHolder, Operator},
VerbVersions: {Anyone},
VerbTags: {Anyone},
VerbList: {Anyone},
}
}
// SweepHolder is the module holding the seat `mesh-artifact-sweep`, the artifact sweep.
const SweepHolder = "artifact-store-tools"
// The entries of a list that stand for a class, and the caller the runtime names for a call it could not prove.
const (
Anyone = stdio.AnyCaller
Operator = stdio.AnyPerson
Unproven = stdio.Unproven
)
// Self is distribution's own account: never on release's list, so registry_holds cannot release in its own name.
const Self = "distribution"
// ParseCallers reads the config value; an empty value is DefaultCallers. A verb no seat has is refused, and Self on
// release's list is refused (design 51).
func ParseCallers(raw string) (Callers, error) {
c, err := stdio.ParseVerbCallers(raw, VerbsOf(ImageSeat), DefaultCallers())
if err != nil {
return nil, err
}
return selfNotReleasing(c)
}
// ConfiguredCallers is the config value `callers` as mesh-config resolved it for this machine, read through the tools
// runtime's stdio.Config on each call (never an environment variable: nothing renders a config value into a
// program's environment); unset is DefaultCallers, and Self on release's list is refused as ParseCallers refuses it.
func ConfiguredCallers() (Callers, error) {
c, err := stdio.ConfiguredVerbCallers(CallersConfig, VerbsOf(ImageSeat), DefaultCallers())
if err != nil {
return nil, err
}
return selfNotReleasing(c)
}
// CallersConfig is the config value's key, and CallersTool the tools bundle's tool answering it as resolved: the
// program, which has no tools runtime, asks that tool over the bus (manifest invokes).
const (
CallersConfig = "callers"
CallersTool = "registry_callers"
)
// WithCallers is v with the caller list c: a copy for one call, so a list read on that call never changes another's.
func (v *Verbs) WithCallers(c Callers) *Verbs {
h := *v.Holder
h.Callers = c
out := *v
out.Holder = &h
return &out
}
func selfNotReleasing(c Callers) (Callers, error) {
if slices.Contains(c[VerbRelease], Self) {
return nil, fmt.Errorf("callers puts %s on release's list: registry_holds would release in its own name", Self)
}
return c, nil
}
// CallerModule is the module of a proved module caller, which the runtime stamps `<node>.<module>`.
func CallerModule(proved string) string { return stdio.ModuleOf(proved) }