Registry seats: no seat delivers artifact-store, and the two store seats serve list (ADR 0632 §2, §3; issue 1966)

The store's address is the registry seat holder's listen, so the provision
artifact-store retires; the sweep and the read tools read the store through
list, never by its files or its read side.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
2026-11-04 20:52:15 +01:00
parent ae5266e776
commit 4d7b2a3778
13 changed files with 585 additions and 72 deletions
+40 -7
View File
@@ -15,6 +15,7 @@ const (
VerbTags = "tags"
VerbHold = "hold"
VerbRelease = "release"
VerbList = "list"
)
const (
@@ -28,7 +29,7 @@ const (
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
func Verbs() []string {
return []string{VerbPublish, VerbPublishPart, VerbVersions, VerbRemove, VerbMirror, VerbTags, VerbHold, VerbRelease}
return []string{VerbPublish, VerbPublishPart, VerbVersions, VerbRemove, VerbMirror, VerbTags, VerbHold, VerbRelease, VerbList}
}
// Events is every event published on the seat's subject: a new copy each call.
@@ -103,6 +104,24 @@ type Hold struct {
Reason string `json:"reason,omitempty"`
}
// Listed is a type of the contract.
type Listed struct {
// for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by
Artifact string `json:"artifact,omitempty"`
// the manifest's digest, sha256:<hex>
Digest string `json:"digest"`
// a hold keeps it from retention
Held bool `json:"held"`
// it is an index, naming a manifest per platform
Index bool `json:"index"`
// for an index, the digests of the manifests it names
Platforms []string `json:"platforms,omitempty"`
// its bytes: its own content and every blob it names
Size int64 `json:"size"`
// the tags naming it, none for an untagged one
Tags []string `json:"tags,omitempty"`
}
// Outcome is the body of published, refused and removed.
type Outcome struct {
// the source commit it was built from
@@ -150,6 +169,14 @@ type Part struct {
Size int64 `json:"size"`
}
// Repository is a type of the contract.
type Repository struct {
// each manifest it holds, by digest
Manifests []Listed `json:"manifests"`
// the repository
Name string `json:"name"`
}
// PublishInput is what publish takes.
type PublishInput struct {
// the source commit it was built from
@@ -345,6 +372,18 @@ type ReleaseAnswer struct {
Released bool `json:"released"`
}
// ListInput is what list takes.
type ListInput struct {
// the repository, <module>/<artifact>; every repository of the seat when not given
Name string `json:"name,omitempty"`
}
// ListAnswer is what list answers.
type ListAnswer struct {
// each repository named, in order of its name
Repositories []Repository `json:"repositories"`
}
// PublishedEvent is the body of the event published.
type PublishedEvent = Outcome
@@ -362,9 +401,3 @@ type HeldEvent = Hold
// ReleasedEvent is the body of the event released.
type ReleasedEvent = Hold
// ArtifactStoreForm is the form of the provision artifact-store.
type ArtifactStoreForm struct {
// the store's address
URL string `json:"url"`
}
@@ -43,6 +43,8 @@ func TestFixtures(t *testing.T) {
decode: func(b []byte) (any, error) { var v ReleaseAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "a tagged version sharing its digest with another tag is kept, not removed", subject: "", given: "{\"kept\":true,\"reason\":\"shared-digest\",\"removed\":false,\"tags\":[\"0.1.19\"]}", wire: "{\"kept\":true,\"reason\":\"shared-digest\",\"removed\":false,\"tags\":[\"0.1.19\"]}",
decode: func(b []byte) (any, error) { var v RemoveAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "list answers an index with its platforms, its tag and its hold", subject: "", given: "{\"repositories\":[{\"manifests\":[{\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"held\":false,\"index\":true,\"platforms\":[\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"],\"size\":2048,\"tags\":[\"3\"]},{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"held\":false,\"index\":false,\"size\":1024}],\"name\":\"mail/server\"}]}", wire: "{\"repositories\":[{\"manifests\":[{\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"held\":false,\"index\":true,\"platforms\":[\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"],\"size\":2048,\"tags\":[\"3\"]},{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"held\":false,\"index\":false,\"size\":1024}],\"name\":\"mail/server\"}]}",
decode: func(b []byte) (any, error) { var v ListAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
}
for _, c := range cases {
v, err := c.decode([]byte(c.given))
+40 -7
View File
@@ -13,6 +13,7 @@ const (
VerbRemove = "remove"
VerbHold = "hold"
VerbRelease = "release"
VerbList = "list"
)
const (
@@ -26,7 +27,7 @@ const (
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
func Verbs() []string {
return []string{VerbPublish, VerbPublishPart, VerbVersions, VerbRemove, VerbHold, VerbRelease}
return []string{VerbPublish, VerbPublishPart, VerbVersions, VerbRemove, VerbHold, VerbRelease, VerbList}
}
// Events is every event published on the seat's subject: a new copy each call.
@@ -101,6 +102,24 @@ type Hold struct {
Reason string `json:"reason,omitempty"`
}
// Listed is a type of the contract.
type Listed struct {
// for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by
Artifact string `json:"artifact,omitempty"`
// the manifest's digest, sha256:<hex>
Digest string `json:"digest"`
// a hold keeps it from retention
Held bool `json:"held"`
// it is an index, naming a manifest per platform
Index bool `json:"index"`
// for an index, the digests of the manifests it names
Platforms []string `json:"platforms,omitempty"`
// its bytes: its own content and every blob it names
Size int64 `json:"size"`
// the tags naming it, none for an untagged one
Tags []string `json:"tags,omitempty"`
}
// Outcome is the body of published, refused and removed.
type Outcome struct {
// the source commit it was built from
@@ -148,6 +167,14 @@ type Part struct {
Size int64 `json:"size"`
}
// Repository is a type of the contract.
type Repository struct {
// each manifest it holds, by digest
Manifests []Listed `json:"manifests"`
// the repository
Name string `json:"name"`
}
// PublishInput is what publish takes.
type PublishInput struct {
// the source commit it was built from
@@ -303,6 +330,18 @@ type ReleaseAnswer struct {
Released bool `json:"released"`
}
// ListInput is what list takes.
type ListInput struct {
// the repository, <module>/<artifact>; every repository of the seat when not given
Name string `json:"name,omitempty"`
}
// ListAnswer is what list answers.
type ListAnswer struct {
// each repository named, in order of its name
Repositories []Repository `json:"repositories"`
}
// PublishedEvent is the body of the event published.
type PublishedEvent = Outcome
@@ -320,9 +359,3 @@ type HeldEvent = Hold
// ReleasedEvent is the body of the event released.
type ReleasedEvent = Hold
// ArtifactStoreForm is the form of the provision artifact-store.
type ArtifactStoreForm struct {
// the store's address
URL string `json:"url"`
}
@@ -43,6 +43,8 @@ func TestFixtures(t *testing.T) {
decode: func(b []byte) (any, error) { var v ReleaseAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "a tagged version sharing its digest with another tag is kept, not removed", subject: "", given: "{\"kept\":true,\"reason\":\"shared-digest\",\"removed\":false,\"tags\":[\"0.1.19\"]}", wire: "{\"kept\":true,\"reason\":\"shared-digest\",\"removed\":false,\"tags\":[\"0.1.19\"]}",
decode: func(b []byte) (any, error) { var v RemoveAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "list answers an untagged archive by the blob it keeps", subject: "", given: "{\"repositories\":[{\"manifests\":[{\"artifact\":\"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"held\":true,\"index\":false,\"size\":4096}],\"name\":\"mail/files\"}]}", wire: "{\"repositories\":[{\"manifests\":[{\"artifact\":\"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"held\":true,\"index\":false,\"size\":4096}],\"name\":\"mail/files\"}]}",
decode: func(b []byte) (any, error) { var v ListAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
}
for _, c := range cases {
v, err := c.decode([]byte(c.given))
+9 -3
View File
@@ -231,7 +231,7 @@ func List() []Seat {
{Name: "provisioning", Description: "Read what the provisioner keeps: the registry (which domain answers which kind of placeholder), the needs each module on each node recorded, what is applied where, or the items of every collection, narrowed by node and module. Reads only.", Optional: true,
Input: map[string]any{"properties": map[string]any{"collection": map[string]any{"description": "for collect: a collection", "type": "string"}, "module": map[string]any{"description": "a module: for collect, a contributor", "type": "string"}, "node": map[string]any{"description": "a node", "type": "string"}, "what": map[string]any{"description": "registry (when not given), needs, applied or collect", "type": "string"}}, "type": "object"}},
}},
{Name: MeshArtifactStore, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0075", Delivers: "artifact-store"},
{Name: MeshArtifactStore, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0075"},
{Name: MeshCatalog, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0604",
Serves: []Verb{
{Name: "modules", Description: "Every module the trunk holds, each with its repository, its directory there, the merge commit it was last changed at and its fingerprint (or why it has none): the catalog of the mesh, read from its holder's state. Reads only.", Optional: true,
@@ -411,7 +411,7 @@ func List() []Seat {
Input: map[string]any{"properties": map[string]any{"digest": map[string]any{"description": "the digest (for a bundle, archive or copied base)", "type": "string"}, "name": map[string]any{"description": "the name", "type": "string"}, "reason": map[string]any{"description": "why it goes", "enum": []string{"tag-gone", "retention"}, "type": "string"}, "tag": map[string]any{"description": "the tag (for a tagged version)", "type": "string"}, "version": map[string]any{"description": "the version", "type": "string"}}, "required": []string{"name", "reason"}, "type": "object"},
Output: map[string]any{"properties": map[string]any{"artifact": map[string]any{"description": "one published thing, as every registry event and answer names it", "properties": map[string]any{"commit": map[string]any{"description": "the source commit it was built from", "type": "string"}, "digest": map[string]any{"description": "its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)", "type": "string"}, "fingerprint": map[string]any{"description": "the build's fingerprint", "type": "string"}, "kind": map[string]any{"description": "what it is", "enum": []string{"package", "image", "bundle", "archive", "upstream"}, "type": "string"}, "name": map[string]any{"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", "type": "string"}, "tag": map[string]any{"description": "the tag it carries, for a tagged version", "type": "string"}, "version": map[string]any{"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", "type": "string"}}, "required": []string{"name", "kind", "digest"}, "type": "object"}, "kept": map[string]any{"description": "it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags)", "type": "boolean"}, "reason": map[string]any{"description": "why it was kept: shared-digest, another tag names its digest", "enum": []string{"shared-digest"}, "type": "string"}, "removed": map[string]any{"description": "it is gone", "type": "boolean"}, "tags": map[string]any{"description": "the other tags still naming its digest, when kept", "items": map[string]any{"type": "string"}, "type": "array"}}, "required": []string{"removed"}, "type": "object"}},
}},
{Name: ContainerImageRegistry, Scope: "mesh", Grain: "whole", Decision: "novox/hq ADR 0605", Delivers: "artifact-store", Emits: []string{"published", "refused", "removed", "kept", "held", "released"},
{Name: ContainerImageRegistry, Scope: "mesh", Grain: "whole", Decision: "novox/hq ADR 0605", Emits: []string{"published", "refused", "removed", "kept", "held", "released"},
Serves: []Verb{
{Name: "publish", Description: "Open a transfer, naming the artifact and the digest of every part; answers the digests the holder lacks, which the caller then sends by publish-part. A transfer already open answers the parts held, so a cut transfer goes on from there. With no parts and a version, the held digest is published under that version and nothing is stored (the trunk's claim).", Optional: true,
Input: map[string]any{"properties": map[string]any{"commit": map[string]any{"description": "the source commit it was built from", "type": "string"}, "digest": map[string]any{"description": "its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)", "type": "string"}, "fingerprint": map[string]any{"description": "the build's fingerprint", "type": "string"}, "kind": map[string]any{"description": "what it is", "enum": []string{"package", "image", "bundle", "archive", "upstream"}, "type": "string"}, "name": map[string]any{"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", "type": "string"}, "parts": map[string]any{"description": "every part's digest and size; none when the digest is already held", "items": map[string]any{"description": "one part of a transfer", "properties": map[string]any{"digest": map[string]any{"description": "the part's digest, sha256:<hex>: an image's layer or config, a package's file, a bundle's blob", "type": "string"}, "size": map[string]any{"description": "its size in bytes", "type": "integer"}}, "required": []string{"digest", "size"}, "type": "object"}, "type": "array"}, "tag": map[string]any{"description": "the tag it carries, for a tagged version", "type": "string"}, "transfer": map[string]any{"description": "the transfer's name, the caller's own; the same name opens the same transfer again", "type": "string"}, "version": map[string]any{"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", "type": "string"}}, "required": []string{"transfer", "name", "kind", "digest"}, "type": "object"},
@@ -437,8 +437,11 @@ func List() []Seat {
{Name: "release", Description: "Let a hold go; emits released. A release of what is not held answers released false and emits nothing, never an error: the outcome asked holds already. By hand only through the holder's own tool, the operator's session as the proved caller, recorded as a hand act.", Optional: true,
Input: map[string]any{"properties": map[string]any{"digest": map[string]any{"description": "the digest", "type": "string"}, "name": map[string]any{"description": "the name", "type": "string"}, "reason": map[string]any{"description": "why it is let go", "type": "string"}}, "required": []string{"name", "digest", "reason"}, "type": "object"},
Output: map[string]any{"properties": map[string]any{"released": map[string]any{"description": "a hold was let go; false when there was none", "type": "boolean"}}, "required": []string{"released"}, "type": "object"}},
{Name: "list", Description: "What the store holds of one repository, or of every repository the seat holds when none is named: every image manifest and index, each with its digest, whether it is an index (and the manifests an index names), the tags naming it, its size and whether it is held. Read from the store itself, so a manifest no tag names is listed too; it reads only (ADR 0632 §3).", Optional: true,
Input: map[string]any{"properties": map[string]any{"name": map[string]any{"description": "the repository, <module>/<artifact>; every repository of the seat when not given", "type": "string"}}, "type": "object"},
Output: map[string]any{"properties": map[string]any{"repositories": map[string]any{"description": "each repository named, in order of its name", "items": map[string]any{"properties": map[string]any{"manifests": map[string]any{"description": "each manifest it holds, by digest", "items": map[string]any{"properties": map[string]any{"artifact": map[string]any{"description": "for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by", "type": "string"}, "digest": map[string]any{"description": "the manifest's digest, sha256:<hex>", "type": "string"}, "held": map[string]any{"description": "a hold keeps it from retention", "type": "boolean"}, "index": map[string]any{"description": "it is an index, naming a manifest per platform", "type": "boolean"}, "platforms": map[string]any{"description": "for an index, the digests of the manifests it names", "items": map[string]any{"type": "string"}, "type": "array"}, "size": map[string]any{"description": "its bytes: its own content and every blob it names", "type": "integer"}, "tags": map[string]any{"description": "the tags naming it, none for an untagged one", "items": map[string]any{"type": "string"}, "type": "array"}}, "required": []string{"digest", "index", "size", "held"}, "type": "object"}, "type": "array"}, "name": map[string]any{"description": "the repository", "type": "string"}}, "required": []string{"name", "manifests"}, "type": "object"}, "type": "array"}}, "required": []string{"repositories"}, "type": "object"}},
}},
{Name: OciArtifactRegistry, Scope: "mesh", Grain: "whole", Decision: "novox/hq ADR 0605", Delivers: "artifact-store", Emits: []string{"published", "refused", "removed", "kept", "held", "released"},
{Name: OciArtifactRegistry, Scope: "mesh", Grain: "whole", Decision: "novox/hq ADR 0605", Emits: []string{"published", "refused", "removed", "kept", "held", "released"},
Serves: []Verb{
{Name: "publish", Description: "Open a transfer, naming the artifact and the digest of every part; answers the digests the holder lacks, which the caller then sends by publish-part. A transfer already open answers the parts held, so a cut transfer goes on from there. With no parts and a version, the held digest is published under that version and nothing is stored (the trunk's claim).", Optional: true,
Input: map[string]any{"properties": map[string]any{"commit": map[string]any{"description": "the source commit it was built from", "type": "string"}, "digest": map[string]any{"description": "its digest, sha256:<hex> (a Go package's h1: hash beside it in the version's record)", "type": "string"}, "fingerprint": map[string]any{"description": "the build's fingerprint", "type": "string"}, "kind": map[string]any{"description": "what it is", "enum": []string{"package", "image", "bundle", "archive", "upstream"}, "type": "string"}, "name": map[string]any{"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", "type": "string"}, "parts": map[string]any{"description": "every part's digest and size; none when the digest is already held", "items": map[string]any{"description": "one part of a transfer", "properties": map[string]any{"digest": map[string]any{"description": "the part's digest, sha256:<hex>: an image's layer or config, a package's file, a bundle's blob", "type": "string"}, "size": map[string]any{"description": "its size in bytes", "type": "integer"}}, "required": []string{"digest", "size"}, "type": "object"}, "type": "array"}, "tag": map[string]any{"description": "the tag it carries, for a tagged version", "type": "string"}, "transfer": map[string]any{"description": "the transfer's name, the caller's own; the same name opens the same transfer again", "type": "string"}, "version": map[string]any{"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", "type": "string"}}, "required": []string{"transfer", "name", "kind", "digest"}, "type": "object"},
@@ -458,6 +461,9 @@ func List() []Seat {
{Name: "release", Description: "Let a hold go; emits released. A release of what is not held answers released false and emits nothing, never an error: the outcome asked holds already. By hand only through the holder's own tool, the operator's session as the proved caller, recorded as a hand act.", Optional: true,
Input: map[string]any{"properties": map[string]any{"digest": map[string]any{"description": "the digest", "type": "string"}, "name": map[string]any{"description": "the name", "type": "string"}, "reason": map[string]any{"description": "why it is let go", "type": "string"}}, "required": []string{"name", "digest", "reason"}, "type": "object"},
Output: map[string]any{"properties": map[string]any{"released": map[string]any{"description": "a hold was let go; false when there was none", "type": "boolean"}}, "required": []string{"released"}, "type": "object"}},
{Name: "list", Description: "What the store holds of one repository, or of every repository the seat holds when none is named: every manifest keeping one bundle or archive, each with its digest, whether it is an index (and the manifests an index names), the tags naming it, its size and whether it is held. Read from the store itself, so a manifest no tag names is listed too; it reads only (ADR 0632 §3).", Optional: true,
Input: map[string]any{"properties": map[string]any{"name": map[string]any{"description": "the repository, <module>/<artifact>; every repository of the seat when not given", "type": "string"}}, "type": "object"},
Output: map[string]any{"properties": map[string]any{"repositories": map[string]any{"description": "each repository named, in order of its name", "items": map[string]any{"properties": map[string]any{"manifests": map[string]any{"description": "each manifest it holds, by digest", "items": map[string]any{"properties": map[string]any{"artifact": map[string]any{"description": "for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by", "type": "string"}, "digest": map[string]any{"description": "the manifest's digest, sha256:<hex>", "type": "string"}, "held": map[string]any{"description": "a hold keeps it from retention", "type": "boolean"}, "index": map[string]any{"description": "it is an index, naming a manifest per platform", "type": "boolean"}, "platforms": map[string]any{"description": "for an index, the digests of the manifests it names", "items": map[string]any{"type": "string"}, "type": "array"}, "size": map[string]any{"description": "its bytes: its own content and every blob it names", "type": "integer"}, "tags": map[string]any{"description": "the tags naming it, none for an untagged one", "items": map[string]any{"type": "string"}, "type": "array"}}, "required": []string{"digest", "index", "size", "held"}, "type": "object"}, "type": "array"}, "name": map[string]any{"description": "the repository", "type": "string"}}, "required": []string{"name", "manifests"}, "type": "object"}, "type": "array"}}, "required": []string{"repositories"}, "type": "object"}},
}},
{Name: Git, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0111", Delivers: "git"},
{Name: NodeBuildGo, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0525, ADR 0536, ADR 0553", Accepts: []string{"delivery", "check", "recheck"}, PulledByKind: true, Yields: true,
@@ -114,5 +114,36 @@
"0.1.19"
]
}
},
{
"name": "list answers an index with its platforms, its tag and its hold",
"of": "verb:list.answer",
"given": {
"repositories": [
{
"name": "mail/server",
"manifests": [
{
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"index": true,
"platforms": [
"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
],
"tags": [
"3"
],
"size": 2048,
"held": false
},
{
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"index": false,
"size": 1024,
"held": false
}
]
}
]
}
}
]
+95 -19
View File
@@ -5,7 +5,6 @@
"grain": "whole",
"decision": "novox/hq ADR 0605",
"description": "The mesh's container images, in the one artifact store: the OCI distribution protocol's read side, an image's tags and mirroring an outside image.",
"delivers": "artifact-store",
"includes": [
"registry"
],
@@ -190,6 +189,36 @@
]
},
"optional": true
},
{
"name": "list",
"description": "What the store holds of one repository, or of every repository the seat holds when none is named: every image manifest and index, each with its digest, whether it is an index (and the manifests an index names), the tags naming it, its size and whether it is held. Read from the store itself, so a manifest no tag names is listed too; it reads only (ADR 0632 §3).",
"callers": "any caller whose role grants it",
"input": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "the repository, <module>/<artifact>; every repository of the seat when not given"
}
}
},
"answer": {
"type": "object",
"properties": {
"repositories": {
"type": "array",
"items": {
"$ref": "#/defs/Repository"
},
"description": "each repository named, in order of its name"
}
},
"required": [
"repositories"
]
},
"optional": true
}
],
"events": [
@@ -210,24 +239,6 @@
"name": "container-image-registry.events",
"max-age-days": 7
},
"provisions": [
{
"name": "artifact-store",
"form": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "the store's address"
}
},
"required": [
"url"
],
"description": "the artifact store's read side, the OCI distribution protocol's GET and HEAD, open on the mesh network (design 51)"
}
}
],
"defs": {
"Hold": {
"type": "object",
@@ -263,6 +274,71 @@
"holder"
],
"description": "the body of held and released"
},
"Repository": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "the repository"
},
"manifests": {
"type": "array",
"items": {
"$ref": "#/defs/Listed"
},
"description": "each manifest it holds, by digest"
}
},
"required": [
"name",
"manifests"
]
},
"Listed": {
"type": "object",
"properties": {
"digest": {
"type": "string",
"description": "the manifest's digest, sha256:<hex>"
},
"index": {
"type": "boolean",
"description": "it is an index, naming a manifest per platform"
},
"platforms": {
"type": "array",
"items": {
"type": "string"
},
"description": "for an index, the digests of the manifests it names"
},
"artifact": {
"type": "string",
"description": "for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by"
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "the tags naming it, none for an untagged one"
},
"size": {
"type": "integer",
"description": "its bytes: its own content and every blob it names"
},
"held": {
"type": "boolean",
"description": "a hold keeps it from retention"
}
},
"required": [
"digest",
"index",
"size",
"held"
]
}
}
}
+1 -2
View File
@@ -3,6 +3,5 @@
"seat": "mesh-artifact-store",
"scope": "mesh",
"grain": "seat-list",
"decision": "novox/hq ADR 0075",
"delivers": "artifact-store"
"decision": "novox/hq ADR 0075"
}
@@ -114,5 +114,25 @@
"0.1.19"
]
}
},
{
"name": "list answers an untagged archive by the blob it keeps",
"of": "verb:list.answer",
"given": {
"repositories": [
{
"name": "mail/files",
"manifests": [
{
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"index": false,
"artifact": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"size": 4096,
"held": true
}
]
}
]
}
}
]
+95 -19
View File
@@ -5,7 +5,6 @@
"grain": "whole",
"decision": "novox/hq ADR 0605",
"description": "The mesh's bundles and archives, by digest, in the one artifact store: the OCI distribution protocol's read side.",
"delivers": "artifact-store",
"includes": [
"registry"
],
@@ -93,6 +92,36 @@
]
},
"optional": true
},
{
"name": "list",
"description": "What the store holds of one repository, or of every repository the seat holds when none is named: every manifest keeping one bundle or archive, each with its digest, whether it is an index (and the manifests an index names), the tags naming it, its size and whether it is held. Read from the store itself, so a manifest no tag names is listed too; it reads only (ADR 0632 §3).",
"callers": "any caller whose role grants it",
"input": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "the repository, <module>/<artifact>; every repository of the seat when not given"
}
}
},
"answer": {
"type": "object",
"properties": {
"repositories": {
"type": "array",
"items": {
"$ref": "#/defs/Repository"
},
"description": "each repository named, in order of its name"
}
},
"required": [
"repositories"
]
},
"optional": true
}
],
"events": [
@@ -113,24 +142,6 @@
"name": "oci-artifact-registry.events",
"max-age-days": 7
},
"provisions": [
{
"name": "artifact-store",
"form": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "the store's address"
}
},
"required": [
"url"
],
"description": "the artifact store's read side, the OCI distribution protocol's GET and HEAD, open on the mesh network (design 51)"
}
}
],
"defs": {
"Hold": {
"type": "object",
@@ -166,6 +177,71 @@
"holder"
],
"description": "the body of held and released"
},
"Repository": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "the repository"
},
"manifests": {
"type": "array",
"items": {
"$ref": "#/defs/Listed"
},
"description": "each manifest it holds, by digest"
}
},
"required": [
"name",
"manifests"
]
},
"Listed": {
"type": "object",
"properties": {
"digest": {
"type": "string",
"description": "the manifest's digest, sha256:<hex>"
},
"index": {
"type": "boolean",
"description": "it is an index, naming a manifest per platform"
},
"platforms": {
"type": "array",
"items": {
"type": "string"
},
"description": "for an index, the digests of the manifests it names"
},
"artifact": {
"type": "string",
"description": "for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by"
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "the tags naming it, none for an untagged one"
},
"size": {
"type": "integer",
"description": "its bytes: its own content and every blob it names"
},
"held": {
"type": "boolean",
"description": "a hold keeps it from retention"
}
},
"required": [
"digest",
"index",
"size",
"held"
]
}
}
}
+37 -6
View File
@@ -15,6 +15,7 @@ export const verbs = {
tags: "tags",
hold: "hold",
release: "release",
list: "list",
} as const;
export const events = {
@@ -72,6 +73,30 @@ export interface Hold {
"reason"?: string;
}
export interface Listed {
/** for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by */
"artifact"?: string;
/** the manifest's digest, sha256:<hex> */
"digest": string;
/** a hold keeps it from retention */
"held": boolean;
/** it is an index, naming a manifest per platform */
"index": boolean;
/** for an index, the digests of the manifests it names */
"platforms"?: string[];
/** its bytes: its own content and every blob it names */
"size": number;
/** the tags naming it, none for an untagged one */
"tags"?: string[];
}
export interface Repository {
/** each manifest it holds, by digest */
"manifests": Listed[];
/** the repository */
"name": string;
}
/** is what mirror takes. */
export interface MirrorInput {
/** its pinned digest, sha256:<hex> */
@@ -145,15 +170,21 @@ export interface ReleaseAnswer {
"released": boolean;
}
/** is what list takes. */
export interface ListInput {
/** the repository, <module>/<artifact>; every repository of the seat when not given */
"name"?: string;
}
/** is what list answers. */
export interface ListAnswer {
/** each repository named, in order of its name */
"repositories": Repository[];
}
/** is the body of the event held. */
export type HeldEvent = Hold;
/** is the body of the event released. */
export type ReleasedEvent = Hold;
/** is the form of the provision artifact-store. */
export interface ArtifactStoreForm {
/** the store's address */
"url": string;
}
+37 -6
View File
@@ -13,6 +13,7 @@ export const verbs = {
remove: "remove",
hold: "hold",
release: "release",
list: "list",
} as const;
export const events = {
@@ -70,6 +71,30 @@ export interface Hold {
"reason"?: string;
}
export interface Listed {
/** for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by */
"artifact"?: string;
/** the manifest's digest, sha256:<hex> */
"digest": string;
/** a hold keeps it from retention */
"held": boolean;
/** it is an index, naming a manifest per platform */
"index": boolean;
/** for an index, the digests of the manifests it names */
"platforms"?: string[];
/** its bytes: its own content and every blob it names */
"size": number;
/** the tags naming it, none for an untagged one */
"tags"?: string[];
}
export interface Repository {
/** each manifest it holds, by digest */
"manifests": Listed[];
/** the repository */
"name": string;
}
/** is what hold takes. */
export interface HoldInput {
/** the digest */
@@ -104,15 +129,21 @@ export interface ReleaseAnswer {
"released": boolean;
}
/** is what list takes. */
export interface ListInput {
/** the repository, <module>/<artifact>; every repository of the seat when not given */
"name"?: string;
}
/** is what list answers. */
export interface ListAnswer {
/** each repository named, in order of its name */
"repositories": Repository[];
}
/** is the body of the event held. */
export type HeldEvent = Hold;
/** is the body of the event released. */
export type ReleasedEvent = Hold;
/** is the form of the provision artifact-store. */
export interface ArtifactStoreForm {
/** the store's address */
"url": string;
}
+176 -3
View File
@@ -1810,7 +1810,6 @@ export const list: readonly Seat[] = [
},
{
"decision": "novox/hq ADR 0075",
"delivers": "artifact-store",
"grain": "seat-list",
"name": "mesh-artifact-store",
"scope": "mesh"
@@ -3809,7 +3808,6 @@ export const list: readonly Seat[] = [
},
{
"decision": "novox/hq ADR 0605",
"delivers": "artifact-store",
"emits": [
"published",
"refused",
@@ -4358,12 +4356,99 @@ export const list: readonly Seat[] = [
],
"type": "object"
}
},
{
"description": "What the store holds of one repository, or of every repository the seat holds when none is named: every image manifest and index, each with its digest, whether it is an index (and the manifests an index names), the tags naming it, its size and whether it is held. Read from the store itself, so a manifest no tag names is listed too; it reads only (ADR 0632 §3).",
"input": {
"properties": {
"name": {
"description": "the repository, <module>/<artifact>; every repository of the seat when not given",
"type": "string"
}
},
"type": "object"
},
"name": "list",
"optional": true,
"output": {
"properties": {
"repositories": {
"description": "each repository named, in order of its name",
"items": {
"properties": {
"manifests": {
"description": "each manifest it holds, by digest",
"items": {
"properties": {
"artifact": {
"description": "for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by",
"type": "string"
},
"digest": {
"description": "the manifest's digest, sha256:<hex>",
"type": "string"
},
"held": {
"description": "a hold keeps it from retention",
"type": "boolean"
},
"index": {
"description": "it is an index, naming a manifest per platform",
"type": "boolean"
},
"platforms": {
"description": "for an index, the digests of the manifests it names",
"items": {
"type": "string"
},
"type": "array"
},
"size": {
"description": "its bytes: its own content and every blob it names",
"type": "integer"
},
"tags": {
"description": "the tags naming it, none for an untagged one",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"digest",
"index",
"size",
"held"
],
"type": "object"
},
"type": "array"
},
"name": {
"description": "the repository",
"type": "string"
}
},
"required": [
"name",
"manifests"
],
"type": "object"
},
"type": "array"
}
},
"required": [
"repositories"
],
"type": "object"
}
}
]
},
{
"decision": "novox/hq ADR 0605",
"delivers": "artifact-store",
"emits": [
"published",
"refused",
@@ -4817,6 +4902,94 @@ export const list: readonly Seat[] = [
],
"type": "object"
}
},
{
"description": "What the store holds of one repository, or of every repository the seat holds when none is named: every manifest keeping one bundle or archive, each with its digest, whether it is an index (and the manifests an index names), the tags naming it, its size and whether it is held. Read from the store itself, so a manifest no tag names is listed too; it reads only (ADR 0632 §3).",
"input": {
"properties": {
"name": {
"description": "the repository, <module>/<artifact>; every repository of the seat when not given",
"type": "string"
}
},
"type": "object"
},
"name": "list",
"optional": true,
"output": {
"properties": {
"repositories": {
"description": "each repository named, in order of its name",
"items": {
"properties": {
"manifests": {
"description": "each manifest it holds, by digest",
"items": {
"properties": {
"artifact": {
"description": "for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by",
"type": "string"
},
"digest": {
"description": "the manifest's digest, sha256:<hex>",
"type": "string"
},
"held": {
"description": "a hold keeps it from retention",
"type": "boolean"
},
"index": {
"description": "it is an index, naming a manifest per platform",
"type": "boolean"
},
"platforms": {
"description": "for an index, the digests of the manifests it names",
"items": {
"type": "string"
},
"type": "array"
},
"size": {
"description": "its bytes: its own content and every blob it names",
"type": "integer"
},
"tags": {
"description": "the tags naming it, none for an untagged one",
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"digest",
"index",
"size",
"held"
],
"type": "object"
},
"type": "array"
},
"name": {
"description": "the repository",
"type": "string"
}
},
"required": [
"name",
"manifests"
],
"type": "object"
},
"type": "array"
}
},
"required": [
"repositories"
],
"type": "object"
}
}
]
},