What provides the artifact store cannot be delivered through it

Refused where it is written: a module that provides `artifact-store` and
also builds artifacts asks the mesh to put an artifact into the thing
that artifact is needed to create. Building publishes to the store, and
the builder will not start without one — "a built artifact nobody can
fetch is not built".

This is the question the substrate record asks of every candidate: can
it grant itself the thing it provides? The store cannot create its own
database, the broker cannot create its own virtual host, and a registry
cannot grant itself a repository. The first two are why they are in the
bundle. This is the same sentence, unenforced.

So such a module names its image, exactly as the bundle names the three
a first node starts from. One that wants an interface or a tool server
beside it is a second module, mirrored the ordinary way once the first
is running — a real limit, and better said here than discovered on a
mesh new enough that nobody is watching it.

Refused at the manifest because the alternative is a build that never
returns.

The provision name is now a constant. A string compared in one place is
a convention; a string a rule turns on is a fact.
This commit is contained in:
2026-09-01 21:18:14 +02:00
parent 4d6ec5b10c
commit be62f49eab
2 changed files with 84 additions and 1 deletions
+36
View File
@@ -310,6 +310,14 @@ const (
ArtifactUpstream = "upstream"
)
// ArtifactStoreProvision is the name a module offers when it is the mesh's store for what modules
// ship — images, and archives, which are directories from a repository packed as blobs.
//
// Named here because a rule depends on it: what provides this cannot be delivered through it
// (novox/hq 04-ISSUES/029). A string compared in one place is a convention; a string a rule turns
// on is a fact, and it should be written once.
const ArtifactStoreProvision = "artifact-store"
// Listening is one port a module accepts connections on.
type Listening struct {
Port int `json:"port"`
@@ -516,6 +524,34 @@ func ParseManifest(raw []byte) (Manifest, error) {
}
}
problems = append(problems, m.Build.problems(m.Module)...)
// **What provides the artifact store cannot be delivered through it** (novox/hq 04-ISSUES/029).
//
// Building publishes to the store, and the builder will not start without one. So a module
// that provides the store and also builds something asks the mesh to put an artifact into the
// thing that artifact is needed to create.
//
// It is the question the substrate record asks of every candidate — can it grant itself the
// thing it provides? The store cannot create its own database, the broker cannot create its
// own virtual host, and a registry cannot grant itself a repository. Such a module names its
// image, exactly as the bundle names the three a first node starts from.
//
// Refused here because the alternative is a build that never returns, on a mesh new enough
// that nobody is watching it yet.
if m.Build != nil && len(m.Build.Artifacts) > 0 {
for _, o := range m.Offers() {
if o != ArtifactStoreProvision {
continue
}
problems = append(problems, fmt.Sprintf(
"%s provides %q and also builds %d artifact(s), which cannot both be true: "+
"building publishes to the artifact store, so this asks the mesh to put an "+
"artifact into the thing that artifact is needed to create. A module that "+
"provides the store names its image instead. One that wants more beside it "+
"— an interface, a tool server — is a second module, mirrored in the "+
"ordinary way once this one is running",
m.Module, ArtifactStoreProvision, len(m.Build.Artifacts)))
}
}
for to := range m.Serves {
var offered bool
for _, o := range m.Offers() {