Rename mesh-control -> mesh-controller, substrate -> foundation
One name per thing, per the HQ glossary: the module/container/image/binary/repo becomes mesh-controller, the seat the-controller, and the store+broker pair the foundation (embedded base bundles, default template and example lock renamed with their go:embed directives). No behaviour change — a pure vocabulary rename. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// secretCommand gives the mesh a value it must carry and could not have invented.
|
||||
//
|
||||
// **Every other secret in this mesh is one the mesh made** — generated, sealed to the machine that
|
||||
// will use it, and never readable again. That is right for something coming into existence, and
|
||||
// wrong for something that already exists: a database created last year has the password it was
|
||||
// created with, and generating a new one puts 32 random bytes where a working credential was.
|
||||
// The machine applies it, reports success, and whatever reads it fails to authenticate somewhere
|
||||
// else entirely — with the mesh insisting the secret was delivered, which it was.
|
||||
//
|
||||
// So this is the entry point for **adopting** something already running. The store has carried
|
||||
// the distinction since the beginning: a module secret records whether it was `made` or
|
||||
// `accepted`, and refuses to invent a replacement for the second. Nothing until now could write
|
||||
// one, so the only accepted secret in the mesh was the broker account issued to a build machine.
|
||||
//
|
||||
// The value is sealed on the way in and the plaintext discarded, exactly as a generated one is.
|
||||
// **The only difference between the two is where the value came from.**
|
||||
func secretCommand(ctx context.Context, args []string) error {
|
||||
if len(args) == 0 || args[0] != "accept" {
|
||||
return errors.New("secret accept <node> <module> <name> [--from <file>]")
|
||||
}
|
||||
rest, flags := split(args[1:])
|
||||
set := flag.NewFlagSet("secret accept", flag.ContinueOnError)
|
||||
from := set.String("from", "",
|
||||
"read the value from this file instead of asking (use - for standard input)")
|
||||
if err := set.Parse(flags); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(rest) != 3 {
|
||||
return errors.New("secret accept <node> <module> <name> [--from <file>]")
|
||||
}
|
||||
node, module, name := rest[0], rest[1], rest[2]
|
||||
|
||||
value, err := valueFor(node, module, name, *from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value = asSupplied(value)
|
||||
if value == "" {
|
||||
return errors.New("there is nothing to seal")
|
||||
}
|
||||
|
||||
open, err := openStores(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer open.Close()
|
||||
|
||||
if err := open.inventory.AcceptSecretForModule(ctx, node, module, name, value); err != nil {
|
||||
return err
|
||||
}
|
||||
// Not printed back, and there is nowhere it could be printed from: it is sealed to that
|
||||
// machine and the mesh cannot read it again.
|
||||
fmt.Printf("%s on %s now holds %q, sealed to that machine.\n", module, node, name)
|
||||
fmt.Printf(" the mesh cannot read it back, and will not replace it with one of its own\n")
|
||||
fmt.Printf(" run `push %s` to send it\n", node)
|
||||
return nil
|
||||
}
|
||||
|
||||
// split separates what this command is about from how it was asked.
|
||||
//
|
||||
// **Because the standard library stops parsing at the first non-flag argument.** With the
|
||||
// positionals first — which is the order that reads correctly — everything after them is left
|
||||
// sitting in the arguments, so `secret accept a b c --from -` arrives as five positionals and the
|
||||
// flag is never seen. The host's own parser carries the same note, and the fault it names is
|
||||
// worse than this one: there, a flag somebody passed was silently ignored and the command
|
||||
// succeeded anyway.
|
||||
func split(args []string) (positional, flags []string) {
|
||||
for i, arg := range args {
|
||||
if strings.HasPrefix(arg, "-") {
|
||||
return args[:i], args[i:]
|
||||
}
|
||||
}
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// asSupplied is the value with its line ending removed and nothing else.
|
||||
//
|
||||
// **A file has a trailing newline and a password does not**, so the ending goes — a credential
|
||||
// wrong by one byte fails in a way nobody connects to how it was supplied.
|
||||
//
|
||||
// **And only the ending.** Trimming both ends is the obvious thing and it is wrong: a password
|
||||
// chosen with a leading space is one the mesh would then deliver as a different password, silently,
|
||||
// with the operator certain they had supplied it correctly.
|
||||
func asSupplied(raw string) string {
|
||||
return strings.TrimRight(raw, "\r\n")
|
||||
}
|
||||
|
||||
// valueFor gets the secret without putting it somewhere it can be read afterwards.
|
||||
//
|
||||
// **Not an argument, and there is no flag that takes one.** A value on the command line is in the
|
||||
// shell's history, in the process list for as long as it runs, and in whatever collects either.
|
||||
// The paths here are a file the operator already has, or a prompt that does not echo — the same
|
||||
// two ways a model-access key is supplied (novox/hq ADR 0024).
|
||||
func valueFor(node, module, name, from string) (string, error) {
|
||||
switch {
|
||||
case from == "-":
|
||||
body, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(body), nil
|
||||
|
||||
case from != "":
|
||||
body, err := os.ReadFile(from)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(body), nil
|
||||
|
||||
default:
|
||||
// The same path a model-access key takes, and for the same reason: a value given as an
|
||||
// argument is in the shell's history and in the process list. Read from standard input,
|
||||
// echoed nowhere by this program.
|
||||
fmt.Fprintf(os.Stderr,
|
||||
"reading %s's %q for %s from standard input; it is not echoed anywhere\n",
|
||||
module, name, node)
|
||||
line, err := bufio.NewReader(os.Stdin).ReadString('\n')
|
||||
if err != nil && line == "" {
|
||||
return "", fmt.Errorf("nothing was given on standard input: %w", err)
|
||||
}
|
||||
return line, nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user