state

package
v1.230.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 14, 2026 License: MPL-2.0 Imports: 8 Imported by: 0

Documentation

Overview

============================================================================= NFTBan v1.73 - Installer State File I/O ============================================================================= SPDX-License-Identifier: MPL-2.0 SPDX-FileCopyrightText: Copyright (c) 2024-2026 Antonios Voulvoulis <contact@nftban.com> meta:name="installer-state-file" meta:type="lib" meta:owner="Antonios Voulvoulis <contact@nftban.com>" meta:created_date="2026-04-04" meta:description="State file struct, atomic write, read, transition persistence" meta:inventory.files="internal/installer/state/file.go" meta:inventory.binaries="" meta:inventory.env_vars="" meta:inventory.config_files="/var/lib/nftban/state/install_state" meta:inventory.systemd_units="" meta:inventory.network="" meta:inventory.privileges="root" =============================================================================

============================================================================= NFTBan v1.73 - Installer State Machine ============================================================================= SPDX-License-Identifier: MPL-2.0 SPDX-FileCopyrightText: Copyright (c) 2024-2026 Antonios Voulvoulis <contact@nftban.com> meta:name="installer-state-machine" meta:type="lib" meta:owner="Antonios Voulvoulis <contact@nftban.com>" meta:created_date="2026-04-04" meta:description="Install state enum, phase enum, exit codes, resume logic" meta:inventory.files="internal/installer/state/machine.go" meta:inventory.binaries="" meta:inventory.env_vars="" meta:inventory.config_files="" meta:inventory.systemd_units="" meta:inventory.network="" meta:inventory.privileges="none" =============================================================================

============================================================================= NFTBan v1.230.0 Gate 6R F2 — RECOVERY_CLASS ============================================================================= SPDX-License-Identifier: MPL-2.0 SPDX-FileCopyrightText: Copyright (c) 2024-2026 Antonios Voulvoulis <contact@nftban.com> meta:name="installer-recovery-class" meta:type="lib" meta:owner="Antonios Voulvoulis <contact@nftban.com>" meta:created_date="2026-09-13" meta:description="Declares, per install state, WHICH recovery mechanism has an established route to COMMITTED. Only states whose --repair resume actually re-establishes every fact COMMITTED requires may advertise --repair; the rest must instruct a full retry. Derived from ResumePhase, not from a second hand-maintained table, so a recovery instruction cannot drift away from what recovery actually does." meta:inventory.files="internal/installer/state/recovery.go" meta:inventory.binaries="" meta:inventory.env_vars="" meta:inventory.config_files="" meta:inventory.systemd_units="" meta:inventory.network="" meta:inventory.privileges="none" =============================================================================

Index

Constants

View Source
const (
	ExitCommitted      = 0
	ExitDegraded       = 1
	ExitFailed         = 2
	ExitAborted        = 3
	ExitFatal          = 4
	ExitRefused        = 5
	ExitIntentRequired = 6

	// PR-25 restore execution exit codes (contract §22).
	ExitRestoreExecuted           = 7
	ExitRestoreFailedExecution    = 8
	ExitRestoreDegraded           = 9
	ExitRestoreFailedVerification = 10
)

ExitCode is the process exit code contract for nftban-installer.

Contract (frozen):

0 = COMMITTED        — all phases passed, firewall running and verified
1 = DEGRADED         — firewall running but some validation checks failed
2 = FAILED           — a critical phase failed, firewall may not be running
3 = ABORTED          — conflicting firewalls detected, no --takeover flag
4 = FATAL            — unrecoverable error (binary not found, permission denied)
5 = REFUSED          — PR-24 restore policy engine: policy forbids restoration
6 = INTENT_REQUIRED  — PR-24 restore policy engine: operator must clarify intent

PR-25 (v1.100) — restore EXECUTION exit codes (contract §19.4 + §22):

7 = RESTORE_EXECUTED              — full success after PR-24 PROCEED
8 = RESTORE_FAILED_EXECUTION      — mid-flight failure; safety net retained
9 = RESTORE_DEGRADED              — completed with soft-fail warning
10 = RESTORE_FAILED_VERIFICATION  — hard-fail after mutation; safety net retained

All four are distinct from ExitCommitted=0 / ExitFatal=4 / ExitRefused=5 / ExitIntentRequired=6 per contract §19.4. They are also distinct from existing 1/2/3 codes to avoid mixing install-class and restore-class outcome semantics.

View Source
const DefaultStateDir = "/var/lib/nftban/state"

DefaultStateDir is the standard location for install state.

View Source
const LockFileName = "installer.lock"

LockFileName is the V125 R-2 installer concurrent-run lock file name. Lives alongside install_state so it shares the same state-dir lifecycle. Consumed by internal/installer/lock via LockFilePath().

View Source
const StateFileName = "install_state"

StateFileName is the install state file name.

Variables

This section is empty.

Functions

func IsApplyTerminal added in v1.100.4

func IsApplyTerminal(s InstallState) bool

IsApplyTerminal is a package-level alias for the (InstallState) IsApplyTerminal method, kept so consumers that hold the state as a plain value can call it symmetrically with the other helpers in this file.

func IsDeferredRebuild added in v1.230.0

func IsDeferredRebuild(s InstallState) bool

IsDeferredRebuild is a package-level alias for the (InstallState) method.

func IsRestoreExecuted added in v1.100.4

func IsRestoreExecuted(s InstallState) bool

IsRestoreExecuted is a package-level alias for the (InstallState) method.

func LockFilePath added in v1.125.0

func LockFilePath(stateDir string) string

LockFilePath returns the full path to the installer concurrent-run lock file given a state-dir. If stateDir is empty, DefaultStateDir is used — matches NewStateFile's empty-stateDir fallback so the two file paths always share a parent directory.

func ValidateRecoveryClass added in v1.230.0

func ValidateRecoveryClass(s InstallState, declared RecoveryClass) string

ValidateRecoveryClass reports why `declared` is wrong for `s`, or "" when it is right.

⛔ THIS IS THE ANTI-DRIFT CONTROL, AND IT IS ONLY REAL BECAUSE IT CAN REJECT. A guard that merely reads back what production computed proves nothing. This takes a class from the CALLER and checks it against the derivation, so a test can declare a DELIBERATELY WRONG classification and require the guard to catch it. If declaring REBUILD_REFUSED_BUSY as REPAIR ever passes, the control is decorative and the lab3 defect can reappear.

Types

type InstallState

type InstallState string

InstallState represents the current state of the installation process.

const (
	StateFilesInstalled   InstallState = "FILES_INSTALLED"
	StateDetectComplete   InstallState = "DETECT_COMPLETE"
	StatePrepareComplete  InstallState = "PREPARE_COMPLETE"
	StateSwitchComplete   InstallState = "SWITCH_COMPLETE"
	StateServicesComplete InstallState = "SERVICES_COMPLETE"
	// StateCommitted is the ONLY install-class state that means "this transaction
	// succeeded". ⛔ See the OWNER RULING at the top of this file before making any
	// rebuild disposition — DEFERRED_RUNTIME above all — sufficient for it.
	StateCommitted        InstallState = "COMMITTED"
	StateDegraded         InstallState = "DEGRADED"
	StateFailedSSH        InstallState = "FAILED_SSH_UNKNOWN"
	StateFailedAbort      InstallState = "FAILED_AUTHORITY_ABORT"
	StateFailedRender     InstallState = "FAILED_RENDER"
	StateFailedRebuild    InstallState = "FAILED_REBUILD"
	StateFailedNoFirewall InstallState = "FAILED_NO_FIREWALL"
	StateFailedTakeover   InstallState = "FAILED_TAKEOVER"

	// StateRebuildRefusedBusy — every rebuild attempt inside the installer's
	// deadline was REFUSED because another nft operation held the convergence
	// lock. Established from the shell's REFUSED result contract, never from
	// stderr text and never from an exit code.
	StateRebuildRefusedBusy InstallState = "REBUILD_REFUSED_BUSY"

	// StateRebuildNotExecuted — no result contract AND no execution witness.
	// The root-cause guard for a missing record: "no record" means one of two
	// opposite things, and this state is the one where execution was NOT
	// established. When the witness DOES prove execution, a missing record
	// stays FAILED_REBUILD and stays fatal.
	StateRebuildNotExecuted InstallState = "REBUILD_NOT_EXECUTED"

	// StateFailedPreflightDiskSpace (V125 R-5) is the terminal failure
	// produced when the disk-space preflight at the end of phaseDetect
	// determines that the state-dir's filesystem has insufficient free
	// space to proceed safely (default threshold 500 MB; operator-tunable
	// via NFTBAN_MIN_DISK_FREE_MB). Refusing here is strictly safer than
	// proceeding into phasePrepare's dnf/apt installs + file writes,
	// which would ENOSPC mid-install and leave the host in an
	// inconsistent state.
	//
	// IsApplyTerminal=true (an apply was attempted and a definitive
	// outcome was reached). IsFailed=true (apply did not succeed).
	// ExitCode falls through to ExitFailed via the default branch,
	// matching the pattern of the other StateFailed* values.
	// ResumePhase falls through to PhaseDetect via the default branch —
	// after the operator frees disk space, --repair re-runs from
	// detection (which includes the preflight, so a still-low-disk
	// host correctly re-refuses).
	StateFailedPreflightDiskSpace InstallState = "FAILED_PREFLIGHT_DISK_SPACE"

	// StateUninstallPlanning is the terminal state for v1.100 PR-22's
	// detect + dry-run plan orchestrator. The planner reaches this
	// state after classifying current authority, probing the optional
	// prior-authority record, and rendering the release plan — without
	// invoking any mutation phase. Later v1.100 PRs (PR-23/25) add the
	// mutation-carrying uninstall states; PR-22 deliberately ships only
	// the planning state so the scope-boundary block in plan output
	// remains literally true: no phase beyond Planning exists yet.
	StateUninstallPlanning InstallState = "UNINSTALL_PLANNING"

	// StateUninstallReleased is the terminal success state for v1.100
	// PR-23's uninstall mutation (authority release core). Reached
	// after:
	//   - kernel nftban tables flushed + deleted
	//   - nftband.service stopped, disabled, masked
	//   - end-state validation passed (no nftban authority remaining)
	//   - emergency SSH table cleanly removed
	//
	// IsApplyTerminal() returns true for this state so downstream
	// lifecycle consumers see it as a completed apply outcome.
	// ExitCode() maps to ExitCommitted (0) — the operator asked for
	// uninstall and got it. However, the uninstall-history
	// representation is intentionally SKIPPED for this state in PR-23
	// (Option A locked 2026-04-20): update-history.json cannot
	// truthfully represent uninstall success under its install-centric
	// schema, and the separate-schema work is explicitly deferred to a
	// later PR. writeHistory is gated on cfg.mode != "uninstall" in
	// main.go, so this state does NOT produce a history entry.
	StateUninstallReleased InstallState = "UNINSTALL_RELEASED"

	// StateUninstallFailedRelease is the terminal failure state for
	// PR-23 when mutation started but did not complete cleanly. The
	// emergency SSH table may still be present (over-permissive SSH
	// is the deliberate fallback — losing SSH on a failure is a worse
	// outcome than a temporary permissive rule). Operator must
	// investigate kernel + service state and either retry or manually
	// resolve. IsApplyTerminal() returns true; ExitCode() maps to
	// ExitFailed (2).
	StateUninstallFailedRelease InstallState = "UNINSTALL_FAILED_RELEASE"

	// PR-24 authority restoration policy-engine states.
	//
	// These are produced ONLY by the pure decision engine in
	// internal/installer/restore. The engine performs no kernel,
	// service, or filesystem mutation; these states therefore represent
	// a policy outcome, not an apply outcome.
	//
	// StateRestoreRefused is the terminal state produced when the
	// lattice returns REFUSE. It is terminal, NOT failed (refusal is
	// not failure — it is a correct policy outcome), and deliberately
	// excluded from IsApplyTerminal so it does not flow through
	// update-history.json under the install-centric schema. ExitCode()
	// returns ExitRefused (5).
	StateRestoreRefused InstallState = "RESTORE_REFUSED"

	// StateRestoreIntentRequired is the terminal state produced when
	// the lattice returns REQUIRE_EXPLICIT_INTENT. Same discipline as
	// StateRestoreRefused: terminal, not failed, not apply-terminal,
	// not recorded in history. ExitCode() returns ExitIntentRequired
	// (6), distinct from refusal so operators and automation can tell
	// "engine said no" from "engine needs you to clarify".
	StateRestoreIntentRequired InstallState = "RESTORE_INTENT_REQUIRED"

	// StateRestoreDecided is the NON-TERMINAL policy-handoff marker
	// produced when the lattice returns PROCEED. Locked semantics per
	// contract seed §7 (merged as PR #493):
	//
	//   1. Policy-only: records that the decision engine said PROCEED.
	//   2. Non-terminal for apply semantics: IsApplyTerminal() and
	//      IsTerminal() both return false.
	//   3. Excluded from update-history.json: Option A discipline
	//      continues; main.go already gates history on cfg.mode !=
	//      "uninstall" AND state.IsApplyTerminal(). This state will
	//      eventually gain its own mode-guard if --mode=restore ever
	//      writes history; for now, IsApplyTerminal=false closes the
	//      write path defensively.
	//   4. Not evidence that restoration happened: no kernel, service,
	//      or filesystem change is implied. PR-25+ execution would
	//      change state further; in PR-24, PROCEED is a handoff
	//      outcome only.
	//
	// ExitCode() returns ExitCommitted (0) — the operator got the
	// decision they asked for, and the process exit code reflects
	// decision success, not execution success.
	StateRestoreDecided InstallState = "RESTORE_DECIDED"

	// StateRestoreExecuted is the full-success terminal: mutation
	// completed, inline verification (§21.1) passed, safety net was
	// removed. Operator's authorized restore is in effect.
	StateRestoreExecuted InstallState = "RESTORE_EXECUTED"

	// StateRestoreFailedExecution is the mid-flight failure terminal:
	// mutation failed before completion. Safety net is still present;
	// system is rolled to the known-safe state. Explicit operator
	// inspection required before any further mutation.
	StateRestoreFailedExecution InstallState = "RESTORE_FAILED_EXECUTION"

	// StateRestoreDegraded is the soft-fail-after-mutation terminal:
	// mutation completed, inline verification flagged a soft-fail
	// condition, safety net was removed under explicit policy. The
	// authorized restore is in effect but warrants operator follow-up.
	StateRestoreDegraded InstallState = "RESTORE_DEGRADED"

	// StateRestoreFailedVerification is the hard-fail-after-mutation
	// terminal: mutation completed but inline verification (§21.1)
	// hard-failed. Safety net is RETAINED (contract §21.3). Explicit
	// operator action required.
	StateRestoreFailedVerification InstallState = "RESTORE_FAILED_VERIFICATION"
)

func (InstallState) ExitCode

func (s InstallState) ExitCode() int

ExitCode returns the process exit code for this state.

func (InstallState) IsApplyTerminal added in v1.100.4

func (s InstallState) IsApplyTerminal() bool

IsApplyTerminal reports whether a state represents the terminal outcome of a real apply operation (install or upgrade). Only these states should produce an entry in update-history.json — preview / planning / dry-run states must not.

Explicit allowlist, not a default catch-all. PR-22B introduced this after the previous audit found that any non-Committed/Degraded state was silently mapped to "install_fail" in history — including dry-run-terminal states that never attempted mutation.

Consumers that need to distinguish "apply succeeded / apply failed / apply was never attempted" must base the decision on IsApplyTerminal and NOT on the string value of the state.

func (InstallState) IsDeferredRebuild added in v1.230.0

func (s InstallState) IsDeferredRebuild() bool

IsDeferredRebuild reports whether the state is a v1.230.0 Gate 6R deferred-rebuild terminal: the rebuild did NOT execute, the firewall was NOT modified, and the convergence is still owed.

⛔ IT IS NOT A FAILURE PREDICATE. IsFailed() stays false for both states. This exists so control flow that must STOP (the phase runner) and diagnostics that must SPEAK (the state file's reason) can act on them without pretending a rebuild failed.

func (InstallState) IsFailed

func (s InstallState) IsFailed() bool

IsFailed returns true if the state represents a failure.

PR-24: restore policy-engine terminal states (StateRestoreRefused, StateRestoreIntentRequired) are NOT failures — refusal and intent-required are correct policy outcomes, not error conditions.

func (InstallState) IsRestoreExecuted added in v1.100.4

func (s InstallState) IsRestoreExecuted() bool

IsRestoreExecuted reports whether the state represents a PR-25 restore execution terminal where actual mutation occurred AND was retained (i.e. the operator's authorized restore is now in effect).

Per contract §19.2 layer 2: this helper returns true ONLY for StateRestoreExecuted and StateRestoreDegraded. It returns false for the two failure-class terminals (StateRestoreFailedExecution and StateRestoreFailedVerification) AND for StateRestoreDecided.

Consumers MUST NOT use sf.State == StateRestoreDecided to infer that restoration execution has occurred (contract §19.3). Use IsRestoreExecuted instead.

func (InstallState) IsTerminal

func (s InstallState) IsTerminal() bool

IsTerminal returns true if the state is a final state (no further transitions).

PR-24: StateRestoreRefused and StateRestoreIntentRequired are terminal; StateRestoreDecided is NOT (it is a policy-handoff marker, per contract seed §7).

func (InstallState) RecoveryClass added in v1.230.0

func (s InstallState) RecoveryClass() RecoveryClass

RecoveryClass returns the mechanism whose route to COMMITTED is established.

func (InstallState) RepairReachesCommitted added in v1.230.0

func (s InstallState) RepairReachesCommitted() bool

RepairReachesCommitted reports whether --repair from this state can establish every fact COMMITTED requires.

⛔ DERIVED, NOT DECLARED. It reads ResumePhase — the same function --repair itself uses — so the answer cannot disagree with the behaviour it describes.

func (InstallState) ResumePhase

func (s InstallState) ResumePhase() Phase

ResumePhase returns the phase to resume from when running in --repair mode.

type Phase

type Phase string

Phase represents a named installer phase.

const (
	PhaseDetect    Phase = "DETECT"
	PhasePrepare   Phase = "PREPARE"
	PhaseSwitch    Phase = "SWITCH"
	PhaseConfigure Phase = "CONFIGURE"
	PhaseValidate  Phase = "VALIDATE"
	PhaseReport    Phase = "REPORT"
)

type RecoveryClass added in v1.230.0

type RecoveryClass string

RecoveryClass names the mechanism an operator should use for a given state.

const (
	// RecoveryRepair — `nftban-installer --repair` has a DEMONSTRATED route to
	// COMMITTED from this state.
	RecoveryRepair RecoveryClass = "REPAIR"

	// RecoveryRetryFullTransaction — --repair must NOT be advertised. The operator
	// must re-run the normal NFTBan update/install transaction.
	//
	// ⛔ WORDED BY OPERATION, NEVER BY PACKAGE MANAGER. The live proof is RPM-only and
	// the originating incident host is dpkg; encoding `rpm -Uvh --force` (or any
	// specific command) into the contract would be wrong on half the fleet. A concrete
	// command may only ever be an ADDITIONAL HINT, and only where the originating
	// package manager is RELIABLY known.
	RecoveryRetryFullTransaction RecoveryClass = "RETRY_FULL_TRANSACTION"
)

type StateFile

type StateFile struct {
	State             InstallState
	Mode              string
	Version           string
	Timestamp         time.Time
	SSHPort           int
	Authority         string
	Panel             string
	Conflicts         string
	SchemaVersion     string
	PhaseReached      string
	FailureReason     string
	PreflightPassed   bool
	RebuildExitCode   int
	RebuildDurationMs int64
	ServicesEnabled   string
	ServicesFailed    string
	// v1.222.1 Lane 4: structured failed-unit attribution companions to
	// SERVICES_FAILED (canonical, comma-separated nftban unit names). Backward-
	// compatible — absent in old state files → empty.
	ServicesFailedPreexisting string
	ServicesFailedInWindow    string

	// v1.222.1 HEALTH-OOM hotfix (Lane 2): profile-derived health-service
	// resource reconciliation result. All optional/backward-compatible — an old
	// state file without these keys parses to zero values. No volatile timestamp.
	// v1.228.5 BUG-REBUILD-DISCARDS-FAILED-WHITELIST-RECONCILE: durable whitelist.d
	// convergence verdict from services.SyncWhitelist, the SOLE installer convergence
	// authority (switchop.Rebuild runs pre-daemon with --install-context and DEFERS
	// the projection). CONVERGED | FAILED | "" (not evaluated). A FAILED value means
	// configured management IPs are not projected into the running set.
	WhitelistConvergence string

	// ConvergenceVerified — v1.230.0 Gate 6R. The POST-UPDATE CONVERGENCE verdict
	// (switchop.VerifyPostUpdateConvergence), persisted as CONVERGENCE_VERIFIED.
	//
	// ⛔ PACKAGE UPDATED != PROJECTION GENERATED != PROJECTION VALIDATED
	//    != KERNEL RULESET APPLIED != RUNTIME CONVERGED.
	// The installer used to collapse those, so an update could be reported successful
	// with convergence never proven. This carries the phase verdict to the assertion
	// that gates COMMITTED, exactly as WHITELIST_CONVERGENCE above does.
	//
	// "" means NOT EVALUATED (a pre-v1.230.0 record, or a path that does not evaluate
	// it). ⛔ It is never read as VERIFIED.
	ConvergenceVerified string

	HealthResourceState         string // effective state: ACTIVE_MATCH/FALLBACK_MATCH/FALLBACK_UNDERSIZED/EXTERNAL_OVERRIDE_CONFLICT/…
	HealthResourceProfile       string // resource tier: small/medium/large
	HealthResourceAuthority     string // always internal/safety
	HealthResourceReason        string // tier-selection reason
	HealthResourceProtection    bool   // true iff profile-derived OOM protection is effectively active
	HealthMemHighCalculated     int64
	HealthMemMaxCalculated      int64
	HealthMemHighEffective      int64
	HealthMemMaxEffective       int64
	HealthTasksMaxEffective     int64
	HealthResourceDropin        string // canonical generated drop-in path
	HealthResourceDropinLoaded  bool
	HealthResourceLoadedDropins string // space-separated ALL loaded DropInPaths (conflict evidence)
	HealthResourceSourceVer     string
	HealthResourceGenerated     string // file-level generated state
	HealthResourceError         string // last reconciliation error (cleared on success)

	// DryRun, when true, makes Transition update in-memory fields only
	// and skip the atomic file write. PR-22B introduced this so that
	// dry-run paths sharing phase functions with real install/upgrade
	// (e.g. phaseDetect reused by runUpdateDryRun) do not persist
	// install_state during observational runs.
	//
	// Callers that need to force a real persistence during a dry-run
	// (none exist today, but reserved for future audit artifacts) can
	// set this to false temporarily and call Transition, but that is
	// discouraged — the expected contract is DryRun=cfg.dryRun at the
	// start of the run and never toggled.
	DryRun bool
	// contains filtered or unexported fields
}

StateFile holds all install state and handles persistence.

Schema contract (frozen):

INSTALL_STATE       — current InstallState enum value
INSTALL_MODE        — "install" or "upgrade"
INSTALL_VERSION     — version string (e.g. "1.73.0")
INSTALL_TIMESTAMP   — RFC3339 UTC timestamp
SSH_PORT            — detected SSH port (int)
AUTHORITY           — "UPDATE", "TAKEOVER", "FRESH", or ""
PANEL               — detected panel type or ""
CONFLICTS           — comma-separated conflict names or ""
SCHEMA_VERSION      — nftables schema version (e.g. "0.7.3")
PHASE_REACHED       — last phase name reached
FAILURE_REASON      — human-readable failure description or ""
PREFLIGHT_PASSED    — "1" or "0"
CONVERGENCE_VERIFIED — post-update convergence verdict (v1.230.0 Gate 6R)
REBUILD_EXIT_CODE   — rebuild process exit code (int)
REBUILD_DURATION_MS — rebuild wall-clock duration in milliseconds
SERVICES_ENABLED    — comma-separated list of enabled service units
SERVICES_FAILED     — comma-separated list of failed service units

func NewStateFile

func NewStateFile(stateDir string) *StateFile

NewStateFile creates a new StateFile with the given state directory. If stateDir is empty, DefaultStateDir is used.

func (*StateFile) Path

func (sf *StateFile) Path() string

Path returns the full path to the state file.

func (*StateFile) Read

func (sf *StateFile) Read() error

Read reads an existing state file. Returns os.ErrNotExist if file is missing (which is normal for a fresh install).

func (*StateFile) RebuildEvidenceContradiction added in v1.230.0

func (sf *StateFile) RebuildEvidenceContradiction() string

RebuildEvidenceContradiction returns a description when this record contradicts itself about the rebuild, or "" when it does not.

⛔ THE DEFECT IT REJECTS (dns1, one file, one run):

FAILURE_REASON=... produced no usable result contract (exit 1): ...
REBUILD_EXIT_CODE=0
REBUILD_DURATION_MS=0

while installer.log recorded `(exit=1)` and `elapsed=31.22s`. The prose was right and the machine-readable pair was wrong — and automation reads the machine-readable pair.

This is the install_state counterpart of the rejection ReadRebuildResult already applies to the rebuild RESULT contract (a REFUSED record that also claims a mutation is refused rather than believed). ONE VALIDATOR PER CONTRACT: this is the only place install_state is checked against itself, exactly as that is the only place the result record is.

⛔ IT REJECTS, IT NEVER REPAIRS. Adopting the prose's number would make an unstructured field the authority for a structured one.

func (*StateFile) RebuildEvidenceRejection added in v1.230.0

func (sf *StateFile) RebuildEvidenceRejection() string

RebuildEvidenceRejection returns why the rebuild evidence was rejected, or "".

func (*StateFile) RebuildEvidenceUsable added in v1.230.0

func (sf *StateFile) RebuildEvidenceUsable() bool

RebuildEvidenceUsable reports whether REBUILD_EXIT_CODE / REBUILD_DURATION_MS from THIS record may be consumed as measurements.

⛔ CONSULT THIS BEFORE READING EITHER FIELD. A rejected pair is not "probably fine"; it is a pair we have positively shown to disagree with the rest of its own record.

func (*StateFile) StateFieldPresent added in v1.228.0

func (sf *StateFile) StateFieldPresent() bool

StateFieldPresent reports whether Read() actually parsed an INSTALL_STATE= line from the file on disk.

This exists because NewStateFile seeds State with a constructor default (StateFilesInstalled). Without this signal a caller cannot distinguish "the file records this state" from "the file recorded nothing and you are looking at the constructor". Verification paths MUST consult it before treating State as persisted evidence.

func (*StateFile) Transition

func (sf *StateFile) Transition(newState InstallState, phase Phase, reason string) error

func (*StateFile) WriteAtomic

func (sf *StateFile) WriteAtomic() error

WriteAtomic writes the state file atomically (write to tmp, then rename).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL