A node-scope provider that answers a requirement on the same machine and
serves connection facts (a port) but mints no credential delivered
nothing to a co-located consumer. resolve.go only built the delivering
Needed when brokered[want] was set — true only for mesh-scope providers;
a node-scope keyless provider set local[want] instead and fell through,
so knownFor saw no binding and boundInto refused the consumer's
${bound:model-access:port} file.
Deliver the served facts as a need whenever the same-node answer serves a
non-empty set, with a loopback fallback for the address when the node is
off the private network — the reachability rule does not apply to two ends
on one machine. The brokered (credentialed, mesh-scope) path is untouched.
Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
270 lines
11 KiB
Go
270 lines
11 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func aModelUser() Manifest {
|
|
return Manifest{Module: "assistant", Requires: []string{"model-access"},
|
|
Binds: map[string]string{"model-access": "/etc/assistant/model.json"},
|
|
Secrets: map[string]string{"model-access": "/etc/assistant/key"}}
|
|
}
|
|
|
|
// A provision answered by a record rather than a node.
|
|
//
|
|
// novox/hq ADR 0024: a hosted model is on nobody's machine and is reached over the public
|
|
// internet, so the rule that refuses two ends sharing no private network must not apply. This
|
|
// node is deliberately not on the private network at all — under the old rule that alone would
|
|
// refuse it.
|
|
func TestAProvisionAnsweredByARecordDoesNotNeedAPrivateNetwork(t *testing.T) {
|
|
got, err := Resolve(
|
|
map[string]Manifest{"assistant": aModelUser()},
|
|
[]string{"assistant"},
|
|
Node{Name: "workstation"},
|
|
World{
|
|
Licences: map[string][]Record{"model-access": {{Name: "personal",
|
|
Serves: map[string]any{"model": "a-model"}}}},
|
|
Using: map[string]map[string]Record{"assistant": {"model-access": {Name: "personal",
|
|
Serves: map[string]any{"model": "a-model"}}}},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("a machine off the private network could not be given model access: %v", err)
|
|
}
|
|
if len(got.Needs) != 1 {
|
|
t.Fatalf("the licence was not recorded as something this node takes: %+v", got.Needs)
|
|
}
|
|
if !got.Needs[0].ByRecord {
|
|
t.Fatal("the licence was treated as a machine, so the reachability rule would apply to it")
|
|
}
|
|
if got.Needs[0].From != "personal" {
|
|
t.Fatalf("the licence is not named by what a person calls it: %+v", got.Needs[0])
|
|
}
|
|
}
|
|
|
|
// Refused when the consumer has not said which — and the refusal names the candidates and the
|
|
// command, because ADR 0024 warns this will be felt: a mesh holding three ways to reach a model
|
|
// refuses every consumer that has not chosen.
|
|
func TestAConsumerThatHasNotSaidWhichLicenceIsRefusedWithTheCandidates(t *testing.T) {
|
|
_, err := Resolve(
|
|
map[string]Manifest{"assistant": aModelUser()},
|
|
[]string{"assistant"},
|
|
Node{Name: "workstation"},
|
|
World{Licences: map[string][]Record{"model-access": {
|
|
{Name: "personal"}, {Name: "the-organisation"},
|
|
}}})
|
|
if err == nil {
|
|
t.Fatal("a consumer was given model access without anybody saying which")
|
|
}
|
|
said := err.Error()
|
|
for _, want := range []string{"personal", "the-organisation", "licence use"} {
|
|
if !strings.Contains(said, want) {
|
|
t.Fatalf("the refusal does not name %q, so it is correct and unusable:\n%s", want, said)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A model the mesh runs itself answers it locally, and a record is not consulted.
|
|
func TestAModelInTheMeshsOwnSetAnswersItWithoutALicence(t *testing.T) {
|
|
got, err := Resolve(
|
|
map[string]Manifest{
|
|
"assistant": aModelUser(),
|
|
"ollama": {Module: "ollama",
|
|
Provides: []Offer{{Name: "model-access", Scope: ScopeNode}}},
|
|
},
|
|
[]string{"assistant", "ollama"},
|
|
Node{Name: "workstation"},
|
|
World{Licences: map[string][]Record{"model-access": {{Name: "personal"}}}})
|
|
if err != nil {
|
|
t.Fatalf("a machine running its own model was asked to choose a licence: %v", err)
|
|
}
|
|
for _, n := range got.Needs {
|
|
if n.ByRecord {
|
|
t.Fatal("a record was used although the answer was on this machine")
|
|
}
|
|
}
|
|
}
|
|
|
|
// A key that was never supplied is refused by name rather than silently not written.
|
|
//
|
|
// The mesh discarded the plaintext when the key was accepted and cannot seal another, so a
|
|
// machine that resolved cleanly would receive no file and fail at whatever read it.
|
|
func TestAModuleOnALicenceWithNoKeyIsRefusedRatherThanLeftEmpty(t *testing.T) {
|
|
r := Resolution{
|
|
Node: "workstation",
|
|
Modules: []Manifest{aModelUser()},
|
|
Needs: []Needed{{Name: "model-access", From: "personal", ByRecord: true, For: "assistant"}},
|
|
}
|
|
_, err := r.Declaration(Rendering{})
|
|
if err == nil {
|
|
t.Fatal("a module was given a licence with no key, so it receives nothing and fails later")
|
|
}
|
|
if !strings.Contains(err.Error(), "licence key personal") {
|
|
t.Fatalf("the refusal does not say how to fix it: %v", err)
|
|
}
|
|
}
|
|
|
|
// And with a key, both files arrive: what is public, and what is not.
|
|
func TestALicenceDeliversWhatIsPublicAndWhatIsSealed(t *testing.T) {
|
|
r := Resolution{
|
|
Node: "workstation",
|
|
Modules: []Manifest{aModelUser()},
|
|
Needs: []Needed{{Name: "model-access", From: "personal", ByRecord: true, For: "assistant",
|
|
Serves: map[string]any{"model": "a-model"}, Sealed: "sealed-blob"}},
|
|
}
|
|
out, err := r.Declaration(Rendering{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
files := map[string]map[string]any{}
|
|
for _, res := range out {
|
|
if path, ok := res["path"].(string); ok {
|
|
files[path] = res
|
|
}
|
|
}
|
|
bound, given := files["/etc/assistant/model.json"]
|
|
if !given {
|
|
t.Fatal("the consumer was not told what it needs to know that is not secret")
|
|
}
|
|
content, _ := bound["content"].(string)
|
|
if !strings.Contains(content, "a-model") {
|
|
t.Fatalf("the binding does not carry what the licence serves:\n%s", content)
|
|
}
|
|
// The binding says it is a record rather than leaving an empty address, which a reader would
|
|
// take for something the mesh failed to fill in.
|
|
if !strings.Contains(content, "not a machine") {
|
|
t.Fatalf("the binding leaves an empty address with no explanation:\n%s", content)
|
|
}
|
|
key, delivered := files["/etc/assistant/key"]
|
|
if !delivered {
|
|
t.Fatal("the key was not delivered")
|
|
}
|
|
if key["sealed"] != "sealed-blob" {
|
|
t.Fatalf("the key is not the sealed one: %+v", key)
|
|
}
|
|
// And never in the open. The whole arrangement is that what travels is unreadable by
|
|
// everything between here and the machine.
|
|
if strings.Contains(content, "sealed-blob") {
|
|
t.Fatal("the key was written into the public file as well")
|
|
}
|
|
}
|
|
|
|
// One machine's unresolvable module must not remove it from the private network.
|
|
//
|
|
// The pass that answers *what does this node offer* takes a failed resolution to mean it learned
|
|
// nothing — so refusing an unanswerable requirement there makes the machine disappear, and every
|
|
// other machine is then told, wrongly, that the two of them share no network. A wrong answer about
|
|
// a machine nobody asked about, caused by a fault on a third.
|
|
func TestAnUnanswerableRequirementDoesNotRemoveAMachineFromTheMesh(t *testing.T) {
|
|
shelf := map[string]Manifest{
|
|
"assistant": aModelUser(),
|
|
"networking": {Module: "networking",
|
|
Provides: []Offer{{Name: "mesh-network", Scope: ScopeNode}}},
|
|
}
|
|
// The first pass: what does this machine offer? It is assigned something nothing answers.
|
|
got, err := Resolve(shelf, []string{"assistant", "networking"},
|
|
Node{Name: "laptop"}, World{Unchecked: true})
|
|
if err != nil {
|
|
t.Fatalf("a machine with one unanswerable requirement was lost entirely: %v", err)
|
|
}
|
|
var offers bool
|
|
for _, m := range got.Modules {
|
|
for _, o := range m.Offers() {
|
|
if o == "mesh-network" {
|
|
offers = true
|
|
}
|
|
}
|
|
}
|
|
if !offers {
|
|
t.Fatal("the machine's own network module was not seen, so it looks off the network")
|
|
}
|
|
|
|
// And the second pass, where the question is actually being asked, still refuses it.
|
|
if _, err := Resolve(shelf, []string{"assistant", "networking"},
|
|
Node{Name: "laptop"}, World{}); err == nil {
|
|
t.Fatal("the requirement nothing answers was accepted when it was actually asked")
|
|
}
|
|
}
|
|
|
|
// A same-node provider that mints no credential but serves a port the consumer cannot guess must
|
|
// still deliver that port. A `local-model` provider (ollama) provides `model-access` at node scope
|
|
// and serves a port and a model name, minting nothing; a co-located consumer requires and binds it
|
|
// and has a file that names `${bound:model-access:port}`. The served facts never reached the
|
|
// consumer's needs — node scope set `local`, not `brokered`, so the delivering branch was skipped —
|
|
// and the consumer's file was refused for naming a provision it "did not require". The endpoint is
|
|
// keyless, but a port is still a fact nobody can invent.
|
|
func TestAKeylessSameNodeProviderStillDeliversWhatItServes(t *testing.T) {
|
|
provider := Manifest{Module: "local-model",
|
|
Provides: []Offer{{Name: "model-access", Scope: ScopeNode}},
|
|
Serves: map[string]map[string]any{
|
|
"model-access": {"port": 11434, "model": "a-model"}}}
|
|
consumer := Manifest{Module: "assistant", Requires: []string{"model-access"},
|
|
Binds: map[string]string{"model-access": "/etc/assistant/model.json"}}
|
|
|
|
// node.At empty: a single-node deployment with no private network. The delivered binding must
|
|
// still carry a usable address — loopback, because a machine off the network still reaches
|
|
// itself, and an empty `at` would be written into the consumer's file as a host that resolves
|
|
// to nothing.
|
|
got, err := Resolve(
|
|
map[string]Manifest{"local-model": provider, "assistant": consumer},
|
|
[]string{"assistant", "local-model"},
|
|
Node{Name: "workstation"},
|
|
World{})
|
|
if err != nil {
|
|
t.Fatalf("a keyless same-node model endpoint was refused: %v", err)
|
|
}
|
|
|
|
var found *Needed
|
|
for i := range got.Needs {
|
|
if got.Needs[i].Name == "model-access" && got.Needs[i].For == "assistant" {
|
|
found = &got.Needs[i]
|
|
}
|
|
}
|
|
if found == nil {
|
|
t.Fatalf("the served endpoint was not delivered to the consumer at all: %+v", got.Needs)
|
|
}
|
|
if found.ByRecord {
|
|
t.Fatal("a same-node endpoint was treated as a record, so the reachability rule would apply")
|
|
}
|
|
if found.At == "" {
|
|
t.Fatalf("the binding carries no address, so its file names a host that resolves to nothing: %+v", found)
|
|
}
|
|
if found.At != "127.0.0.1" {
|
|
t.Fatalf("off the private network the address must fall back to loopback, got %q", found.At)
|
|
}
|
|
if got, want := plainly(found.Serves["port"]), "11434"; got != want {
|
|
t.Fatalf("the port the consumer cannot guess was not delivered: got %q want %q", got, found.Serves)
|
|
}
|
|
if found.Serves["model"] != "a-model" {
|
|
t.Fatalf("the extra served fact was not delivered: %+v", found.Serves)
|
|
}
|
|
|
|
// End to end: a file that names ${bound:model-access:...} is filled rather than refused, which
|
|
// is the whole failure this fixes — boundInto reads knownFor, and knownFor reads the needs.
|
|
consumer.Resources = []map[string]any{{
|
|
"id": "env", "type": "file", "path": "/etc/assistant/.env",
|
|
"content": "OPENAI_BASE_URL=http://${bound:model-access:at}:${bound:model-access:port}/v1\n",
|
|
}}
|
|
got, err = Resolve(
|
|
map[string]Manifest{"local-model": provider, "assistant": consumer},
|
|
[]string{"assistant", "local-model"},
|
|
Node{Name: "workstation"},
|
|
World{})
|
|
if err != nil {
|
|
t.Fatalf("resolution refused the consumer with a bound file: %v", err)
|
|
}
|
|
out, err := got.Declaration(Rendering{})
|
|
if err != nil {
|
|
t.Fatalf("the declaration refused the consumer's bound file: %v", err)
|
|
}
|
|
var env string
|
|
for _, res := range out {
|
|
if res["path"] == "/etc/assistant/.env" {
|
|
env, _ = res["content"].(string)
|
|
}
|
|
}
|
|
if want := "http://127.0.0.1:11434/v1"; !strings.Contains(env, want) {
|
|
t.Fatalf("the bound file was not filled with the served endpoint, want %q:\n%s", want, env)
|
|
}
|
|
}
|