Roll a core build back by a witness that is not the new build (hq to-be 45 Phase 4)

The launcher trusted a counter only a by-hand reconcile ever cleared and a
known-good nothing in the daemon wrote, so no machine could roll its host back;
the controller and the node tools were replaced in place with nothing kept.

- The launcher runs a delivered host that is not known-good on trial: one that
  crashes, stops for nothing, or does not report within ten minutes goes back
  to known-good, once per version, recorded in rolled-back. The host proves
  itself when the mesh takes a report under its own build, says every standing
  verdict on its reports, never stands aside for a rolled-back version, and
  restarts its service once when its launcher was replaced on disk.
- The engine keeps the controller's and the node tools' previous build beside
  the new one and judges the new one: the lease taken by the controller it
  started (read-only direct get of mesh-controller_lease/holder), or this
  machine's runtime answering $SRV.PING.node-tools.<node>, within sixty seconds
  of time it could ask. Not healthy: the previous restored, once, said. Proved:
  the previous deleted. A build declared not-reversible is never rolled back.
- Retire never removes a version newer than the running one.
This commit is contained in:
jochen
2026-10-06 18:23:56 +02:00
parent 8d853791b2
commit 0c405b70cc
23 changed files with 2636 additions and 115 deletions
+26
View File
@@ -590,6 +590,19 @@ type Process struct {
// For a process that stays up; a step or a scheduled run is not running a moment later by
// design, so there is nothing to hand over to.
Replaces []string `json:"replaces,omitempty"`
// Witness is how the node-engine judges a new build of this process, and restores the build
// before it when the new one is not healthy in bound (novox/hq to-be 45 §8): "lease" — the
// controller this machine started holds the controller's lease; "ping" — this machine's runtime
// answers the services protocol's PING; "none". Absent is the default for the process's name:
// the mesh's two core processes are judged, nothing else is. For a process that stays up.
Witness string `json:"witness,omitempty"`
// NotReversible says why this build may not be rolled back, when it may not: the build before it
// would run against what this one changes — a migration it runs that the older build cannot read
// (to-be 45 §8, rule 8). A build so declared that is not healthy in bound is left running and said
// as urgent; the build before it is never started against the newer data.
NotReversible string `json:"not-reversible,omitempty"`
}
func (d *Process) Identity() string { return d.ID }
@@ -637,6 +650,19 @@ func (d *Process) validate(where string, _ bool) []string {
if len(d.Run) == 0 {
problems = append(problems, where+": a process needs to say what to run")
}
switch d.Witness {
case "", "lease", "ping", "none":
default:
problems = append(problems, fmt.Sprintf("%s: witness %q is not one this host keeps: lease, ping or none",
where, d.Witness))
}
if d.Witness != "" && d.Witness != "none" && (d.RunOnce || d.Schedule != "") {
problems = append(problems, where+": a witness judges a process that stays up; a step or a "+
"scheduled run is not running between its runs")
}
if strings.ContainsAny(d.NotReversible, "\n\r") {
problems = append(problems, where+": not-reversible is one line")
}
for _, part := range d.Run {
if part == "" {
problems = append(problems, where+": a process command has an empty element")