Refuse a declaration for the other mode, or older than the mesh's last, and say what an apply would change first
An operator ran `mesh-host reconcile` on an adopted control-node with twelve modules assigned. It applied the bundle the host carries — the genesis declaration, foundation only, converged: recreated the store, failed on the broker's held port, wrote the converged base filter and started its service, and stopped at the first failing action. The filter closed the machine for forty-five minutes. The host reported the node adopted in every report, the declaration said converged, and nothing compared the two; nothing was printed before acting (hq issue 104). The host now records the node's mode — from every declaration the mesh sends, and at genesis from what the operator said — and refuses, at the point of application, a declaration that says the other mode, naming both and the act that changes it. Only a declaration the link delivers, signed, changes the mode: that is how `converge` and `adopt` arrive, so the flip still works and nothing else can do it. Genesis marks the bundle consumed, with the digest of what it applied, so `reconcile` holds a node the mesh has spoken to against what the mesh last said and never the bundle, and refuses the carried bytes when they are not what genesis applied. A file is refused when it is not what the mesh last said: a declaration carries no sequence and no issued-at, so the host cannot tell older from newer, and says so. Both commands print what they would change — a hold, a removal, an action named as one — before touching anything, and --dry-run is that list and nothing more.
This commit is contained in:
@@ -80,6 +80,25 @@ func SaveDeclared(path string, d Declared) error {
|
||||
return os.Rename(tmp.Name(), path)
|
||||
}
|
||||
|
||||
// ReadDeclared reads what was kept without proving it is the mesh's.
|
||||
//
|
||||
// For naming and comparing only — which declaration this node was last told, and what mode it
|
||||
// said — never for applying. A declaration to apply goes through LoadDeclared, which verifies.
|
||||
func ReadDeclared(path string) (Declared, error) {
|
||||
raw, err := os.ReadFile(path)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return Declared{}, ErrNothingDeclared
|
||||
}
|
||||
if err != nil {
|
||||
return Declared{}, fmt.Errorf("this node was told something and cannot read it back: %w", err)
|
||||
}
|
||||
var d Declared
|
||||
if err := json.Unmarshal(raw, &d); err != nil {
|
||||
return Declared{}, fmt.Errorf("what this node was told is unreadable at %s: %w", path, err)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// LoadDeclared reads it back and proves it is still the mesh's.
|
||||
//
|
||||
// Verified against the signing key this node holds, which came from its token. A declaration on
|
||||
|
||||
@@ -104,6 +104,34 @@ type State struct {
|
||||
// Firewall is the firewall found on this machine when it was first adopted, and whether the
|
||||
// mesh has since retired it (novox/hq ADR 0100). Nil on a node that was never adopted.
|
||||
Firewall *FoundFirewall `json:"firewall,omitempty"`
|
||||
|
||||
// Mode is the node's mode as this host last recorded it: adopted or converged (novox/hq ADR
|
||||
// 0100). Empty on a machine nothing has said a mode to yet. Recorded from every declaration
|
||||
// the mesh sends and at genesis from what the operator said, so a declaration that says the
|
||||
// other mode can be refused before it is applied (novox/hq issue 104).
|
||||
Mode string `json:"mode,omitempty"`
|
||||
|
||||
// Genesis is the bundle this host consumed raising the foundation, if it has. Once recorded,
|
||||
// the bundle carried in the binary is not applied again: what genesis applied was rewritten
|
||||
// for this machine, and the mesh has said more since (novox/hq issue 104).
|
||||
Genesis *Genesis `json:"genesis,omitempty"`
|
||||
}
|
||||
|
||||
// Modes a node can be in (novox/hq ADR 0100).
|
||||
const (
|
||||
ModeAdopted = "adopted"
|
||||
ModeConverged = "converged"
|
||||
)
|
||||
|
||||
// Genesis is the bundle a host consumed raising this machine's foundation.
|
||||
type Genesis struct {
|
||||
// Digest is sha256 of the exact bytes applied.
|
||||
Digest string `json:"digest"`
|
||||
At time.Time `json:"at"`
|
||||
// Rewritten is true when the bytes applied were the carried bundle rewritten for this
|
||||
// machine — its foundation ports, root credentials, and adoption — so the bundle the binary
|
||||
// carries is not what was applied.
|
||||
Rewritten bool `json:"rewritten,omitempty"`
|
||||
}
|
||||
|
||||
// FoundFirewall is what the host found filtering this machine, and what it did about it.
|
||||
|
||||
Reference in New Issue
Block a user