// Package reference holds the form of an artifact's reference once a registry seat published it: `//@` // (novox/hq ADR 0605 §9), and the older `artifact-store://` form read beside it. It is the Delivery domain's rule over // the names of the two registry seats that front the artifact store; it names no seat itself (ADR 0634 §4, ADR 0635 §4): the // caller gives the seats' names from their contracts (Seats), so a package here cannot drift from a contract. package reference import "strings" // ArtifactStoreScheme marks a reference to something in the mesh's artifact store, kept without the store's address. const ArtifactStoreScheme = "artifact-store://" // UpstreamPrefix opens the holder's own name for an upstream copy it keeps: `upstream//@`, after // `/` in the seat's form and after `artifact-store://` in the older one. const UpstreamPrefix = "upstream/" // Seats are the names of the two registry seats a reference of the registry form names first: Image the seat that fronts // images and upstream copies (container-image-registry), Object the seat that fronts bundles and archives // (oci-artifact-registry). The caller reads them from the seats' contracts. type Seats struct{ Image, Object string } // All is the two names. func (s Seats) All() []string { return []string{s.Image, s.Object} } // Recorded is a reference as the mesh records it: the artifact store's address, if the builder wrote one, taken off. func (s Seats) Recorded(reference string) string { if _, _, seat := s.InRegistrySeat(reference); seat || strings.HasPrefix(reference, ArtifactStoreScheme) { return reference } if rest, isURL := strings.CutPrefix(reference, "http://"); isURL { if _, path, ok := strings.Cut(rest, "/v2/"); ok && strings.Contains(path, "/blobs/") { return ArtifactStoreScheme + path } return reference } host, path, ok := strings.Cut(reference, "/") if !ok || !isRegistryHost(host) || !strings.Contains(path, "@sha256:") { return reference } return ArtifactStoreScheme + path } // isRegistryHost is the runtime's own rule for reading the first component of a reference as a registry rather than a // namespace: it has a dot or a port in it, or it is localhost. func isRegistryHost(component string) bool { return component == "localhost" || strings.ContainsAny(component, ".:") } // InArtifactStore reports whether a reference is a kept one, and what it names there: `/@` for an // image, `//blobs/` for a bundle or an archive. It reads both forms (ADR 0605 §9). func (s Seats) InArtifactStore(reference string) (path string, kept bool) { if seat, rest, ok := s.InRegistrySeat(reference); ok { if seat == s.Object { name, digest, _ := strings.Cut(rest, "@") return name + "/blobs/" + digest, true } return rest, true } return strings.CutPrefix(reference, ArtifactStoreScheme) } // SeatReference is the reference a build names an image, bundle, archive or upstream copy by once it published it through // a registry seat: `//@sha256:`, as InRegistrySeat reads it back. func SeatReference(seat, module, artifact, digest string) string { return seat + "/" + module + "/" + artifact + "@" + digest } // InRegistrySeat reads a reference in the form a registry seat answers: the seat it came through and the rest. A seat's // name holds no dot or port, so no image of an outside registry reads as one, and a kept `artifact-store://` reference is // not one. It also reads the holder's own naming of an upstream copy through the image seat, whose rest InUpstream reads. func (s Seats) InRegistrySeat(reference string) (seat, path string, ok bool) { first, rest, found := strings.Cut(reference, "/") if !found || !strings.Contains(rest, "@sha256:") { return "", "", false } if _, _, upstream := InUpstream(rest); upstream && first == s.Image { return first, rest, true } if strings.Count(rest, "/") != 1 { return "", "", false } for _, name := range s.All() { if first == name { return name, rest, true } } return "", "", false } // InUpstream reads a path in the holder's naming of an upstream copy, `upstream//@sha256:`: the // upstream registry host and the image's path there, with its digest. func InUpstream(path string) (host, image string, ok bool) { rest, found := strings.CutPrefix(path, UpstreamPrefix) if !found { return "", "", false } host, image, found = strings.Cut(rest, "/") name, digest, pinned := strings.Cut(image, "@") if !found || !isRegistryHost(host) || name == "" || strings.HasPrefix(name, "/") || strings.HasSuffix(name, "/") || strings.Contains(name, "//") || !pinned || !strings.HasPrefix(digest, "sha256:") || strings.Contains(digest, "/") { return "", "", false } return host, image, true } // Routed is a kept reference as a machine fetches it, through the artifact store at `address` (host:port). A reference // that is not a kept one is what it was. func (s Seats) Routed(reference, address string) string { path, kept := s.InArtifactStore(reference) if !kept { return reference } if strings.Contains(path, "/blobs/") { return "http://" + address + "/v2/" + path } return address + "/" + path }