Genesis makes the root secrets, the operator key, and installs the vault

The template raises the store with the password 'bootstrap' and the broker
with its image's default administrator, and the installer carried both into
the mesh as accepted secrets — permanent, and not secret (novox/hq issue 071).

Now the installer makes both credentials, once, at the paths the postgres and
lavinmq modules declare as their own secrets, rewrites the produced bundle to
use them (the store reads its password from a file; the broker's default
account is given the new password by an action before anything dials it), and
writes the bundle at 0600 since it now carries them.

Before the first secret is accepted it makes the operator's sealing key beside
the bundle and gives the mesh the public half, so everything minted from there
is sealed to it too (ADR 0085, amended). Phase three adopts the broker as the
lavinmq module beside the store and installs mesh-vault as a foundation module;
the run ends by writing the operator-sealed export beside the key.
This commit is contained in:
2026-09-21 00:12:55 +02:00
parent 1f483fcacd
commit ee0c8b856e
6 changed files with 630 additions and 13 deletions
+71 -4
View File
@@ -48,6 +48,7 @@ const (
StepApply Step = "apply"
StepVerify Step = "verify"
StepEnrol Step = "enrol"
StepOperator Step = "operator"
StepRegistry Step = "registry"
StepPublish Step = "publish"
StepControlPlane Step = "control-plane"
@@ -57,6 +58,8 @@ const (
StepSDK Step = "sdk"
StepBase Step = "base"
StepStore Step = "store"
StepBroker Step = "broker"
StepVault Step = "vault"
StepCatalogue Step = "catalogue"
StepNetwork Step = "network"
StepFilter Step = "filter"
@@ -76,12 +79,12 @@ const (
// mesh made, out of a repository and a commit it can name, and can therefore make again.
var Steps = []Step{
StepPreflight, StepLoad, StepBuild, StepBundle, StepApply, StepVerify,
StepEnrol, StepRegistry, StepPublish, StepControlPlane, StepRetire, StepBuilder,
StepEnrol, StepOperator, StepRegistry, StepPublish, StepControlPlane, StepRetire, StepBuilder,
StepPackages, StepSDK,
// 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,
StepBase, StepStore, StepBroker, StepVault, StepCatalogue, StepNetwork, StepFilter, StepExtras,
}
// Error is a failure, named by the step it happened in.
@@ -201,8 +204,13 @@ type Deps struct {
// Result is what the bootstrap did, in the shape `--json` prints.
type Result struct {
System string `json:"system"`
DryRun bool `json:"dry-run,omitempty"`
// OperatorKey is where the operator's private key was written; OperatorKeyMade whether this
// run made it. RootExport is where the operator-sealed export landed.
OperatorKey string
OperatorKeyMade bool
RootExport string
System string `json:"system"`
DryRun bool `json:"dry-run,omitempty"`
// Image is what THIS MACHINE'S RUNTIME holds the control plane as, read back from it — and
// what the produced bundle names it by.
@@ -380,6 +388,31 @@ func Run(ctx context.Context, o Options, d Deps, say func(string)) (Result, erro
}
result.BundleWas, result.BundlePlaces, result.Bundle = rewritten.Was, rewritten.Places, o.Out
result.Temporary = rewritten.TempName
// The mesh's root credentials: made here, never the template's (novox/hq issue 071).
creds, err := RootSecrets(o.DryRun)
if err != nil {
return result, failed(StepBundle, err)
}
root, err := RewriteRoot(&rewritten, creds)
if err != nil {
return result, failed(StepBundle, err)
}
for _, c := range []struct {
what, path string
made bool
}{{"store superuser", StoreSuperuserFile, creds.StoreMade}, {"broker admin", BrokerAdminFile, creds.BrokerMade}} {
switch {
case c.made && o.DryRun:
say(fmt.Sprintf(" %-17s would be made and kept at %s (0600)", c.what, c.path))
case c.made:
say(fmt.Sprintf(" %-17s made, kept at %s (0600)", c.what, c.path))
default:
say(fmt.Sprintf(" %-17s already at %s — kept", c.what, c.path))
}
}
say(fmt.Sprintf(" credentials the template's bootstrap and guest are gone: %d store and %d broker "+
"connection(s) rewritten, the store reads its password from a file, the broker's admin is changed once it answers",
root.StoreURLs, root.BrokerURLs))
if rewritten.Renamed {
say(fmt.Sprintf(" control plane %s, renamed from %s",
rewritten.TempName, rewritten.WasCalled))
@@ -509,6 +542,18 @@ func Run(ctx context.Context, o Options, d Deps, say func(string)) (Result, erro
return result, failed(StepEnrol, err)
}
// ---- operator — the key the mesh's root secrets are also sealed to, held by a person ------
//
// Before anything is accepted into the mesh: the store's and broker's credentials go in during
// the control-plane step, and they must be sealed to this key as well as to the node, or they
// are as unrecoverable as the constants they replaced (novox/hq ADR 0085, amended).
say("operator — a key the mesh seals its root secrets to, held by a person and never by the mesh")
operator, err := MakeOperatorKey(ctx, o, temporary, say)
result.OperatorKey, result.OperatorKeyMade = operator.Path, operator.Made
if err != nil {
return result, failed(StepOperator, err)
}
// ---- 7. registry ----------------------------------------------------------------------
say("registry — somewhere for this mesh to keep its own images")
registry, err := InstallRegistry(ctx, o, d, temporary, say)
@@ -598,6 +643,20 @@ func Run(ctx context.Context, o Options, d Deps, say func(string)) (Result, erro
return result, failed(StepStore, err)
}
// ---- 14b. broker ----------------------------------------------------------------------
say("broker — the foundation's broker, adopted as the lavinmq module: one bus, not two")
if err := InstallBroker(ctx, o, permanentControl, rewritten.Declaration, say); err != nil {
return result, failed(StepBroker, err)
}
// ---- 14c. vault -----------------------------------------------------------------------
// A foundation module (novox/hq ADR 0085, amended): it keeps every operator-sealed secret on
// its own disk, outside the store, from the first push that carries one.
say("vault — the mesh's secrets get an owner, and their operator-sealed copies a place to live")
if err := InstallVault(ctx, o, permanentControl, say); err != nil {
return result, failed(StepVault, 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 {
@@ -622,6 +681,14 @@ func Run(ctx context.Context, o Options, d Deps, say func(string)) (Result, erro
return result, failed(StepExtras, err)
}
// ---- export — what the operator keeps beside the key ---------------------------------
say("export — every root secret, sealed to the operator key, written beside it")
exported, err := ExportRootSecrets(ctx, o, permanentControl, say)
result.RootExport = exported
if 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.")