// 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 ( "encoding/json" "fmt" "slices" "sort" "strings" ) // 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 and release on both of distribution's. func VerbsOf(seat string) []string { v := []string{VerbPublish, VerbPublishPart, VerbVersions, VerbRemove, VerbHold, VerbRelease} if seat == ImageSeat { v = append(v, VerbMirror, VerbTags) } sort.Strings(v) return v } // Callers is the holder's trusted config value `callers` (the form ADR 0363 gives a restricted family): per verb, the // modules that may call it. 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 map[string][]string // 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 is 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}, } } // SweepHolder is the module holding the seat `mesh-artifact-sweep`, the artifact sweep. const SweepHolder = "artifact-store-tools" // Anyone on a verb's list lets every proved caller call it. const Anyone = "*" // Operator on a list stands for the operator's session, the caller the tool runner proves as `person.` (ADR 0386 // §7): a release by hand through registry_holds carries it on, never distribution's own account. const Operator = "operator" // Unproven is the caller the runtime names for a call it could not prove (mesh-sdk stdio.Unproven). const Unproven = "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) { if strings.TrimSpace(raw) == "" { return DefaultCallers(), nil } var c Callers if err := json.Unmarshal([]byte(raw), &c); err != nil { return nil, fmt.Errorf("callers is not a JSON object of verb to modules: %w", err) } known := VerbsOf(ImageSeat) for verb, mods := range c { if !slices.Contains(known, verb) { return nil, fmt.Errorf("callers names the verb %q, which no seat of distribution's has", verb) } if verb == VerbRelease && slices.Contains(mods, Self) { return nil, fmt.Errorf("callers puts %s on release's list: registry_holds would release in its own name", Self) } } return c, nil } // Allowed answers nil when caller may call verb, else the refusal naming the verb and the caller. func (c Callers) Allowed(verb, caller string) error { if caller == "" || caller == Unproven { return fmt.Errorf("%s: refused: the request carries no caller the tool runner proved", verb) } person := strings.HasPrefix(caller, "person.") for _, m := range c[verb] { switch { case m == Anyone, m == Operator && person, !person && m == CallerModule(caller): return nil } } return fmt.Errorf("%s: refused for %s: not on the holder's caller list for %s", verb, caller, verb) } // CallerModule is the module of a proved module caller, which the runtime stamps `.`. func CallerModule(proved string) string { if i := strings.LastIndexByte(proved, '.'); i >= 0 { return proved[i+1:] } return proved }