restarts watch and memory drop-in: the last three review findings (#284)
Summary
Astra's second pass on slice A: all four P1s fixed, three P2s left. - A reload owed to systemd is a marker file beside the drop-in, written before the file changes and removed after the reload, instead of a rollback. Every way of stopping in between leaves the marker, so the next run reloads even though the bytes already match. - The saved restart watch is validated on load (version, times, counter ranges); anything else is an empty watch. A decodable file with no baseline no longer turns a lifetime counter into recent restarts. - A clock stepped backwards rebaselines the watch instead of keeping future-dated restarts in the window. Census overlay regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 files changed,
+154
−52
internal/host/restartwatch.go+35 −6internal/host/restartwatch_test.go+59 −0internal/ops/litestream_memory.go+22 −17internal/ops/ops_test.go+31 −22internal/provision/plan14_root_reads_test.go+1 −1internal/provision/testdata/plan14-root-reads-ls284.tsv+6 −6
CI cancelled — superseded by a newer push — run details
CI log
=== gate === cd launchctx && go vet ./... && go test ./... -count=1 ok amadan.net/carlos/platform.git/launchctx 0.011s ? amadan.net/carlos/platform.git/launchctx/controlname [no test files] ? amadan.net/carlos/platform.git/launchctx/wireid [no test files] go vet ./... gofmt -l .: clean go test ./... -count=1 -timeout 30m ok github.com/carlosframework/platform 332.941s ? github.com/carlosframework/platform/cmd/changelog [no test files] ok github.com/carlosframework/platform/cmd/console 3.026s ok github.com/carlosframework/platform/cmd/moveprobe 93.193s ok github.com/carlosframework/platform/cmd/zddprobe 0.377s ok github.com/carlosframework/platform/internal/activator 226.409s ok github.com/carlosframework/platform/internal/agentsock 0.018s ok github.com/carlosframework/platform/internal/alerting 0.003s ok github.com/carlosframework/platform/internal/analytics 0.019s ok github.com/carlosframework/platform/internal/appconfig 2.269s ok github.com/carlosframework/platform/internal/apperr 0.070s ok github.com/carlosframework/platform/internal/applog 0.129s ok github.com/carlosframework/platform/internal/awsinv 0.005s ok github.com/carlosframework/platform/internal/boxchoice 0.004s ok github.com/carlosframework/platform/internal/boxid 0.842s ? github.com/carlosframework/platform/internal/boxid/boxidtest [no test files] ? github.com/carlosframework/platform/internal/boxid/internal/signtest [no test files] ok github.com/carlosframework/platform/internal/boxvolumes 0.008s ok github.com/carlosframework/platform/internal/caps 0.011s ok github.com/carlosframework/platform/internal/certwarm 0.002s ok github.com/carlosframework/platform/internal/cgroup 0.693s ok github.com/carlosframework/platform/internal/changelog 0.308s ok github.com/carlosframework/platform/internal/clamd 0.054s ? github.com/carlosframework/platform/internal/cliflag [no test files] amadan: job cancelled — a newer push superseded it
diff --git a/internal/host/restartwatch.go b/internal/host/restartwatch.go| index 01160b67d..028cfbc06 100644 |
| --- a/internal/host/restartwatch.go |
| +++ b/internal/host/restartwatch.go |
| @@ -44,11 +44,16 @@ type unitRestartState struct { |
| // the agent's own restarts are counted: carlos-edge.service is one of the |
| // units, and a watch held only in memory would die with each of them. |
| type restartWatch struct { |
| + // V is the saved shape's version; loadRestartWatch takes no other. |
| + V int `json:"v"` |
| // At is the last tick that got an answer from systemd. |
| At time.Time `json:"at"` |
| Units map[string]*unitRestartHistory `json:"units"` |
| } |
| +// restartWatchVersion is the only saved shape this build reads. |
| +const restartWatchVersion = 1 |
| + |
| type unitRestartHistory struct { |
| // Last is the counter at the last tick. |
| Last int64 `json:"last"` |
| @@ -79,14 +84,17 @@ type restartBump struct { |
| // |
| // A gap longer than the window between two answers leaves nothing to count: |
| // the watch knows how far each counter moved and not when, so it starts |
| -// again from the present rather than date old restarts to this tick. |
| +// again from the present rather than date old restarts to this tick. A clock |
| +// that has gone backwards is the same: what was saved is dated in a future |
| +// the watch can no longer measure against. |
| func (w *restartWatch) tick(now time.Time, units map[string]unitRestartState) map[string]metrics.UnitRestarts { |
| if units == nil { |
| return nil |
| } |
| - if w.Units == nil || (!w.At.IsZero() && now.Sub(w.At) > restartWindow) { |
| + if w.Units == nil || now.Before(w.At) || (!w.At.IsZero() && now.Sub(w.At) > restartWindow) { |
| w.Units = map[string]*unitRestartHistory{} |
| } |
| + w.V = restartWatchVersion |
| for name := range w.Units { |
| if _, ok := units[name]; !ok { |
| delete(w.Units, name) // unloaded: nothing left to watch |
| @@ -133,18 +141,39 @@ func (w *restartWatch) tick(now time.Time, units map[string]unitRestartState) ma |
| // alert state it feeds. |
| func restartWatchPath() string { return filepath.Join(dataDir(), "monitor", "restarts.json") } |
| -// loadRestartWatch reads a saved watch. Anything unreadable is an empty |
| -// watch, which is always a safe place to start from: it can delay an alert by |
| -// a few ticks and can never invent one. |
| +// loadRestartWatch reads a saved watch. Anything unreadable, of another |
| +// version, or not exactly the shape tick leaves behind is an empty watch, |
| +// which is always a safe place to start from: it can delay an alert by a few |
| +// ticks and can never invent one. The file sits in the data directory, so it |
| +// is checked as input, not trusted as this program's own output: a unit |
| +// history with no baseline would read a healthy unit's lifetime counter as |
| +// that many restarts just now. |
| func loadRestartWatch(path string) restartWatch { |
| var w restartWatch |
| b, err := os.ReadFile(path) |
| - if err != nil || json.Unmarshal(b, &w) != nil { |
| + if err != nil || json.Unmarshal(b, &w) != nil || !w.wellFormed() { |
| return restartWatch{} |
| } |
| return w |
| } |
| +func (w restartWatch) wellFormed() bool { |
| + if w.V != restartWatchVersion || w.At.IsZero() { |
| + return false |
| + } |
| + for _, h := range w.Units { |
| + if h == nil || h.Last < 0 || h.QuietSince.IsZero() || h.QuietSince.After(w.At) { |
| + return false |
| + } |
| + for _, b := range h.Bumps { |
| + if b.N <= 0 || b.At.IsZero() || b.At.After(w.At) { |
| + return false |
| + } |
| + } |
| + } |
| + return true |
| +} |
| + |
| // saveRestartWatch writes the watch by temp file and rename, like |
| // persistAlertState. Best effort: a failed save costs history across the |
| // next agent restart, nothing more. |
diff --git a/internal/host/restartwatch_test.go b/internal/host/restartwatch_test.go| index 9efda2ccd..abdbe2979 100644 |
| --- a/internal/host/restartwatch_test.go |
| +++ b/internal/host/restartwatch_test.go |
| @@ -1,6 +1,7 @@ |
| package host |
| import ( |
| + "os" |
| "path/filepath" |
| "testing" |
| "time" |
| @@ -152,6 +153,64 @@ func TestRestartWatchSavedAcrossAnAgentRestart(t *testing.T) { |
| } |
| } |
| +// The saved watch is a file in the data directory, so it is read as input. A |
| +// history that decodes but is not what tick writes must not become a |
| +// baseline: a unit with no recorded counter would turn a healthy unit's |
| +// lifetime 900 into 900 restarts at startup. |
| +func TestRestartWatchDiscardsASavedWatchItDidNotWrite(t *testing.T) { |
| + t0 := time.Date(2026, 10, 13, 12, 0, 0, 0, time.UTC) |
| + at := t0.Format(time.RFC3339) |
| + for name, body := range map[string]string{ |
| + "not json": `{`, |
| + "no version": `{"at":"` + at + `","units":{"carlos-litestream.service":{"last":5,"quiet_since":"` + at + `"}}}`, |
| + "another version": `{"v":2,"at":"` + at + `","units":{}}`, |
| + "no time": `{"v":1,"units":{"carlos-litestream.service":{"last":5,"quiet_since":"` + at + `"}}}`, |
| + "empty unit history": `{"v":1,"at":"` + at + `","units":{"carlos-litestream.service":{}}}`, |
| + "null unit history": `{"v":1,"at":"` + at + `","units":{"carlos-litestream.service":null}}`, |
| + "negative counter": `{"v":1,"at":"` + at + `","units":{"carlos-litestream.service":{"last":-3,"quiet_since":"` + at + `"}}}`, |
| + "negative rise": `{"v":1,"at":"` + at + `","units":{"carlos-litestream.service":{"last":5,"quiet_since":"` + at + `","bumps":[{"at":"` + at + `","n":-900}]}}}`, |
| + "rise from the future": `{"v":1,"at":"` + at + `","units":{"carlos-litestream.service":{"last":5,"quiet_since":"` + at + `","bumps":[{"at":"2030-01-01T00:00:00Z","n":4}]}}}`, |
| + } { |
| + path := filepath.Join(t.TempDir(), "restarts.json") |
| + if err := os.WriteFile(path, []byte(body), 0o644); err != nil { |
| + t.Fatal(err) |
| + } |
| + w := loadRestartWatch(path) |
| + if w.Units != nil || !w.At.IsZero() { |
| + t.Errorf("%s: loaded as %+v, want an empty watch", name, w) |
| + continue |
| + } |
| + if got := w.tick(t0.Add(time.Minute), oneUnit(900)); got[lsUnit].Count != 0 || got[lsUnit].Settled { |
| + t.Errorf("%s: a healthy unit at 900 read as %+v, want a fresh baseline", name, got[lsUnit]) |
| + } |
| + } |
| +} |
| + |
| +// A clock stepped backwards leaves the saved rises dated in the future. They |
| +// are dropped with the rest of the history, not kept "inside the window" |
| +// until the clock catches up with them. |
| +func TestRestartWatchRebaselinesWhenTheClockGoesBackwards(t *testing.T) { |
| + path := filepath.Join(t.TempDir(), "restarts.json") |
| + noon := time.Date(2026, 10, 13, 12, 0, 0, 0, time.UTC) |
| + var w restartWatch |
| + w.tick(noon, oneUnit(10)) |
| + if got := w.tick(noon.Add(time.Minute), oneUnit(14)); got[lsUnit].Count != 4 { |
| + t.Fatalf("before the step: %+v, want 4", got[lsUnit]) |
| + } |
| + if err := saveRestartWatch(path, w); err != nil { |
| + t.Fatal(err) |
| + } |
| + // The clock is corrected back an hour and the agent restarts. |
| + w = loadRestartWatch(path) |
| + got := w.tick(noon.Add(-time.Hour), oneUnit(14)) |
| + if got[lsUnit].Count != 0 || got[lsUnit].Settled { |
| + t.Fatalf("after the step: %+v, want no dated restarts and not settled", got[lsUnit]) |
| + } |
| + if got = w.tick(noon.Add(-time.Hour).Add(restartWindow), oneUnit(14)); got[lsUnit].Count != 0 || !got[lsUnit].Settled { |
| + t.Fatalf("a window later: %+v, want settled at zero", got[lsUnit]) |
| + } |
| +} |
| + |
| func TestParseUnitRestarts(t *testing.T) { |
| // The shape systemd 257 prints: properties in its own order, a blank line |
| // between units. |
diff --git a/internal/ops/litestream_memory.go b/internal/ops/litestream_memory.go| index c503cfa2a..55f359c0d 100644 |
| --- a/internal/ops/litestream_memory.go |
| +++ b/internal/ops/litestream_memory.go |
| @@ -16,7 +16,6 @@ package ops |
| // this verb keeps the unit's own default. |
| import ( |
| - "errors" |
| "fmt" |
| "os" |
| "path/filepath" |
| @@ -85,33 +84,39 @@ func LitestreamMemoryDropIn(n int) string { |
| // reload for one, and a half-written drop-in falls back to the unit's smaller |
| // default under a replicator that is already using more. |
| // |
| -// A reload that fails puts the previous file back. The file on disk then |
| -// still differs from what this verb wants, so the next run tries again; |
| -// leaving the new file in place would have every later run find nothing to |
| -// change and never reload. |
| +// A reload is owed from the moment the file is about to change until systemd |
| +// has taken it, and that debt is a marker file beside the drop-in, not a |
| +// comparison of bytes: once the new file is on disk a later run finds nothing |
| +// to rewrite, and would never reload a cap an earlier run failed to load. |
| +// The marker is written first and removed last, so every way of stopping in |
| +// between leaves it for the next run. systemd ignores it (no .conf suffix). |
| func writeMemoryDropIn(path string, n int, reload func() error) (changed bool, err error) { |
| want := LitestreamMemoryDropIn(n) |
| + pending := path + ".reload-pending" |
| prior, readErr := os.ReadFile(path) |
| - switch { |
| - case readErr == nil && string(prior) == want: |
| - return false, nil |
| - case readErr != nil && !os.IsNotExist(readErr): |
| + if readErr != nil && !os.IsNotExist(readErr) { |
| return false, readErr |
| } |
| + current := readErr == nil && string(prior) == want |
| + if _, err := os.Lstat(pending); current && os.IsNotExist(err) { |
| + return false, nil |
| + } |
| if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { |
| return false, err |
| } |
| - if err := safefile.WriteFile(path, []byte(want), 0o644); err != nil { |
| + if err := safefile.WriteFile(pending, nil, 0o644); err != nil { |
| return false, err |
| } |
| - if err := reload(); err != nil { |
| - var undo error |
| - if readErr == nil { |
| - undo = safefile.WriteFile(path, prior, 0o644) |
| - } else { |
| - undo = os.Remove(path) |
| + if !current { |
| + if err := safefile.WriteFile(path, []byte(want), 0o644); err != nil { |
| + return false, err |
| } |
| - return false, errors.Join(fmt.Errorf("systemd did not reload: %w", err), undo) |
| } |
| + if err := reload(); err != nil { |
| + return false, fmt.Errorf("systemd did not reload: %w", err) |
| + } |
| + // systemd has the cap. A marker that will not go only costs the next run |
| + // one reload it did not need. |
| + _ = os.Remove(pending) |
| return true, nil |
| } |
diff --git a/internal/ops/ops_test.go b/internal/ops/ops_test.go| index a3a414636..7e9d89a79 100644 |
| --- a/internal/ops/ops_test.go |
| +++ b/internal/ops/ops_test.go |
| @@ -941,7 +941,7 @@ func TestWriteMemoryDropInReloadsOnlyOnChange(t *testing.T) { |
| t.Errorf("drop-in mode = %v (%v), want 0644: systemd reads it as root, operators as anyone", fi.Mode().Perm(), err) |
| } |
| if ents, _ := os.ReadDir(filepath.Dir(path)); len(ents) != 1 { |
| - t.Errorf("the drop-in directory holds %d entries, want only the drop-in (no staging left behind)", len(ents)) |
| + t.Errorf("the drop-in directory holds %d entries, want only the drop-in (no staging or marker left behind)", len(ents)) |
| } |
| // The same count changes no byte, so it reloads nothing. |
| @@ -955,37 +955,46 @@ func TestWriteMemoryDropInReloadsOnlyOnChange(t *testing.T) { |
| } |
| // A reload that fails must not strand the new cap on disk unloaded: every |
| -// later run would find the file already right and never reload it. The |
| -// previous file comes back, so the next run tries again and, when the reload |
| -// works, reports the change. |
| +// later run would find the file already right and never reload it. The debt |
| +// is a marker beside the drop-in, so the next run reloads whatever the bytes |
| +// say, and reports the change once systemd has it. |
| func TestWriteMemoryDropInRetriesAfterAFailedReload(t *testing.T) { |
| path := filepath.Join(t.TempDir(), "d", "50-memory.conf") |
| - broken := func() error { return errors.New("no systemd here") } |
| - working := func() error { return nil } |
| + pending := path + ".reload-pending" |
| + reloads := 0 |
| + broken := func() error { reloads++; return errors.New("no systemd here") } |
| + working := func() error { reloads++; return nil } |
| + want := "MemoryMax=" + strconv.FormatInt(LitestreamMemoryMax(200)>>20, 10) + "M\n" |
| - // No file before: a failed reload leaves none. |
| if changed, err := writeMemoryDropIn(path, 200, broken); changed || err == nil { |
| t.Fatalf("changed=%v err=%v, want false and the reload error", changed, err) |
| } |
| - if _, err := os.Stat(path); !os.IsNotExist(err) { |
| - t.Fatalf("a failed first reload left the drop-in in place (stat err %v)", err) |
| + if body, _ := os.ReadFile(path); !strings.Contains(string(body), want) { |
| + t.Fatalf("the new cap is not on disk after a failed reload:\n%s", body) |
| } |
| - if changed, err := writeMemoryDropIn(path, 200, working); !changed || err != nil { |
| - t.Fatalf("retry: changed=%v err=%v, want true, nil", changed, err) |
| + if _, err := os.Stat(pending); err != nil { |
| + t.Fatalf("a failed reload left no marker: %v", err) |
| } |
| - |
| - // A file before: a failed reload puts its bytes back. |
| - before, _ := os.ReadFile(path) |
| - if changed, err := writeMemoryDropIn(path, 400, broken); changed || err == nil { |
| - t.Fatalf("changed=%v err=%v, want false and the reload error", changed, err) |
| + // The bytes are already right, and it still reloads, twice if it has to. |
| + if changed, err := writeMemoryDropIn(path, 200, broken); changed || err == nil || reloads != 2 { |
| + t.Fatalf("second failure: changed=%v err=%v reloads=%d, want false, an error, 2", changed, err, reloads) |
| + } |
| + if changed, err := writeMemoryDropIn(path, 200, working); !changed || err != nil || reloads != 3 { |
| + t.Fatalf("retry: changed=%v err=%v reloads=%d, want true, nil, 3", changed, err, reloads) |
| } |
| - if after, _ := os.ReadFile(path); string(after) != string(before) { |
| - t.Fatalf("a failed reload left the new cap on disk:\n%s", after) |
| + if _, err := os.Stat(pending); !os.IsNotExist(err) { |
| + t.Fatalf("the marker outlived a successful reload (stat err %v)", err) |
| } |
| - if changed, err := writeMemoryDropIn(path, 400, working); !changed || err != nil { |
| - t.Fatalf("retry over a previous file: changed=%v err=%v, want true, nil", changed, err) |
| + if changed, err := writeMemoryDropIn(path, 200, working); changed || err != nil || reloads != 3 { |
| + t.Fatalf("settled: changed=%v err=%v reloads=%d, want false, nil, 3", changed, err, reloads) |
| + } |
| + |
| + // A marker with nothing else wrong (a run stopped after systemd reloaded |
| + // but before the marker went) costs one more reload and no more. |
| + if err := os.WriteFile(pending, nil, 0o644); err != nil { |
| + t.Fatal(err) |
| } |
| - if after, _ := os.ReadFile(path); !strings.Contains(string(after), "MemoryMax="+strconv.FormatInt(LitestreamMemoryMax(400)>>20, 10)+"M\n") { |
| - t.Fatalf("the retried cap is not the one for 400 databases:\n%s", after) |
| + if changed, err := writeMemoryDropIn(path, 200, working); !changed || err != nil || reloads != 4 { |
| + t.Fatalf("stray marker: changed=%v err=%v reloads=%d, want true, nil, 4", changed, err, reloads) |
| } |
| } |
diff --git a/internal/provision/plan14_root_reads_test.go b/internal/provision/plan14_root_reads_test.go| index 41ccd4f28..d3c0f085a 100644 |
| --- a/internal/provision/plan14_root_reads_test.go |
| +++ b/internal/provision/plan14_root_reads_test.go |
| @@ -667,7 +667,7 @@ func TestPlan14RootReadCensus(t *testing.T) { |
| // Census ruling ls284 (#284): the litestream memory drop-in, the unit's |
| // restart backoff and the agent's restart-counter probe and its saved state add seven frozen |
| // declarations. |
| - seal = "d17b4d9b8522d2a671b4e0ecdb010a6eeb2dc4c32d2e79a70266cc66845b56ca" |
| + seal = "4501bab20e84943df805b7727eba4aa64e21da84d56e80da5847187ddc6561c8" |
| errs := append(compareRootReads(got, inv), checkRootReadLegacy(inv, seal)...) |
| errs = append(errs, checkRootReadDeclarations(got, inv)...) |
| if len(errs) > 0 { |
diff --git a/internal/provision/testdata/plan14-root-reads-ls284.tsv b/internal/provision/testdata/plan14-root-reads-ls284.tsv| index 9bcbdfcf1..06ecb0245 100644 |
| --- a/internal/provision/testdata/plan14-root-reads-ls284.tsv |
| +++ b/internal/provision/testdata/plan14-root-reads-ls284.tsv |
| @@ -2,21 +2,21 @@ |
| # The ruling is requested from the census owner on amadan #280; these rows state the branch's own account. Earlier overlays are immutable. |
| @legacy f942faf6b1bda4b171de3930c7a9f5627d4db32a2f802c5ecb10ef26410e0af9 internal/console/templates/metrics.html external legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The alerts table shows a rule's detail beside its name. Same alert state objects, read by the same console sweep; no new source or authority. file:external@0 8732b0e3a4e60029b6c13109be12b15f0a48f51f0fb3c7578e6a52fc26e93140 |
| internal/console/templates/metrics.html external legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The alerts table shows a rule's detail beside its name. Same alert state objects, read by the same console sweep; no new source or authority. file:external@0 8732b0e3a4e60029b6c13109be12b15f0a48f51f0fb3c7578e6a52fc26e93140 |
| -@legacy 94e5b066d93dbc5769523cae394e58c44b75643afdd2af60a76b91a35a506a99 internal/host/restartwatch.go loadRestartWatch legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The agent, as carlos, reads back its own restart-watch state from the data directory's monitor folder, beside the alert state it already keeps there. Feeds an alert only; never read by root and no root decision. file:ReadFile@81 96d67295e3e0842253c19e7df5dcefd15485b38cb48b4e249cc45a6ee193d976 |
| -internal/host/restartwatch.go loadRestartWatch legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The agent, as carlos, reads back its own restart-watch state from the data directory's monitor folder, beside the alert state it already keeps there. Feeds an alert only; never read by root and no root decision. file:ReadFile@81 96d67295e3e0842253c19e7df5dcefd15485b38cb48b4e249cc45a6ee193d976 |
| +@legacy 4d3f9d6b930b32c1fd002d7a831aaf4c7317afd20972ba356031c135ffef8a1b internal/host/restartwatch.go loadRestartWatch legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The agent, as carlos, reads back its own restart-watch state from the data directory's monitor folder, beside the alert state it already keeps there. Feeds an alert only; never read by root and no root decision. file:ReadFile@81 1507415bd6d74eb96c6e23de214e9da98cfa6be25a4888d3ecf95b56c45d508c |
| +internal/host/restartwatch.go loadRestartWatch legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The agent, as carlos, reads back its own restart-watch state from the data directory's monitor folder, beside the alert state it already keeps there. Feeds an alert only; never read by root and no root decision. file:ReadFile@81 1507415bd6d74eb96c6e23de214e9da98cfa6be25a4888d3ecf95b56c45d508c |
| @legacy bc86270eeccc35765f5ecd7771202ed99605065e5e703c23bc14934aa79ddb3d internal/host/restartwatch.go platformUnitRestarts legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The agent, as carlos, asks systemd for the restart counters of loaded carlos-*.service units; a fixed command with no registry or tenant input, the same class as internal/host/probes.go adoptTimerActive under J4 root-input. Feeds an alert only; no root decision. file:CommandContext@166 fde6a651c0a8a66c324124a2bd3085959c9b575cae08e2d65d2eb666761b0dcc |
| internal/host/restartwatch.go platformUnitRestarts legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The agent, as carlos, asks systemd for the restart counters of loaded carlos-*.service units; a fixed command with no registry or tenant input, the same class as internal/host/probes.go adoptTimerActive under J4 root-input. Feeds an alert only; no root decision. file:CommandContext@166 fde6a651c0a8a66c324124a2bd3085959c9b575cae08e2d65d2eb666761b0dcc |
| @legacy e39e5045ef43883a78c327f56fbd90e501ff3c012c8ddf512b0f7585b75623c1 internal/host/units/carlos-litestream.service external legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Memory cap default 512M with GOMEMLIMIT, and restart backoff (RestartSteps, RestartMaxDelaySec). User, paths, command and sandboxing unchanged; no new source or authority. file:external@0 2fc82f963202103a16a89aaecdcaeb89c6b9fee8e9d0e09cb76389a2326c1714 |
| internal/host/units/carlos-litestream.service external legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Memory cap default 512M with GOMEMLIMIT, and restart backoff (RestartSteps, RestartMaxDelaySec). User, paths, command and sandboxing unchanged; no new source or authority. file:external@0 2fc82f963202103a16a89aaecdcaeb89c6b9fee8e9d0e09cb76389a2326c1714 |
| -@legacy 63b9984a01df41c70dfd5364238390d2a78d6b814b46f03f5f7c25f799d386e7 internal/ops/litestream_memory.go writeMemoryDropIn legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Root reads the root-owned drop-in this verb itself wrote, to decide whether to rewrite it; the same compare-before-write as writeIfChanged under J4 root-input. The path is fixed or an operator-supplied flag. file:ReadFile@146 9ba23f0626a3f649027d809610c6fa0e02584f9e501517de69b9d57767e06d90 |
| -internal/ops/litestream_memory.go writeMemoryDropIn legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Root reads the root-owned drop-in this verb itself wrote, to decide whether to rewrite it; the same compare-before-write as writeIfChanged under J4 root-input. The path is fixed or an operator-supplied flag. file:ReadFile@146 9ba23f0626a3f649027d809610c6fa0e02584f9e501517de69b9d57767e06d90 |
| +@legacy db8b810137dce7ad80730610211e0a8a2262242dacedf00621e2cca9b3818dcf internal/ops/litestream_memory.go writeMemoryDropIn legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Root reads the root-owned drop-in this verb itself wrote, and lstats its own reload-pending marker beside it, to decide whether to rewrite and reload; the same compare-before-write as writeIfChanged under J4 root-input. The path is fixed or an operator-supplied flag. file:Lstat@342,file:ReadFile@183 32b3e20e28898170b36664fe67715582425888f7e90aeaa9c2afb561799b28fe |
| +internal/ops/litestream_memory.go writeMemoryDropIn legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Root reads the root-owned drop-in this verb itself wrote, and lstats its own reload-pending marker beside it, to decide whether to rewrite and reload; the same compare-before-write as writeIfChanged under J4 root-input. The path is fixed or an operator-supplied flag. file:Lstat@342,file:ReadFile@183 32b3e20e28898170b36664fe67715582425888f7e90aeaa9c2afb561799b28fe |
| @legacy dc2175dd0445552045146497dd473327ec0ddca2de728f6b8a2c5c755b82420d internal/ops/ops.go runLitestream legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Same registry open and prior-config read. Adds one root-owned write, the memory drop-in beside the unit, sized from the database count this run computed, and a daemon-reload; no new input is trusted. data-literal@214,file:Open@1383,file:ReadFile@2357 380a01961cc02f2a18673bc4ad0a0bc1112905d4726f8ab9e7b370da36686306 |
| internal/ops/ops.go runLitestream legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Same registry open and prior-config read. Adds one root-owned write, the memory drop-in beside the unit, sized from the database count this run computed, and a daemon-reload; no new input is trusted. data-literal@214,file:Open@1383,file:ReadFile@2357 380a01961cc02f2a18673bc4ad0a0bc1112905d4726f8ab9e7b370da36686306 |
| @legacy cb8030cd13f53803aca45d166742f194f1cb00e1d881263a776b79a334a37189 internal/ops/ops.go systemdDaemonReload legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Root runs a fixed systemctl daemon-reload with no caller input, the same class as internal/host/bootstrap.go systemctl under J4 root-input. file:Command@48 3edadda9e71b806a78d14160f2da0b6091212c4ee55933a327856be811290a74 |
| internal/ops/ops.go systemdDaemonReload legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Root runs a fixed systemctl daemon-reload with no caller input, the same class as internal/host/bootstrap.go systemctl under J4 root-input. file:Command@48 3edadda9e71b806a78d14160f2da0b6091212c4ee55933a327856be811290a74 |
| @coverage internal/console 85d4f9521a2acc08aa1276f414be41cea32aa4275f761546e77e951d0d58b558 |
| @coverage internal/console/templates e622e1e901447dc067aba32e4e520eef4279ffcdd807695f012fd38f646d48b4 |
| -@coverage internal/host 53769084266f3ae76e999ed6f7f01f3f362618535af4bf283c607e41aa51a3be |
| +@coverage internal/host 563e0efbe9bd9de3e4d6844cb6d650d5cd221681fed5594ecd502d45cef68826 |
| @coverage internal/host/units e06e4a1a0d9d45f8e7494f0419834bf23e14fb2cd4861b94ef905bfaf795a7d1 |
| @coverage internal/metrics 1adc51fc485ba895970c22277b7bff997c24ebededbd19f48a10e75cf531323a |
| -@coverage internal/ops 1c36eec5ac001ce556030e557579be93dd082d57d47c5c002ee93bdc44cf12f8 |
| +@coverage internal/ops 31e815c0a685f7ef7013c9295a1f570faff3b6846901c215617832b436ffffc1 |