node public-domain: asking what it is no longer takes it away
`node public-domain <name>` cleared the domain. It reads like a question — it is exactly what anybody types to find out what the answer is — and it silently took every routed name the node had. There is no output that makes up for that: by the time it prints, the fact is gone, and the mesh cannot tell a person what a domain used to be. The bare form reports now. Clearing is still a real thing to want — a machine that stops facing the outside composes no names, and lab-versus-production is this one setting (novox/hq ADR 0056) — so it keeps a way to be said, by name: `--clear`. A domain and `--clear` together are refused rather than one of them silently winning. The other `node` subcommands were checked. `add`, `list` and `show` write nothing they were not asked to, so there is nothing to make consistent with. `overlay place <node>` with no flags has the same shape — it clears the endpoint, the site and the hub flag — and is deliberately left alone here. It is a verb rather than a question and every caller passes flags, so the fix is a different judgement and belongs in its own change. The usage text gains the three forms, and `module forget`'s new flag, neither of which it named before. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
+73
-21
@@ -22,7 +22,7 @@ import (
|
||||
|
||||
func nodeCommand(ctx context.Context, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("node add <name>, node list, node show <name>, or node public-domain <name> [domain]")
|
||||
return errors.New("node add <name>, node list, node show <name>, or " + publicDomainUsage)
|
||||
}
|
||||
open, err := openStores(ctx)
|
||||
if err != nil {
|
||||
@@ -66,32 +66,84 @@ func nodeCommand(ctx context.Context, args []string) error {
|
||||
return nil
|
||||
|
||||
case "public-domain":
|
||||
// The domain this node composes its routed names under (novox/hq ADR 0056). Given a domain,
|
||||
// it is set; given nothing, it is cleared — a node that stops facing the outside composes no
|
||||
// names. Lab-versus-production is this one setting and nothing else (see the ADR).
|
||||
if len(args) < 2 || len(args) > 3 {
|
||||
return errors.New(
|
||||
"node public-domain <name> [domain] — a domain sets it, nothing clears it")
|
||||
}
|
||||
domain := ""
|
||||
if len(args) == 3 {
|
||||
domain = args[2]
|
||||
}
|
||||
if err := inv.SetPublicDomain(ctx, args[1], domain); err != nil {
|
||||
return err
|
||||
}
|
||||
if domain == "" {
|
||||
fmt.Printf("%s has no public domain, so it composes no routed names\n", args[1])
|
||||
} else {
|
||||
fmt.Printf("%s composes its routed names under %s\n", args[1], domain)
|
||||
}
|
||||
return nil
|
||||
// The domain this node composes its routed names under (novox/hq ADR 0056).
|
||||
//
|
||||
// **The form with no argument reports; clearing is asked for by name.** It used to clear —
|
||||
// so `node public-domain anchor`, which reads like a question and is what anybody types to
|
||||
// find out what the answer is, silently took every routed name the node had. A read-shaped
|
||||
// invocation must never be a destructive write: there is no output that makes up for it,
|
||||
// because the damage is already done by the time it prints.
|
||||
return publicDomain(ctx, inv, args[1:])
|
||||
|
||||
default:
|
||||
return fmt.Errorf("node has no %q; it has add, list, show and public-domain", args[0])
|
||||
}
|
||||
}
|
||||
|
||||
// publicDomainUsage is the one description of the three forms, so a refusal and the help agree.
|
||||
const publicDomainUsage = "node public-domain <name> — what it is now; " +
|
||||
"<name> <domain> to set it; <name> --clear to take it away"
|
||||
|
||||
// publicDomain reads, sets or clears the domain a node composes its routed names under.
|
||||
//
|
||||
// Three forms, and the destructive one is the only one that has to be asked for. Clearing is a
|
||||
// real thing to want — a machine that stops facing the outside composes no names, and
|
||||
// lab-versus-production is this one setting (novox/hq ADR 0056) — so it keeps a way to say it.
|
||||
// What it does not keep is being the thing that happens when nothing was said at all.
|
||||
func publicDomain(ctx context.Context, inv *inventory.Inventory, args []string) error {
|
||||
set := flag.NewFlagSet("node public-domain", flag.ContinueOnError)
|
||||
clear := set.Bool("clear", false, "take the domain away; it composes no routed names after")
|
||||
positionals, err := parseAround(set, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(positionals) == 0 || len(positionals) > 2 {
|
||||
return errors.New(publicDomainUsage)
|
||||
}
|
||||
node := positionals[0]
|
||||
|
||||
switch {
|
||||
case *clear && len(positionals) == 2:
|
||||
// Both, which cannot be meant. Refused rather than one of them silently winning.
|
||||
return fmt.Errorf("give %s a domain or --clear, not both: %q and --clear say opposite "+
|
||||
"things and the mesh will not choose between them", node, positionals[1])
|
||||
|
||||
case *clear:
|
||||
if err := inv.SetPublicDomain(ctx, node, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s has no public domain, so it composes no routed names\n", node)
|
||||
fmt.Printf(" run `push %s` to take them off it\n", node)
|
||||
return nil
|
||||
|
||||
case len(positionals) == 2:
|
||||
if err := inv.SetPublicDomain(ctx, node, positionals[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s composes its routed names under %s\n", node, positionals[1])
|
||||
fmt.Printf(" run `push %s` to send it\n", node)
|
||||
return nil
|
||||
|
||||
default:
|
||||
// Asked, so answered. NodeByName first, so a name the mesh has never heard of is a refusal
|
||||
// rather than "it has no public domain", which is true of that name and says nothing.
|
||||
if _, err := inv.NodeByName(ctx, node); err != nil {
|
||||
return err
|
||||
}
|
||||
domain, err := inv.PublicDomainOf(ctx, node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if domain == "" {
|
||||
fmt.Printf("%s has no public domain, so it composes no routed names\n", node)
|
||||
fmt.Printf(" `node public-domain %s <domain>` gives it one\n", node)
|
||||
return nil
|
||||
}
|
||||
fmt.Printf("%s composes its routed names under %s\n", node, domain)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func tokenCommand(ctx context.Context, args []string) error {
|
||||
if len(args) == 0 || args[0] != "issue" {
|
||||
return errors.New("token issue --node <name>, or token issue --new <name>")
|
||||
|
||||
Reference in New Issue
Block a user