catalogue: deliver a keyless same-node provider's served facts

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
This commit is contained in:
2026-09-07 05:08:04 +02:00
parent e5eaa0478a
commit b78a911e34
2 changed files with 102 additions and 0 deletions
+20
View File
@@ -251,6 +251,26 @@ func Resolve(catalogue map[string]Manifest, assigned []string, node Node, world
needs = append(needs, Needed{
Name: want, From: node.Name, At: node.At,
Serves: servedHere(catalogue, chosen, want), For: because[want]})
} else if served := servedHere(catalogue, chosen, want); len(served) > 0 {
// Answered here with no credential to mint, but the provider serves facts the
// consumer cannot guess — a port, a model name — and so still needs a binding.
// **The reachability rule does not apply**: both ends are on this same machine, so
// there is no private network to share and nothing to refuse (novox/hq ADR 0024's
// sibling case — a same-node keyless endpoint that the consumer still cannot invent
// the port for, exactly what declaration.go's here() was left to patch after the
// fact). Delivering it here as a need is what lets knownFor see it, so a file that
// says ${bound:<provision>:port} is filled rather than refused.
//
// The address is this machine's own name on the private network when it has one, and
// loopback when it does not — a machine off the network still reaches itself, and the
// consumer's binding must carry a usable `at`. The same fallback declaration.go's
// here() applies, done here because this need is now found before here() would run.
at := node.At
if at == "" {
at = "127.0.0.1"
}
needs = append(needs, Needed{
Name: want, From: node.Name, At: at, Serves: served, For: because[want]})
}
continue
}