mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate head 205f1541efa5 merged into trunk 6f1a05366c1d — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head 205f1541efa5 merged into trunk 6f1a05366c1d — pass: its merge-check.sh passed
The forge writes every package tarball at its public name, and its consumers had no source for that name but a second copy kept by hand, though the mesh composes it for the forge's route. A served value may now name the provider's own names; a name its machine does not compose leaves the key out, and the plan says so.
185 lines
8.3 KiB
Go
185 lines
8.3 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
)
|
|
|
|
// A consumer of a provision that keeps its data stays bound where its data is (novox/hq ADR 0232).
|
|
//
|
|
// **What a resolution chooses is not where a consumer's data is.** Resolving answers "which provider
|
|
// would I pick now", from the seats' holders, the pins and what is assigned where, and every one of
|
|
// those can change under a consumer without anybody meaning to move it. For a resolver that is the
|
|
// point: any provider answers alike. For a database it is the consumer's whole state: on 2026-10-05
|
|
// one change to how a seat's holder answers (issue 258) re-bound five database consumers on one
|
|
// machine to the store on another, each was given a fresh, empty database there, and nothing warned
|
|
// for twenty hours (issue 273). Nothing was lost only because the old provider kept everything.
|
|
//
|
|
// So the mesh records where each such consumer was last sent (the store's `binding` table), and a
|
|
// resolution that would answer it from anywhere else keeps the recorded provider instead and says so.
|
|
// **Only a pin moves it**, because a pin is a person: the one act that says "answer this machine's
|
|
// consumers from there", taken knowing the data must go first.
|
|
|
|
// KeptBinding is one consumer this resolution would have moved, and did not.
|
|
type KeptBinding struct {
|
|
// Machine is where the consumer runs, and Consumer the module there that is bound.
|
|
Machine string
|
|
Consumer string
|
|
// Provision is what it is bound for.
|
|
Provision string
|
|
// Bound is the provider it was recorded at, and still is; Would is the one the resolution chose.
|
|
Bound Chosen
|
|
Would Chosen
|
|
}
|
|
|
|
// String is the condition's sentence: the move, where the data is, and the act that confirms it.
|
|
func (k KeptBinding) String() string {
|
|
return fmt.Sprintf("would move %s's %s from %s to %s — its data is on %s; kept there. "+
|
|
"`pin %s %s %s %s` to confirm a move (and move the data first)",
|
|
k.Consumer, k.Provision, k.Bound, k.Would, k.Bound, k.Machine, k.Provision, k.Would.Node, k.Would.Module)
|
|
}
|
|
|
|
// keepBound holds every need for a provision that keeps its consumers' data at the provider its
|
|
// consumer was bound to, where the resolution chose another.
|
|
//
|
|
// A need with no record is a binding being made, and is left as resolved: it is recorded when it is
|
|
// first sent. A pin naming the provider the resolution chose is a person moving it, and is left too.
|
|
// Otherwise the recorded provider answers, if it still provides the provision — beside the consumer,
|
|
// or offered from elsewhere — and the move is returned as kept. **One that no longer does is refused,
|
|
// never answered by the provider chosen**: answering it there is exactly the silent move this exists
|
|
// to stop, and the consumer's data is still wherever it was.
|
|
func keepBound(needs []Needed, catalogue map[string]Manifest, node Node, world World,
|
|
keeps map[string]bool, here func(string) bool) ([]Needed, []KeptBinding, []string) {
|
|
var kept []KeptBinding
|
|
var problems []string
|
|
refused := map[[2]string]bool{}
|
|
out := make([]Needed, 0, len(needs))
|
|
for _, n := range needs {
|
|
if n.ByRecord || !keeps[n.Name] {
|
|
out = append(out, n)
|
|
continue
|
|
}
|
|
n.KeepsData = true
|
|
bound, recorded := world.Bound[n.For][n.Name]
|
|
chose := Chosen{Node: n.From, Module: n.Module}
|
|
if !recorded || sameProvider(bound, chose) {
|
|
out = append(out, n)
|
|
continue
|
|
}
|
|
if pin, pinned := world.Pinned[n.Name]; pinned && pin.matches(Provider{Node: chose.Node, Module: chose.Module}) {
|
|
out = append(out, n)
|
|
continue
|
|
}
|
|
held, ok, why := boundNeed(n, bound, catalogue, node, world, here)
|
|
if !ok {
|
|
if key := [2]string{n.For, n.Name}; !refused[key] {
|
|
refused[key] = true
|
|
problems = append(problems, fmt.Sprintf(
|
|
"%s on %s is bound to %s for %q, which keeps its data, and %s — it would move to %s, "+
|
|
"which holds none of it. Move its data and say so with `pin %s %s %s %s`, or give "+
|
|
"%s back what it provided",
|
|
n.For, node.Name, bound, n.Name, why, chose, node.Name, n.Name, chose.Node, chose.Module,
|
|
bound.Node))
|
|
}
|
|
continue
|
|
}
|
|
out = append(out, held)
|
|
kept = append(kept, KeptBinding{Machine: node.Name, Consumer: n.For, Provision: n.Name, Bound: bound, Would: chose})
|
|
}
|
|
return out, kept, problems
|
|
}
|
|
|
|
// sameProvider is whether a recorded provider is the one chosen. A record naming no module (none
|
|
// is written without one, but a person's hand might) matches any module on its node.
|
|
func sameProvider(bound, chose Chosen) bool {
|
|
return bound.Node == chose.Node && (bound.Module == "" || bound.Module == chose.Module)
|
|
}
|
|
|
|
// boundNeed is the need answered by the recorded provider, or why it cannot be.
|
|
func boundNeed(n Needed, bound Chosen, catalogue map[string]Manifest, node Node, world World,
|
|
here func(string) bool) (Needed, bool, string) {
|
|
held := Needed{Name: n.Name, For: n.For, Local: n.Local, KeepsData: true}
|
|
if bound.Node == node.Name {
|
|
m, known := catalogue[bound.Module]
|
|
if !known || !here(bound.Module) || !providesAt(m, n.Name, ScopeMesh) {
|
|
return Needed{}, false, fmt.Sprintf("%s no longer runs on %s", bound.Module, node.Name)
|
|
}
|
|
at := node.At
|
|
if at == "" {
|
|
at = "127.0.0.1"
|
|
}
|
|
held.From, held.At, held.Module = node.Name, at, m.Module
|
|
held.Serves, held.SharedOwn, held.Identity = servedByOne(m, n.Name), sharedByOne(m, n.Name), m.IdentityBoundOf(n.Name)
|
|
return held, true, ""
|
|
}
|
|
matching := bound.among(world.Offered[n.Name])
|
|
if len(matching) != 1 {
|
|
return Needed{}, false, fmt.Sprintf("%s no longer provides it", bound)
|
|
}
|
|
p := matching[0]
|
|
if node.At == "" || p.At == "" {
|
|
return Needed{}, false, fmt.Sprintf("%s and %s are not both on the private network", node.Name, p.Node)
|
|
}
|
|
identity := DefaultIdentityBound
|
|
if pm, known := catalogue[p.Module]; known {
|
|
held.SharedOwn, _ = pm.SharedCredentialOf(n.Name)
|
|
identity = pm.IdentityBoundOf(n.Name)
|
|
}
|
|
held.From, held.At, held.Module, held.Serves, held.Identity = p.Node, p.At, p.Module, p.Serves, identity
|
|
held.LeftOut = p.LeftOut
|
|
return held, true, ""
|
|
}
|
|
|
|
// Unbound is one consumer that still asks for a provision and whose own resolution binds it to
|
|
// another provider than the one a pair credential on record was made with (novox/hq issue 274).
|
|
//
|
|
// **A provider is granted exactly the consumers bound to it.** The credential from the old provider
|
|
// stays on record — it is the key to a login that provider keeps, disabled, with the consumer's data,
|
|
// until a person deletes it with `cleanup delete` (ADR 0230), and a pin back must find it — but it is
|
|
// no longer granted, so the provider stops being asked for it and retires it. Said on every plan and
|
|
// push of the provider, so a credential the mesh keeps and does not use is never kept silently.
|
|
type Unbound struct {
|
|
// Provision is what was required, Provider the machine the credential on record is from.
|
|
Provision string `json:"provision"`
|
|
Provider string `json:"provider"`
|
|
// Consumer is the machine, Module the module on it that requires it, Local the credential's
|
|
// name inside it where it keeps several (ADR 0094).
|
|
Consumer string `json:"consumer"`
|
|
Module string `json:"module"`
|
|
Local string `json:"local,omitempty"`
|
|
// BoundTo is every provider the consumer's resolution binds this credential to now; empty when
|
|
// it binds it nowhere — the module asks for the provision under other local names.
|
|
BoundTo []string `json:"bound_to,omitempty"`
|
|
}
|
|
|
|
func (u Unbound) String() string {
|
|
who := u.Module
|
|
if u.Local != "" {
|
|
who += " (as " + u.Local + ")"
|
|
}
|
|
now := "is bound to no provider under that name"
|
|
if len(u.BoundTo) > 0 {
|
|
now = "is bound to " + strings.Join(u.BoundTo, ", ")
|
|
}
|
|
return fmt.Sprintf("%s on %s %s for %s, not to %s — %s no longer grants it, so it retires that "+
|
|
"login and keeps its data until `cleanup delete` (ADR 0230); the credential from %s stays on "+
|
|
"record while that login does", who, u.Consumer, now, u.Provision, u.Provider, u.Provider, u.Provider)
|
|
}
|
|
|
|
// BindsFrom is the providers this resolution binds a pair credential's need to — the provision, the
|
|
// module that requires it and the credential's local name — answered by a machine rather than by a
|
|
// record. None means this machine states no such binding.
|
|
func (r Resolution) BindsFrom(provision, module, local string) []string {
|
|
var from []string
|
|
for _, n := range r.Needs {
|
|
if n.ByRecord || n.Name != provision || n.For != module || n.Local != local {
|
|
continue
|
|
}
|
|
if !slices.Contains(from, n.From) {
|
|
from = append(from, n.From)
|
|
}
|
|
}
|
|
return from
|
|
}
|