From 928942c78c0f84bc67fb226445515936d160955e Mon Sep 17 00:00:00 2001 From: mesh-tools Date: Tue, 20 Oct 2026 08:29:11 +0000 Subject: [PATCH] Take the catalogue from mesh-sdk in internal/recordmirror, finish the own-manifest tests, vendor/modules.txt for the pin, the capture note and the slow-package comment (issue 1381, 6 of 6 import swaps) With this commit no Go file outside vendor/ and the two packages about to go imports internal/catalogue, internal/overlay or mesh-host/validate, go.mod and vendor/modules.txt agree on mesh-sdk 14fd5cb68fd1, and the tree builds. testdata/beside/CAPTURED names the pinned SDK commit (conformance/events is unchanged since aa68b673). merge-check.sh's comment no longer names internal/catalogue as a slow package. --- internal/ownmanifest/genesis_registry_test.go | 77 +++++++++++ internal/ownmanifest/helpers_test.go | 109 ++++++++++++++++ internal/ownmanifest/own_module_json_test.go | 123 ++++++++++++++++++ internal/ownmanifest/public_issuer_test.go | 88 +++++++++++++ internal/ownmanifest/seats_test.go | 41 ++++++ internal/recordmirror/mirror_store_test.go | 2 +- internal/recordmirror/readers_test.go | 3 +- merge-check.sh | 4 +- testdata/beside/CAPTURED | 4 +- vendor/modules.txt | 6 +- 10 files changed, 449 insertions(+), 8 deletions(-) create mode 100644 internal/ownmanifest/genesis_registry_test.go create mode 100644 internal/ownmanifest/helpers_test.go create mode 100644 internal/ownmanifest/own_module_json_test.go create mode 100644 internal/ownmanifest/public_issuer_test.go create mode 100644 internal/ownmanifest/seats_test.go diff --git a/internal/ownmanifest/genesis_registry_test.go b/internal/ownmanifest/genesis_registry_test.go new file mode 100644 index 00000000..a167a3ec --- /dev/null +++ b/internal/ownmanifest/genesis_registry_test.go @@ -0,0 +1,77 @@ +package ownmanifest + +import ( + "strings" + "testing" + + . "git.novox.be/novox/mesh-sdk/go/catalogue" +) + +// agentBuilt is the build agent as the mesh holds it once built: its image pinned. +func agentBuilt(t *testing.T) Manifest { + t.Helper() + return catalogueResolved(t, "build-agent", Built{Name: "server", Kind: ArtifactImage, + Reference: "mesh-builder@sha256:" + strings.Repeat("a", 64)}) +} + +func TestGenesisAssignsTheBuildAgentWithNothingProvidingTheRegistry(t *testing.T) { + agent := agentBuilt(t) + if !agent.IsOptional("npm-package-registry") { + t.Fatalf("the build agent's npm-package-registry is hard (ADR 0336 §1): %v", agent.Requires) + } + if agent.IsOptional("artifact-store") { + t.Fatal("the build agent's artifact-store is optional; it stays hard") + } + store := map[string][]Provider{"artifact-store": { + {Node: "anchor", At: "anchor.internal", Module: "distribution", Serves: map[string]any{"port": float64(5000)}}}} + books := shelf(agent, catalogueManifest(t, "gitea"), catalogueManifest(t, "distribution")) + + // Genesis's node at step 13: the store answers, nothing provides the registry. + got, err := Resolve(books, []string{"build-agent"}, reachable(), World{Offered: store}) + if err != nil { + t.Fatalf("assigning the build agent with no registry was refused: %v", err) + } + if have := strings.Join(names(got), " "); have != "build-agent" { + t.Fatalf("assigning the build agent put %s on the machine; nothing is installed for the registry", have) + } + if len(got.Absent) != 1 || got.Absent[0] != (Absent{Consumer: "build-agent", Requirement: "npm-package-registry"}) { + t.Fatalf("the registry is not listed unbound for the build agent: %+v", got.Absent) + } + out, err := got.Declaration(Rendering{ArtifactStore: "anchor.internal:5000", + Needed: map[string]map[string]string{"build-agent": {"broker": "sealed-broker"}}}) + if err != nil { + t.Fatalf("the build agent does not compose with the registry unbound: %v", err) + } + for _, id := range []string{"build-agent." + BoundID("npm-package-registry"), "build-agent." + SecretID("npm-package-registry"), + "build-agent." + NeedID("genesis-package-registry")} { + if fileNamed(out, id) != nil { + t.Errorf("%s was delivered with the registry unbound and no genesis document given", id) + } + } + + // The genesis document, once the installer seals it at the packages step (ADR 0336 §3). + out, err = got.Declaration(Rendering{ArtifactStore: "anchor.internal:5000", + Needed: map[string]map[string]string{"build-agent": {"broker": "sealed-broker", "genesis-package-registry": "sealed-genesis"}}}) + if err != nil { + t.Fatal(err) + } + doc := fileNamed(out, "build-agent."+NeedID("genesis-package-registry")) + if doc == nil || doc["sealed"] != "sealed-genesis" || !strings.HasSuffix(doc["path"].(string), "/genesis-package-registry.json") { + t.Fatalf("the genesis document is not delivered where the agent reads it: %v", doc) + } + + // The gitea module assigned: the requirement binds, with no act on the build agent (ADR 0336 §2). + registry := map[string][]Provider{"artifact-store": store["artifact-store"], "npm-package-registry": { + {Node: "anchor", At: "anchor.internal", Module: "gitea", + Serves: map[string]any{"port": float64(3000), "scheme": "http", "npm-path": "/api/packages/novox/npm/"}}}} + bound, err := Resolve(books, []string{"build-agent"}, reachable(), World{Offered: registry}) + if err != nil { + t.Fatal(err) + } + if n := boundFrom(t, bound, "build-agent", "npm-package-registry"); n.Module != "gitea" { + t.Fatalf("bound to %s", n.Module) + } + if len(bound.Absent) != 0 { + t.Fatalf("the registry is still listed unbound once gitea is assigned: %+v", bound.Absent) + } +} diff --git a/internal/ownmanifest/helpers_test.go b/internal/ownmanifest/helpers_test.go new file mode 100644 index 00000000..281e0726 --- /dev/null +++ b/internal/ownmanifest/helpers_test.go @@ -0,0 +1,109 @@ +package ownmanifest + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "git.novox.be/novox/mesh-sdk/go/catalogue" + "github.com/novox/mesh-controller/internal/beside" +) + +// Moved from internal/catalogue (issue 1381) with the tests below; each helper is the one the old package's tests +// shared, over the SDK's exported API. + +var bundleDigest = "sha256:" + strings.Repeat("b", 64) + +func fileNamed(out []map[string]any, id string) map[string]any { + for _, r := range out { + if r["id"] == id { + return r + } + } + return nil +} + +func shelf(ms ...catalogue.Manifest) map[string]catalogue.Manifest { + out := map[string]catalogue.Manifest{} + for _, m := range ms { + out[m.Module] = m + } + return out +} + +func reachable() catalogue.Node { + return catalogue.Node{Name: "workstation", Site: "house", At: "workstation.internal", + Capabilities: map[string]bool{"seat": true, "container-runtime": true}} +} + +func names(r catalogue.Resolution) []string { + var out []string + for _, m := range r.Modules { + out = append(out, m.Module) + } + return out +} + +func boundFrom(t *testing.T, r catalogue.Resolution, consumer, provision string) catalogue.Needed { + t.Helper() + for _, n := range r.Needs { + if n.For == consumer && n.Name == provision { + return n + } + } + t.Fatalf("no binding of %s for %s: %+v", consumer, provision, r.Needs) + return catalogue.Needed{} +} + +// moduleTree is where a module is read from: this repository's own modules/ when it lives whole here +// (novox/hq ADR 0420 §1, issue 1019: build-agent and route-proxy), with this repository as the tree root; +// otherwise the catalogue beside, with the catalogue as the root. +func moduleTree(t *testing.T, module string) (root, dir string) { + t.Helper() + own := filepath.Join("..", "..", "modules", module) + if _, err := os.Stat(filepath.Join(own, "module.json")); err == nil { + return filepath.Join("..", ".."), own + } + return beside.Dir(t, "mesh-catalog"), filepath.Join(beside.Catalogue(t), module) +} + +func catalogueManifest(t *testing.T, module string) catalogue.Manifest { + t.Helper() + root, dir := moduleTree(t, module) + m, err := catalogue.ReadTreeManifest(root, dir) + if err != nil { + t.Fatalf("%s does not parse:\n%v", module, err) + } + return m +} + +// catalogueResolved is a catalogue module as the mesh holds it once built: the manifest as written, resolved against +// what the build produced besides the archive its templates are packed into (novox/hq ADR 0349 §2). +func catalogueResolved(t *testing.T, module string, built ...catalogue.Built) catalogue.Manifest { + t.Helper() + root, dir := moduleTree(t, module) + m, err := catalogue.ReadTreeManifestWritten(root, dir) + if err != nil { + t.Fatalf("%s does not parse:\n%v", module, err) + } + if name := catalogue.TemplatesArtifact(m); name != "" { + read, err := catalogue.ReadTemplates(m, dir) + if err != nil { + t.Fatalf("%s: %v", module, err) + } + // By the path inside the archive, as the build reads it back, not by the path in the module's directory. + texts := map[string]string{} + for path, text := range read { + texts[strings.TrimPrefix(path, m.TemplatesDir()+"/")] = text + } + built = append(built, catalogue.Built{Name: name, Kind: catalogue.ArtifactArchive, + Reference: catalogue.ArtifactStoreScheme + module + "/" + name + "/blobs/" + bundleDigest, Digest: bundleDigest, + Texts: texts}) + } + held, err := m.Resolve(built) + if err != nil { + t.Fatalf("%s does not resolve against its own build: %v", module, err) + } + return held +} diff --git a/internal/ownmanifest/own_module_json_test.go b/internal/ownmanifest/own_module_json_test.go new file mode 100644 index 00000000..743caa35 --- /dev/null +++ b/internal/ownmanifest/own_module_json_test.go @@ -0,0 +1,123 @@ +package ownmanifest + +import ( + "os" + "path/filepath" + "strings" + "testing" + + . "git.novox.be/novox/mesh-sdk/go/catalogue" +) + +func TestTheControllersOwnManifestClaimsItsSeat(t *testing.T) { + // The seat test above fabricates manifests, so deleting the claim from the real module.json + // would fail nothing (novox/hq issue 059's review). This binds the one manifest this + // repository owns: the controller claims the mesh-scoped seat named after its server + // (ADR 0079), or the one-controller property is convention again. + raw, err := os.ReadFile(filepath.Join("..", "..", "module.json")) + if err != nil { + t.Fatalf("the controller's own manifest is unreadable: %v", err) + } + m, err := ParseManifest(raw) + if err != nil { + t.Fatalf("the controller's own manifest does not parse: %v", err) + } + for _, c := range m.Claims { + if c.Name == "mesh-controller" && c.At() == ScopeMesh { + return + } + } + t.Fatalf("module.json no longer claims the mesh-scoped mesh-controller seat: %+v", m.Claims) +} + +// **The control plane's own manifest, composed through the whole path.** +// +// The store was given 6852 and the broker's plain port 5679 (the control-node's migration, novox/hq +// 04-ISSUES/102). The control plane's own connections are sealed at genesis with the ports genesis +// wrote; what its container is told beside them is where this machine put the store and the +// broker now, read from the node's settings exactly as every consumer's binding is. +func TestTheControlPlanesOwnAddressesFollowTheNodesPorts(t *testing.T) { + raw, err := os.ReadFile("../../module.json") + if err != nil { + t.Fatal(err) + } + m, err := ParseManifest(raw) + if err != nil { + t.Fatalf("the control plane's own manifest does not parse:\n%v", err) + } + // The manifest itself names them now; withSeatPorts is a no-op on it, and this holds it so. + for _, r := range m.Resources { + if r["type"] != "process" { + continue + } + env, _ := r["env"].(map[string]any) + for key, want := range seatPorts { + if env[key] != want { + t.Errorf("module.json says %s=%v, not %q", key, env[key], want) + } + } + } + control, err := m.Resolve([]Built{{ + Name: "controller", Kind: ArtifactBundle, + Reference: ArtifactStoreScheme + "mesh-controller/controller@sha256:" + strings.Repeat("c", 64), + Digest: "sha256:" + strings.Repeat("c", 64), + }}) + if err != nil { + t.Fatal(err) + } + r := Resolution{Node: "anchor", Modules: []Manifest{control}} + needed := map[string]map[string]string{"mesh-controller": {}} + for name := range m.OwnSecrets { + needed["mesh-controller"][name] = "sealed-" + name + } + out, err := r.Declaration(Rendering{ + Needed: needed, + ArtifactStore: "anchor.internal:5100", + Seats: map[string]map[int]int{ + "mesh-store": {5432: 6852}, + "mesh-broker": {5671: 5671, 5672: 5679, 15672: 15673}, + }, + }) + if err != nil { + t.Fatalf("the control plane does not compose: %v", err) + } + server := fileNamed(out, "mesh-controller.controller") + if server == nil { + t.Fatalf("the control plane's process is not in the declaration: %v", out) + } + env, _ := server["env"].(map[string]any) + for key, want := range map[string]string{ + "MESH_STORE_INVENTORY_PORT": "6852", + "MESH_STORE_IDENTITY_PORT": "6852", + "MESH_STORE_LICENCES_PORT": "6852", + "MESH_BROKER_MANAGEMENT_PORT": "15673", + "MESH_BROKER_ADDRESS_PORT": "5671", + } { + if env[key] != want { + t.Errorf("the control plane is told %s=%v; the node put it on %s", key, env[key], want) + } + } + if got := server["source"]; got != "anchor.internal:5100/mesh-controller/controller@sha256:"+strings.Repeat("c", 64) { + t.Errorf("the control plane's own bundle is fetched from %v, not routed through the store", got) + } + + // And on a mesh where the foundation is where genesis raised it, nothing is added. + out, err = r.Declaration(Rendering{Needed: needed, ArtifactStore: "anchor.internal:5100"}) + if err != nil { + t.Fatal(err) + } + env, _ = fileNamed(out, "mesh-controller.controller")["env"].(map[string]any) + if env["MESH_STORE_INVENTORY_PORT"] != "" { + t.Errorf("with no settings, the control plane is told %v", env) + } +} + +// seatPorts is what the control plane's manifest says beside each sealed connection: the port +// this machine put the seat's holder at (novox/hq 04-ISSUES/102). +var seatPorts = map[string]string{ + "MESH_STORE_INVENTORY_PORT": "${seat:mesh-store:5432}", + "MESH_STORE_IDENTITY_PORT": "${seat:mesh-store:5432}", + "MESH_STORE_LICENCES_PORT": "${seat:mesh-store:5432}", + "MESH_BROKER_MANAGEMENT_PORT": "${seat:mesh-broker:15672}", + "MESH_BROKER_ADDRESS_PORT": "${seat:mesh-broker:5671}", +} diff --git a/internal/ownmanifest/public_issuer_test.go b/internal/ownmanifest/public_issuer_test.go new file mode 100644 index 00000000..2d3e7d7e --- /dev/null +++ b/internal/ownmanifest/public_issuer_test.go @@ -0,0 +1,88 @@ +package ownmanifest + +import ( + "testing" + + . "git.novox.be/novox/mesh-sdk/go/catalogue" +) + +// The public issuer is the proxy's own fact (novox/hq ADR 0226), and the folding of it must not +// move the proxy's account. +// +// route-proxy keeps each ACME authority's account and certificates in a directory named after a +// digest of the directory URL and of the root bundle its `trust` container copies in +// (examples/route-proxy, forThisAuthority). Until ADR 0226 the URL was rendered from a binding to +// `public-acme`'s `acme-ca`, spelled with the port. A URL spelled any other way — even the same +// authority without `:443` — or a root bundle from another image is a new authority to the proxy: +// a new account, and every routed name ordered again, on two machines at once, against Let's +// Encrypt's rate limits. So what the module now states is held to exactly what the binding rendered. + +// renderedBeforeTheFold is route-proxy's acme.env as the binding to public-acme rendered it on every +// machine running the proxy, read from the controller's plan on 2026-10-06. +const renderedBeforeTheFold = "ACME_DIRECTORY=https://acme-v02.api.letsencrypt.org:443/directory\n" + + "ACME_ROOTS=https://acme-v02.api.letsencrypt.org:443\n" + + "ACME_ROOTS_PATH=\n" + +// trustImage is the image whose system bundle becomes the public root the account directory is +// named after. Moving it renames that directory. +const trustImage = "alpine@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b" + +func TestRouteProxyKeepsItsPublicAccountDirectory(t *testing.T) { + m := catalogueManifest(t, "route-proxy") + for _, r := range m.Requires { + if r == "acme-ca" { + t.Fatal("route-proxy still asks for acme-ca; the public issuer is its own fact (ADR 0226)") + } + } + + // As a build would leave it: each artifact a container names resolved to an image. Which image + // does not matter to the file checked here. + for _, res := range m.Resources { + if artifact, ok := res["artifact"].(string); ok { + delete(res, "artifact") + res["image"] = "registry.invalid/route-proxy/" + artifact + + "@sha256:1111111111111111111111111111111111111111111111111111111111111111" + } + } + r := Resolution{ + Node: "anchor", + Modules: []Manifest{m}, + Needs: []Needed{{ + Name: "internal-acme-ca", From: "home", At: "home.internal", For: "route-proxy", + Serves: map[string]any{ + "port": float64(9000), "path": "/acme/acme/directory", "roots": "/roots.pem", + }, + }}, + } + // Its own broker credential, sealed as the mesh would; what it is does not matter here. + out, err := r.Declaration(Rendering{Needed: map[string]map[string]string{ + "route-proxy": {"broker": "sealed"}}}) + if err != nil { + t.Fatalf("route-proxy could not be composed for a machine: %v", err) + } + env := fileNamed(out, "route-proxy.acme-env") + if env == nil { + t.Fatalf("nothing writes the public issuer's environment: %v", out) + } + if got, _ := env["content"].(string); got != renderedBeforeTheFold { + t.Fatalf("acme.env changed, which moves the proxy's account and reorders every certificate:\n"+ + "got %q\nwant %q", got, renderedBeforeTheFold) + } + if fileNamed(out, "route-proxy.bound-acme-ca") != nil { + t.Error("a binding to acme-ca is still written") + } + + var trust string + if m.Build != nil { + for _, a := range m.Build.Artifacts { + if a.Name == "trust" { + trust = a.From + } + } + } + if trust != trustImage { + t.Errorf("the trust image is %q, not %q: its system bundle is the public root the account "+ + "directory is named after, so moving it reorders every certificate. Move it only with a "+ + "plan for the account (ADR 0226)", trust, trustImage) + } +} diff --git a/internal/ownmanifest/seats_test.go b/internal/ownmanifest/seats_test.go new file mode 100644 index 00000000..e87d0466 --- /dev/null +++ b/internal/ownmanifest/seats_test.go @@ -0,0 +1,41 @@ +package ownmanifest + +import ( + "os" + "path/filepath" + "testing" + + "git.novox.be/novox/mesh-sdk/go/catalogue" + "github.com/novox/mesh-controller/internal/beside" +) + +// Every module in use claims a seat in the set, so closing it refuses nothing that runs. +// +// Read from the catalogue (internal/beside) and from this repository's own manifest, the two places a +// manifest lives (ADR 0069). The private-network module's manifest is composed in code, and its claim +// is checked where it is composed. +func TestEveryManifestInUseClaimsASeatTheMeshDefines(t *testing.T) { + paths, _ := filepath.Glob(filepath.Join(beside.Catalogue(t), "*", "module.json")) + if len(paths) == 0 { + t.Fatal("no manifests in the catalogue, so this proved nothing") + } + paths = append(paths, "../../module.json") + var checked int + for _, path := range paths { + raw, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + // The SDK's claimProblems is unexported; the parser runs it on every manifest whose claims are well formed + // (go/catalogue manifest.go), so a manifest that parses claims only seats the mesh defines or declares. + m, err := catalogue.ParseManifest(raw) + if err != nil { + t.Errorf("%s: %v", path, err) + continue + } + checked += len(m.Claims) + } + if checked == 0 { + t.Fatal("no claims were checked, so this proved nothing") + } +} diff --git a/internal/recordmirror/mirror_store_test.go b/internal/recordmirror/mirror_store_test.go index 7e4cbdda..4b706724 100644 --- a/internal/recordmirror/mirror_store_test.go +++ b/internal/recordmirror/mirror_store_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" + "git.novox.be/novox/mesh-sdk/go/catalogue" "github.com/novox/mesh-controller/internal/broker" - "github.com/novox/mesh-controller/internal/catalogue" "github.com/novox/mesh-controller/internal/inventory" ) diff --git a/internal/recordmirror/readers_test.go b/internal/recordmirror/readers_test.go index 864a22ea..91736ad0 100644 --- a/internal/recordmirror/readers_test.go +++ b/internal/recordmirror/readers_test.go @@ -17,7 +17,7 @@ import ( // mayNameAMirror are the files of the controller that may name a record mirror's bucket outside their tests: // where the buckets are defined, asserted and granted (internal/broker), the list a reader names in `reads` -// (internal/catalogue/state.go), and each mirror's own writer (this package and the loop that runs it). The +// (the catalogue's state.go, now mesh-sdk's), and each mirror's own writer (this package and the loop that runs it). The // audit of a mirror against its store (ADR 0289 §4, issue 419) joins this list when it is built. var mayNameAMirror = []string{ // The chain opens its buckets, the assignments' among them, which stopped being a mirror when the chain took them @@ -28,7 +28,6 @@ var mayNameAMirror = []string{ // The grant of a machine's own keys of the assignments mirror to its provisioner client (design 57 P3). "internal/broker/provisioner.go", "internal/broker/writers.go", - "internal/catalogue/state.go", } // The packages whose names stand for a mirror, by import path, and the names in each. diff --git a/merge-check.sh b/merge-check.sh index 4c5f0a52..9cd42198 100755 --- a/merge-check.sh +++ b/merge-check.sh @@ -28,8 +28,8 @@ export GOFLAGS=-mod=vendor GOPROXY=off # **The unit level's bound, in seconds** (issue 895): the slowest package's own test time of the untagged run, the # compile not counted. A run over it fails the check and names the slowest tests. It is 5 s since the tests carry the -# `integration` tag (design 47 milestone 3): the slowest packages, cmd/mesh-controller and internal/catalogue, measured -# 2.6-2.8 s on a quiet machine in about 2000 small tests each, and the build node runs the store tests beside this run +# `integration` tag (design 47 milestone 3): the slowest package, cmd/mesh-controller (internal/catalogue went to mesh-sdk, issue +# 1381), measured 2.6-2.8 s on a quiet machine in about 2000 small tests, and the build node runs the store tests beside this run # and other work besides, so twice the measure. How-we-build section 5 asks 1 s for the whole unit suite: issue 1055 # (a catalogue loaded once per package run, fewer source-scanning tests, the timed run started alone). unit_bound=5 diff --git a/testdata/beside/CAPTURED b/testdata/beside/CAPTURED index 2b8b844e..5a005d0d 100644 --- a/testdata/beside/CAPTURED +++ b/testdata/beside/CAPTURED @@ -9,7 +9,7 @@ a second place to clean besides the catalogue itself. mesh-catalog e0bc919d4945bb5f13f971f74643a0b714cf45da modules/*/module.json, modules/nats/Dockerfile mesh-host 9d9e3a46c4f5943cfffb413d962ce48b75e2fd39 examples/foundation-first-node.lock -mesh-sdk aa68b67389c964ea3e573bc50630c77d6518cdab conformance/events/module-event.json +mesh-sdk 14fd5cb68fd157390087b41f293b569dda6c37a6 conformance/events/module-event.json To move them, from this repository's root, with the two repositories checked out beside it: @@ -27,7 +27,7 @@ and write the commits here. The SDK is captured at the commit go.mod pins for gi git -C ../mesh-sdk archive conformance/events | tar -x -C testdata/beside/mesh-sdk And from mesh-host at the same commit as its line above, the node-engine's placement guard, which -internal/catalogue's engine_guard_test.go holds the controller's resolved-path rules to (novox/hq issue 496), +mesh-sdk's go/catalogue engine_guard_test.go holds the controller's resolved-path rules to (novox/hq issue 496), kept as .captured so no Go tool reads it as this repository's code: mkdir -p testdata/beside/mesh-host/internal/apply diff --git a/vendor/modules.txt b/vendor/modules.txt index ff0a7f49..ce761ef2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,8 +1,12 @@ -# git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261019091011-aa68b67389c9 +# git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261020071554-14fd5cb68fd1 ## explicit; go 1.26.0 git.novox.be/novox/mesh-sdk/go/asks +git.novox.be/novox/mesh-sdk/go/catalogue +git.novox.be/novox/mesh-sdk/go/declaration git.novox.be/novox/mesh-sdk/go/delivery +git.novox.be/novox/mesh-sdk/go/overlay git.novox.be/novox/mesh-sdk/go/seal +git.novox.be/novox/mesh-sdk/go/validate # github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op ## explicit; go 1.24.0 github.com/antithesishq/antithesis-sdk-go/assert