// What a named unit is running, established from the bytes — D3 of the // console release defences (spec 2026-10-06-console-release-defences-design.md // §4.2, §4.3, §4.5). // // observe checks the first three of spec §4.3's mint conditions: the binding // is verified, the unit is active in one invocation for at least `after`, the // sha256 of /proc//exe equals the live link's bytes, and the route is // ready now. The root pass mints on it (proofroot.go) and records the build // in the healthy history on it. recordSettled is the other way into that // history: the hook D1's checkedUnitRestart runs when the settle check passes. // // Both paths hash the running executable's bytes. A sha256 is never taken // from a manifest or a version directory's name: the only build a box may // vouch for is the one it is running. package host import ( "context" "fmt" "os" "time" "github.com/carlosframework/platform/internal/activator" "github.com/carlosframework/platform/internal/registry" "github.com/carlosframework/platform/internal/release" ) type liveBuild struct { binding proofBinding SHA256 string Version string ActiveSince time.Time // The process observed, so a later look can tell it is the same one. invocation string pid int start string } type proofObserver struct { resolve proofBindingResolver sys unitSystem procExe func(pid int) string // "/proc//exe" in production optDir string now func() time.Time ready func(rt registry.Route) func() error // routeReady in production; nil func = no upstream } func productionProofObserver(optDir string) proofObserver { return proofObserver{sys: systemdUnits{}, procExe: procExePath, optDir: optDir, now: time.Now, resolve: productionRootRestartChecks(systemdUnits{}, optDir).resolve, ready: func(rt registry.Route) func() error { return proofRouteReady(rt, 2*time.Second) }} } func procExePath(pid int) string { return fmt.Sprintf("/proc/%d/exe", pid) } func (o proofObserver) observe(rt registry.Route, after time.Duration) (liveBuild, error) { if o.resolve == nil { return liveBuild{}, os.ErrPermission } binding, err := o.resolve(context.Background(), rt) if err != nil { return liveBuild{}, err } if binding.Source == "" { return liveBuild{}, os.ErrPermission } rt = binding.Route link := release.LiveLink(o.optDir, rt.Host) before, err := o.sys.Show(rt.Unit) if err != nil { return liveBuild{}, fmt.Errorf("cannot read %s: %v", rt.Unit, err) } if before.ExecPath != proofExecPath(binding, o.optDir) { return liveBuild{}, fmt.Errorf("binding: %s runs %s, not %s", rt.Unit, before.ExecPath, link) } if before.ActiveState != "active" || before.MainPID <= 0 { return liveBuild{}, fmt.Errorf("%s is %s", rt.Unit, before.ActiveState) } if before.ActiveEnterTimestamp.IsZero() { return liveBuild{}, fmt.Errorf("%s reports no ActiveEnterTimestamp", rt.Unit) } start, err := o.sys.StartTime(before.MainPID) if err != nil || start == "" { return liveBuild{}, os.ErrPermission } if up := o.now().Sub(before.ActiveEnterTimestamp); up < after { return liveBuild{}, fmt.Errorf("%s has been active for %s, needs %s", rt.Unit, up.Round(time.Second), after) } exe, err := release.FileSHA256(o.procExe(before.MainPID)) if err != nil { return liveBuild{}, fmt.Errorf("hashing the running executable: %v", err) } live, err := release.FileSHA256(link) if err != nil { return liveBuild{}, fmt.Errorf("hashing the live link's build: %v", err) } if exe != live { return liveBuild{}, fmt.Errorf("the running executable (sha256 %s) is not the live link's build (sha256 %s)", exe, live) } if binding.Network { if o.ready == nil { return liveBuild{}, os.ErrPermission } if ready := o.ready(rt); ready != nil { if err := ready(); err != nil { return liveBuild{}, fmt.Errorf("not ready: %v", err) } } else { return liveBuild{}, os.ErrPermission } } again, err := o.sys.Show(rt.Unit) if err != nil || again.ActiveState != "active" || again.InvocationID != before.InvocationID || again.MainPID != before.MainPID { return liveBuild{}, fmt.Errorf("%s restarted during the check", rt.Unit) } current, err := o.resolve(context.Background(), rt) if err != nil || current.Source != binding.Source { return liveBuild{}, os.ErrPermission } againExe, err := release.FileSHA256(o.procExe(again.MainPID)) if err != nil || againExe != exe || again.ExecPath != before.ExecPath { return liveBuild{}, os.ErrPermission } againStart, err := o.sys.StartTime(again.MainPID) if err != nil || againStart != start { return liveBuild{}, os.ErrPermission } ver, _ := release.LiveVersion(o.optDir, rt.Host) return liveBuild{SHA256: exe, Version: ver, ActiveSince: before.ActiveEnterTimestamp, invocation: before.InvocationID, pid: before.MainPID, start: start, binding: binding}, nil } func (o proofObserver) recordSettled(history string) func(registry.Route) error { return func(rt registry.Route) error { if rt.Unit == "" || rt.Unit == activator.UnitName(rt.Host) { return nil } b, err := o.observe(rt, 0) if err != nil { return err } return release.RecordHealthy(history, b.binding.Route.Account, b.binding.Route.App, release.HealthyBuild{SHA256: b.SHA256, Version: b.Version, How: "settle", At: o.now().UTC()}) } }