A carried peer is nameable, and the mesh answers for it (hq 112)

The tunnel the hub took over routes to machines the predecessor knows
by name and the mesh knew only by address — taking the resolver in that
state silences three machines at once. Now the operator states which
machine a carried address is (overlay name <address> <name>), the
statement rides tunnel_peer.named, and namesInTheMesh answers for named
not-yet-enrolled peers — one reading, so the hosts fact, a container's
hosts and the resolver cannot disagree. Enrolment verifies the word:
a machine enrolling under a named peer's key with a different name is
refused where the operator can read it, the stated name keeps the
carried address, and an enrolled peer's name is the node's — naming it
again refuses. The issue's rule holds: a name the predecessor answers
for keeps resolving until the machine behind it is a node.
This commit is contained in:
2026-09-26 20:09:42 +02:00
parent 50734095b8
commit 952092ccb3
5 changed files with 195 additions and 4 deletions
+37 -2
View File
@@ -48,7 +48,7 @@ func overlayRange(ctx context.Context, inv *inventory.Inventory) (string, error)
func overlayCommand(ctx context.Context, args []string) error {
if len(args) == 0 {
return errors.New("overlay place <node> [flags], or overlay show")
return errors.New("overlay place <node> [flags], overlay name <address> <name>, or overlay show")
}
// Answered before anything is opened. A message about which command to use should not need a
// database to say so, and needing one turns a redirect into a connection error.
@@ -69,12 +69,29 @@ func overlayCommand(ctx context.Context, args []string) error {
return overlayPlace(ctx, inv, args[1:])
case "show":
return overlayShow(ctx, open)
case "name":
return overlayName(ctx, inv, args[1:])
default:
return fmt.Errorf("overlay has no %q; it has place and show", args[0])
return fmt.Errorf("overlay has no %q; it has place, name and show", args[0])
}
}
// overlayName is the operator saying which machine a carried address is (novox/hq issue 112),
// so the mesh answers for its name until the machine enrols and verifies it.
func overlayName(ctx context.Context, inv *inventory.Inventory, args []string) error {
if len(args) != 2 {
return errors.New("overlay name <carried-address> <node-name>")
}
address, name := args[0], args[1]
if err := inv.NamePeer(ctx, address, name); err != nil {
return err
}
fmt.Printf("the peer at %s is %s until it enrols — the mesh answers for %s.<suffix> from the "+
"operator's word, and enrolment under this key must use this name\n", address, name, name)
return nil
}
func overlayPlace(ctx context.Context, inv *inventory.Inventory, args []string) error {
if len(args) == 0 {
return errors.New(
@@ -588,6 +605,24 @@ func namesInTheMesh(ctx context.Context, inv *inventory.Inventory,
for _, p := range places {
out[overlay.InternalName(p.Name)] = p.Address
}
// And the carried peers the operator has named (novox/hq issue 112): machines the
// predecessor's resolver answers for and the mesh routes to, known by name on the operator's
// word until they enrol — at which point enrolment verifies the name and the node's own
// entry takes over above. A name the predecessor answers for must keep resolving until the
// machine behind it is a node; without these, taking the resolver silences three machines.
carried, err := inv.CarriedPeers(ctx)
if err != nil {
return nil, err
}
for _, p := range carried {
if p.Named == "" || p.EnrolledAs != "" {
continue
}
if _, taken := out[overlay.InternalName(p.Named)]; taken {
continue // a node of the mesh owns the name; the stale statement loses
}
out[overlay.InternalName(p.Named)] = p.Address
}
return out, nil
}