The installer goes as far as it can, and asks where a human must choose

Twelve steps made a mesh that RUNS and then said "what remains is somebody
else's". The seven things that turn it into a mesh that WORKS — the shared base,
a database provider, the catalogue, the private network, the packet filter — were
typed afterwards, which is how they went missing for weeks without anything
complaining.

Six more steps now: base, store, catalogue, network, filter, extras. Everything
in them is module add, build, assign and push — the same verbs a person types,
through the same commands, so the installer and an operator remain one act.

Where a human must choose, the installer asks. A choice resolves in the order a
person expects: the flag wins; a lone option answers itself ALOUD, because "it
chose for me" and "there was nothing to choose" read identically afterwards
unless one speaks; a terminal is asked; a default fills in; and a required
choice nothing answered refuses naming its flag — a guessed packet filter is a
machine somebody else configured. The filter is required, so the question is
which, not whether. A run without a terminal (the lab, --json) is never left
waiting on a prompt nobody will answer.

Placement is part of the network step, not a separate act — a lesson paid for:
the module installed, the names file was written with no names in it, and
everything reported success because nobody had said where the machine IS. The
hub endpoint derives from the broker address when unsaid: the host other
machines dial is one fact, not two that drift.

Extras fail the run rather than soft-fail: somebody asked for them by name, and
a mesh reporting success minus one thing is reporting the wrong thing.

Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
2026-09-15 21:56:23 +02:00
parent 7a223464e5
commit 21474b0144
7 changed files with 550 additions and 4 deletions
+68 -3
View File
@@ -53,6 +53,12 @@ const (
StepControlPlane Step = "control-plane"
StepRetire Step = "retire"
StepBuilder Step = "builder"
StepBase Step = "base"
StepStore Step = "store"
StepCatalogue Step = "catalogue"
StepNetwork Step = "network"
StepFilter Step = "filter"
StepExtras Step = "extras"
)
// Steps in the order they happen, so a failure can say "step 2 of 11".
@@ -69,6 +75,10 @@ const (
var Steps = []Step{
StepPreflight, StepLoad, StepBuild, StepBundle, StepApply, StepVerify,
StepEnrol, StepRegistry, StepPublish, StepControlPlane, StepRetire, StepBuilder,
// Phase two. The twelve above make a mesh that RUNS; these make one that WORKS — able to
// build, to say what it holds, on its network, filtering. They used to be things somebody
// typed afterwards, which is how they went missing without anything complaining.
StepBase, StepStore, StepCatalogue, StepNetwork, StepFilter, StepExtras,
}
// Error is a failure, named by the step it happened in.
@@ -145,6 +155,24 @@ type Options struct {
// HostInBackground starts the host unsupervised instead, which is what the lab does and what no
// real machine should do — it does not survive a reboot.
HostInBackground bool
// ---- phase two — a mesh that runs becomes a mesh that works ----
// ToolsSource is where the shared base is built from. Everything with code of its own
// compiles against it, so it is the first thing the mesh builds for itself.
ToolsSource Source
// CatalogSource is where the catalogue REPOSITORY is, for building its modules. The catalogue
// CHECKOUT (Catalogue, above) says what a module is; this is where a builder clones it.
CatalogSource Source
// Site is where this machine sits, for the private network's placement.
Site string
// Answers are the choices, answered by flag: name → answer. What a terminal would be asked.
Answers map[string]string
// Prompt asks a person one choice. Nil is an unattended run: flags and defaults answer, and a
// required choice nothing answered is a refusal rather than a guess.
Prompt func(Choice) (string, error)
// Extras are catalogue modules beyond the floor, asked for by name.
Extras []string
}
// pivots reports whether this run goes past the substrate.
@@ -535,9 +563,46 @@ func Run(ctx context.Context, o Options, d Deps, say func(string)) (Result, erro
return result, failed(StepBuilder, err)
}
say("\nthis machine is a mesh of one node, and the control plane it runs is a module " +
"pinned to an image its own registry serves.")
say("it holds a builder, so it can make the rest of the catalogue rather than be handed it.")
// ---- 13. base -------------------------------------------------------------------------
say("base — the shared toolchain and runtime everything with code stands on")
if err := BuildBase(ctx, o, permanentControl, say); err != nil {
return result, failed(StepBase, err)
}
// ---- 14. store ------------------------------------------------------------------------
// A database PROVIDER. The substrate's store is the control plane's own memory and offers
// nothing to anything; the first thing that wants a database is the catalogue, next.
say("store — a database provider, which the substrate's own store is not")
if err := InstallFromCatalogue(ctx, o, permanentControl, "postgres", say); err != nil {
return result, failed(StepStore, err)
}
// ---- 15. catalogue --------------------------------------------------------------------
say("catalogue — the module graph: what is held, what a change reaches, what to rebuild")
if err := InstallFromCatalogue(ctx, o, permanentControl, "mesh-catalog", say); err != nil {
return result, failed(StepCatalogue, err)
}
// ---- 16. network ----------------------------------------------------------------------
say("network — the private network, and this machine's name on it")
if err := PlaceOnTheNetwork(ctx, o, permanentControl, rewritten.BrokerAddress, say); err != nil {
return result, failed(StepNetwork, err)
}
// ---- 17. filter -----------------------------------------------------------------------
say("filter — required, so the question is which, not whether")
if err := ChooseAndInstallFilter(ctx, o, permanentControl, say); err != nil {
return result, failed(StepFilter, err)
}
// ---- 18. extras -----------------------------------------------------------------------
say("extras — beyond the floor, if asked")
if err := InstallExtras(ctx, o, permanentControl, say); err != nil {
return result, failed(StepExtras, err)
}
say("\nthis machine is a mesh of one node: it builds its own software, holds its graph, " +
"sits on its private network, and filters what modules declared.")
say("what remains is somebody else's: adding nodes, and assigning what they should run.")
return result, nil