Files
mesh-host/cmd/mesh-bootstrap/main.go
T
jschoubben b82ab95f74 mesh-bootstrap: the first-node procedure, as a program rather than a test
The only complete written-down copy of how a mesh is stood up was an integration
test in the lab. That is why every bootstrap gap kept being found late: an install
procedure that lives as a test fixture is exercised by whoever writes tests, never
by whoever installs. This is that procedure.

A separate binary, not a mesh-host subcommand. mesh-host says of itself that it
connects to nothing and listens on nothing and that what it applies comes from a
file, and that sentence is what makes an always-running root daemon auditable. An
installer loads images and interrogates a control plane. Same tier, different
program.

The control plane's image is carried, not built and not fetched. The forge that
holds its source runs on the mesh, so a bootstrap that had to fetch it would need
a mesh in order to raise one. Embedding breaks that cycle the way the carried
bundle breaks "copy it onto a machine and run it". The image id is read out of the
saved tar before the runtime is asked anything, which is what makes the load
idempotent: the installer can ask whether the machine already holds exactly this.

Five steps, each idempotent and each saying whether it found or changed something,
because this is run over and over by somebody getting a machine working. It stops
at a running substrate with a control plane that replies — enrolment, the module
catalogue and assignment are the next stage and are deliberately absent.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-10 23:17:30 +02:00

193 lines
6.9 KiB
Go

// Command mesh-bootstrap brings a mesh into existence on a bare machine.
//
// Tier 0, beside `mesh-host` and not inside it. Bootstrapping is done by hand and it changes a
// machine, which is what tier 0 is (novox/hq 03-DESIGN/01-to-be/05-the-node-host.md) — but
// `mesh-host` states of itself that it connects to nothing and listens on nothing and that what it
// applies comes from a file, and that is the whole reason an always-running root daemon can be
// audited by reading one page. An installer that loads images and interrogates a control plane
// cannot be folded into it without making that sentence false. Same tier, same repository,
// different program.
//
// What it does not do is enrol this machine, register modules or assign them. It stops at a
// running substrate with a control plane that answers, which is a mesh of one node.
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"net"
"os"
"os/signal"
"syscall"
"time"
"github.com/novox/mesh-host/internal/apply"
"github.com/novox/mesh-host/internal/bootstrap"
"github.com/novox/mesh-host/internal/store"
)
// version is stamped at build time. Unset in a development build, and said so rather than
// defaulted to something that looks like a release.
var version = "development build"
const (
defaultTemplate = "substrate.lock"
defaultOut = "/var/lib/mesh-host/substrate.lock"
)
const usage = `mesh-bootstrap — make a bare machine into a mesh
bootstrap preflight, load, bundle, apply, verify (the default)
version
--bundle the substrate template to build this machine's bundle from
(default ` + defaultTemplate + `)
--out where the produced bundle is written, for a person to read
(default ` + defaultOut + `)
--state where this node records what it has applied
(default ` + store.DefaultPath + `)
--system which operating system this is; by default it is asked
--timeout how long any single probe may take (default 30s)
--wait how long a thing that is merely starting is given (default 3m)
--dry-run everything that does not change the machine
--json machine-readable output
It carries the control plane's image and applies a substrate. Every step is idempotent:
run it again after fixing whatever it named, and the steps that already succeeded say so.
`
func main() {
// Ctrl-C must stop the installer, not be swallowed by whatever it is waiting for — and it
// waits on pulls, on a runtime starting, and on a control plane opening its stores.
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
command, opts, jsonOut, err := parseArgs(os.Args[1:])
if err == nil {
err = run(ctx, command, opts, jsonOut)
}
if err != nil {
fmt.Fprintf(os.Stderr, "mesh-bootstrap: %v\n", err)
os.Exit(1)
}
}
// parseArgs takes an optional subcommand first, then its flags.
//
// Parsed in a loop for the reason `mesh-host` records: the standard library stops at the FIRST
// non-flag argument, so a flag sitting after one is silently dropped and the command exits zero
// having ignored what it was asked. That fault has been paid for twice in this repository and is
// not being paid for a third time.
func parseArgs(args []string) (string, bootstrap.Options, bool, error) {
opts := bootstrap.Options{
Template: defaultTemplate,
Out: defaultOut,
State: store.DefaultPath,
// Longer than the host's 10s: these probes reach a container runtime that may be busy
// pulling, and a probe that times out on a working machine is a false refusal.
Timeout: 30 * time.Second,
// A socket-activated runtime queued behind the network, and a control plane running its
// first `initdb`-shaped wait, are both minutes rather than seconds.
Wait: 3 * time.Minute,
}
var jsonOut bool
command := "bootstrap"
if len(args) > 0 && len(args[0]) > 0 && args[0][0] != '-' {
command = args[0]
args = args[1:]
}
set := newFlagSet(&opts, &jsonOut)
var positionals []string
rest := args
for {
if err := set.Parse(rest); err != nil {
return "", opts, false, err
}
rest = set.Args()
if len(rest) == 0 {
break
}
positionals = append(positionals, rest[0])
rest = rest[1:]
}
// Refused rather than ignored: a mistyped argument that changes nothing and reports success is
// worse than an error, and this program's whole job is to change a machine.
if len(positionals) > 0 {
return "", opts, false, fmt.Errorf(
"unexpected argument %q — try `mesh-bootstrap help`", positionals[0])
}
return command, opts, jsonOut, nil
}
func newFlagSet(opts *bootstrap.Options, jsonOut *bool) *flag.FlagSet {
set := flag.NewFlagSet("mesh-bootstrap", flag.ContinueOnError)
set.SetOutput(os.Stderr)
set.Usage = func() { fmt.Fprint(os.Stderr, usage) }
set.StringVar(&opts.Template, "bundle", opts.Template, "the substrate template to build from")
set.StringVar(&opts.Out, "out", opts.Out, "where the produced bundle is written")
set.StringVar(&opts.State, "state", opts.State, "where this node records what it has applied")
set.StringVar(&opts.System, "system", opts.System, "which operating system this is")
set.DurationVar(&opts.Timeout, "timeout", opts.Timeout, "how long any single probe may take")
set.DurationVar(&opts.Wait, "wait", opts.Wait, "how long something merely starting is given")
set.BoolVar(&opts.DryRun, "dry-run", false, "everything that does not change the machine")
set.BoolVar(jsonOut, "json", false, "machine-readable output")
return set
}
func run(ctx context.Context, command string, opts bootstrap.Options, jsonOut bool) error {
switch command {
case "bootstrap":
say := func(line string) {
if !jsonOut {
fmt.Println(line)
}
}
result, err := bootstrap.Run(ctx, opts, bootstrap.Deps{
Run: apply.ExecRunner,
Dial: dial,
}, say)
// Printed whichever way it went. What the installer got through before it stopped is on
// the machine either way, and a report that only exists on success describes a machine
// nobody has (novox/hq ADR 0018).
if jsonOut {
encoder := json.NewEncoder(os.Stdout)
encoder.SetIndent("", " ")
if encodeErr := encoder.Encode(result); encodeErr != nil && err == nil {
return encodeErr
}
}
return err
case "version":
fmt.Println(version)
return nil
case "help", "-h", "--help":
fmt.Fprint(os.Stderr, usage)
return nil
default:
return fmt.Errorf("unknown command %q — try `mesh-bootstrap help`", command)
}
}
// dial answers whether a TCP address responds.
//
// A connection rather than a ping or a name lookup: what has to work is a pull, and a pull opens a
// connection to exactly this address. A machine whose DNS resolves and whose route is missing
// passes a lookup and fails the thing that matters.
func dial(ctx context.Context, address string) error {
var dialer net.Dialer
conn, err := dialer.DialContext(ctx, "tcp", address)
if err != nil {
return err
}
return conn.Close()
}