carlos / platform Public

status: additive raw Metrics block on Box — private to the bucket, never published to fleet.json

Adds Box.Metrics (*BoxMetrics: mem total/avail, load1, disk free, release,
tunnel state) as the one deliberate, documented exception to this package's
band-everything privacy rule. CollectBox attaches it only when at least one
field is known, mirroring the "unknown stays absent" rule every other Box
field follows, so an old parser and a can't-answer box are indistinguishable
on the wire. Excluded from fleet.json by construction (FleetBox copies Box's
fields one at a time and never names Metrics) — dryrun.go's leak scan now
proves it, requiring the raw fixture values inside status/boxes/*.json and
forbidding them everywhere else DryRun publishes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 13b6aa208cd62d51ff92871443cf3bab3295f5e1 parent e257656
3 files changed, +201 −3
  • internal/status/dryrun.go +64 −1
  • internal/status/snapshot.go +79 −2
  • internal/status/status_test.go +58 −0
diff --git a/internal/status/dryrun.go b/internal/status/dryrun.go
index 288254cab..4c46cbc26 100644
--- a/internal/status/dryrun.go
+++ b/internal/status/dryrun.go
@@ -46,6 +46,25 @@ var dryRunNeedles = []string{
"0.95", // fixture DiskFrac, verbatim
}
+// dryRunMetricsNeedles are the distinctive raw values Task 5's BoxMetrics
+// carries — real numbers and strings that Box.Metrics is the ONE deliberate
+// exception privileged to publish (see snapshot.go's package doc), and which
+// therefore MUST appear in the published box object. Unlike dryRunNeedles
+// above (banned everywhere, box object included), these are allowed inside
+// status/boxes/*.json and banned only from everything else DryRun publishes
+// — see the scope split in DryRun's scan loop below. Each value is
+// deliberately distinctive (the repeated 42s, a value with no legitimate
+// reason to appear in a template or a fleet-wide aggregate) so a false
+// negative from an incidental digit collision is effectively impossible.
+var dryRunMetricsNeedles = []string{
+ "4242424242", // fixture MemTotalBytes, verbatim
+ "4242424241", // fixture MemAvailBytes, verbatim
+ "4242424240", // fixture DiskFreeBytes, verbatim
+ "42.42", // fixture Load1, verbatim
+ "dryrun4242sha", // fixture Release, verbatim
+ "reconnecting", // fixture Tunnel, verbatim (a real, valid Tunnel value)
+}
+
// ScanForLeaks reports which of banned's strings appear anywhere in j. It is
// the single implementation both DryRun's self-check and
// contract_test.go's scanForLeaks test helper run — the test package can't
@@ -89,6 +108,14 @@ func dryRunFixtures() ([]registry.Route, Probes) {
lag := int64(120)
adopting := true // a known reading, so adoption_running publishes non-null and gets scanned
expiredCanaries := 2 // ditto for expired_canaries — a known count, not the unexercised nil path
+
+ // Task 5's raw Metrics fixtures: distinctive values (see
+ // dryRunMetricsNeedles) that MUST land in the published box object and
+ // MUST NOT land anywhere else DryRun publishes — the positive half of
+ // proving Box.Metrics's private exception actually stays private.
+ memTotal, memAvail, diskFree := int64(4242424242), int64(4242424241), int64(4242424240)
+ load1 := 42.42
+
probes := Probes{
MachineID: "ip-10-0-0-7",
Region: "us-east-1",
@@ -99,7 +126,13 @@ func dryRunFixtures() ([]registry.Route, Probes) {
ExpiredCanaries: &expiredCanaries,
// Every hibernating route reports "listening" so Awake/Asleep publish
// non-null — an unexercised null field is a field DryRun can't scan.
- Listening: func(string) *bool { v := true; return &v },
+ Listening: func(string) *bool { v := true; return &v },
+ MemTotalBytes: &memTotal,
+ MemAvailBytes: &memAvail,
+ Load1: &load1,
+ DiskFreeBytes: &diskFree,
+ Release: "dryrun4242sha",
+ Tunnel: "reconnecting",
}
return routes, probes
}
@@ -198,6 +231,13 @@ func DryRun(dir string, out io.Writer) error {
return fmt.Errorf("dry run: nothing published under status/ — publish path is broken")
}
+ // boxKeySeen tracks whether the scan actually reached a status/boxes/
+ // object carrying the Metrics fixtures — the positive half of the
+ // Metrics scope check below, the same reason appPublished exists for
+ // the opt-in app: a leak-scan that finds nothing proves nothing about
+ // whether the thing that SHOULD be there actually is.
+ boxKeySeen := false
+
for _, key := range keys {
body, _, err := s.Get(ctx, key)
if err != nil {
@@ -209,6 +249,25 @@ func DryRun(dir string, out io.Writer) error {
return fmt.Errorf("dry run: %s leaked %v", key, leaks)
}
+ // dryRunMetricsNeedles are scoped: status/boxes/*.json is Box.Metrics's
+ // one privileged, private home (see snapshot.go's package doc), so
+ // the raw fixture values are EXPECTED there and must NOT be scanned
+ // out — that isn't a leak, it's the field doing its job. Every other
+ // key DryRun publishes (fleet.json, index.html, status.css) is
+ // exactly what BuildFleet/PublishFleet expose beyond the bucket, so
+ // those raw values must never appear there.
+ if strings.HasPrefix(key, "status/boxes/") {
+ if leaks := ScanForLeaks(body, dryRunMetricsNeedles); len(leaks) == 0 {
+ // None of the fixtures made it in at all — Metrics isn't
+ // being attached, which means this check isn't exercising
+ // anything (a false "no leak").
+ return fmt.Errorf("dry run: %s: expected raw Metrics fixtures %v, found none — Box.Metrics not attached?", key, dryRunMetricsNeedles)
+ }
+ boxKeySeen = true
+ } else if leaks := ScanForLeaks(body, dryRunMetricsNeedles); len(leaks) > 0 {
+ return fmt.Errorf("dry run: %s leaked raw Metrics fixture %v — Box.Metrics must never reach the public feed", key, leaks)
+ }
+
// Page assets (index.html, status.css) aren't JSON and simply fail
// to decode here — silently skipped, since the band-digit check
// only means anything for objects that actually carry Load/Capacity
@@ -221,6 +280,10 @@ func DryRun(dir string, out io.Writer) error {
}
}
+ if !boxKeySeen {
+ return fmt.Errorf("dry run: no status/boxes/*.json object was scanned — Metrics scope check never ran")
+ }
+
fmt.Fprintln(out, "dry-run: no leaks")
return nil
}
diff --git a/internal/status/snapshot.go b/internal/status/snapshot.go
index 378e55f4d..b8afea2a3 100644
--- a/internal/status/snapshot.go
+++ b/internal/status/snapshot.go
@@ -4,6 +4,23 @@
// HERE, in the process that does the counting and banding, not downstream —
// a merge step or a page template can only leak what this package already
// let through.
+//
+// Box.Metrics is the one deliberate exception to that rule, not a hole in
+// it: a raw, unbanded block (real byte counts, a load average, the running
+// build, tunnel state) that CollectBox attaches to the Box it returns, which
+// WriteBox lands in status/boxes/<label>.json alongside everything else this
+// package computes. That file is bucket-private — the deployment bucket
+// blocks all public access (infra/modules/deployment-buckets), and the edge
+// process's fixed three-object menu (internal/edge/status.go) only ever
+// serves "/", "/fleet.json", and "/status.css"; a request for
+// status/boxes/<label>.json 404s the same as any other unlisted path. Only
+// fleet-status machinery (loadBoxes in fleet.go) and operators with bucket
+// credentials ever read it. The public merge step, fleet.go's BuildFleet,
+// copies Box's fields into FleetBox one at a time by field name and simply
+// never names Metrics — so the banded-words-only commitment on fleet.json
+// holds by construction, not by a filter that could be forgotten. See
+// dryrun.go's leak fixtures, which assert a raw metric value survives into
+// the box object but never into fleet.json or index.html.
package status
import (
@@ -43,6 +60,37 @@ type Probes struct {
// zero; 0 is itself a real, confident answer when every canary route on
// the box is fresh, or there are none at all.
ExpiredCanaries *int
+
+ // MemTotalBytes, MemAvailBytes, Load1, and DiskFreeBytes are the raw
+ // numbers MemFrac/DiskFrac above are already computed FROM — CollectBox
+ // still bands MemFrac/DiskFrac into Load/Capacity as always, but also
+ // carries these through unbanded into Box.Metrics, the package's one
+ // documented private exception (see the package doc comment). nil means
+ // unknown, same as every other pointer field here.
+ MemTotalBytes *int64
+ MemAvailBytes *int64
+ Load1 *float64
+ DiskFreeBytes *int64
+ // Release is the running binary's version.Build (a short sha or similar),
+ // and Tunnel is one of ""|"connected"|"reconnecting" — both land in
+ // Box.Metrics unbanded, like the four numeric fields above. "" means
+ // unknown/not-applicable for both, never a guess.
+ Release string
+ Tunnel string
+}
+
+// BoxMetrics is the raw, PRIVATE block: real numbers, never banded. It lives
+// in status/boxes/<label>.json — which only fleet-status machinery and
+// operators read — and is deliberately NOT copied into FleetBox, so the
+// publicly served fleet.json keeps the banded-words-only commitment. See the
+// package doc comment for why this block is safe to exist at all.
+type BoxMetrics struct {
+ MemTotalBytes *int64 `json:"mem_total_bytes,omitempty"`
+ MemAvailBytes *int64 `json:"mem_avail_bytes,omitempty"`
+ Load1 *float64 `json:"load1,omitempty"`
+ DiskFreeBytes *int64 `json:"disk_free_bytes,omitempty"`
+ Release string `json:"release,omitempty"` // running binary's version.Build
+ Tunnel string `json:"tunnel,omitempty"` // ""|"connected"|"reconnecting"
}
// RouteCounts is aggregate-only: no host, no socket path, just how many.
@@ -68,7 +116,14 @@ type Replication struct {
// Box is one box's self-report: what CollectBox produces and WriteBox
// serializes. Every field here has already had its needles pulled — there is
-// no hostname, machine id, or raw ratio anywhere in this type.
+// no hostname, machine id, or raw ratio anywhere in this type — with exactly
+// one deliberate exception: Metrics, the raw/private block documented on the
+// package comment and on BoxMetrics itself. Every OTHER field on Box is
+// still bound by the no-raw-numbers rule; Metrics is not "one more field",
+// it's the one place that rule is intentionally suspended, gated by staying
+// bucket-private (see the package doc) and excluded from FleetBox by
+// construction (fleet.go's BuildFleet copies fields one at a time and never
+// names Metrics).
type Box struct {
GeneratedAt time.Time `json:"generated_at"`
Label string `json:"label"`
@@ -91,6 +146,12 @@ type Box struct {
// is exactly the kind of identifying detail this package's whole design
// refuses to carry, but how many is aggregate and safe.
ExpiredCanaries *int `json:"expired_canaries"`
+ // Metrics is the raw/private exception documented above and on
+ // BoxMetrics — omitted entirely (not a present-but-empty object) when
+ // CollectBox has nothing to report, so an old parser reading a box this
+ // package wrote before Metrics existed, and a box that genuinely can't
+ // answer any of the six fields, produce the identical absent-key shape.
+ Metrics *BoxMetrics `json:"metrics,omitempty"`
}
// replHealthyWithin is the lag under which replication counts as healthy.
@@ -246,7 +307,7 @@ func CollectBox(routes []registry.Route, p Probes, now time.Time) Box {
healthy = &h
}
- return Box{
+ box := Box{
GeneratedAt: now,
Label: Label(p.MachineID),
Region: p.Region,
@@ -257,6 +318,22 @@ func CollectBox(routes []registry.Route, p Probes, now time.Time) Box {
AdoptionRunning: p.AdoptRunning,
ExpiredCanaries: p.ExpiredCanaries,
}
+
+ // Metrics attaches only when something is known — an all-unknown Probes
+ // publishes no block at all, the same "unknown stays absent, never a
+ // guess" rule every other field on Box follows, and it keeps a box that
+ // can't answer any of the six fields indistinguishable on the wire from
+ // a box running code that predates Metrics entirely.
+ if p.MemTotalBytes != nil || p.MemAvailBytes != nil || p.Load1 != nil ||
+ p.DiskFreeBytes != nil || p.Release != "" || p.Tunnel != "" {
+ box.Metrics = &BoxMetrics{
+ MemTotalBytes: p.MemTotalBytes, MemAvailBytes: p.MemAvailBytes,
+ Load1: p.Load1, DiskFreeBytes: p.DiskFreeBytes,
+ Release: p.Release, Tunnel: p.Tunnel,
+ }
+ }
+
+ return box
}
// WriteBox marshals a Box indented (human-diffable in the bucket) and writes
diff --git a/internal/status/status_test.go b/internal/status/status_test.go
index f38b6d0ab..b3d907a25 100644
--- a/internal/status/status_test.go
+++ b/internal/status/status_test.go
@@ -215,6 +215,64 @@ func TestWriteBoxRetriesOnceOnCASConflict(t *testing.T) {
}
}
+// ---- Task 5: raw Metrics block (private to the bucket) --------------------
+
+// TestCollectBoxCarriesRawMetrics proves CollectBox attaches an unbanded
+// Metrics block straight through from Probes when the six fields are known —
+// the one deliberate exception to this package's band-everything rule (see
+// snapshot.go's package doc).
+func TestCollectBoxCarriesRawMetrics(t *testing.T) {
+ mt, ma, df := int64(4056547328), int64(2028273664), int64(9999999)
+ l1 := 0.42
+ b := status.CollectBox(nil, status.Probes{
+ MachineID: "m", MemTotalBytes: &mt, MemAvailBytes: &ma, Load1: &l1, DiskFreeBytes: &df,
+ Release: "abc1234", Tunnel: "connected",
+ }, time.Now())
+ m := b.Metrics
+ if m == nil || *m.MemTotalBytes != mt || *m.Load1 != l1 || m.Release != "abc1234" || m.Tunnel != "connected" {
+ t.Fatalf("metrics block wrong: %+v", m)
+ }
+}
+
+// TestCollectBoxOmitsMetricsWhenAllUnknown proves the all-unknown case
+// really omits the block rather than attaching an empty-but-present
+// BoxMetrics{} — the same "unknown stays absent" rule every other Box field
+// follows, and what makes an old parser's absent-key shape and a
+// can't-answer box's shape identical on the wire.
+func TestCollectBoxOmitsMetricsWhenAllUnknown(t *testing.T) {
+ b := status.CollectBox(nil, status.Probes{MachineID: "m"}, time.Now())
+ if b.Metrics != nil {
+ t.Fatalf("metrics block should be nil when nothing is known, got %+v", b.Metrics)
+ }
+}
+
+// TestBoxBackCompatParse: yesterday's Box shape (no metrics key) must parse
+// today, and a FUTURE box's unknown keys (inside or alongside metrics) must
+// not break today's parse either — additive wire shape, per CLAUDE.md's
+// "consumer = everything that PARSES the shape" rule.
+func TestBoxBackCompatParse(t *testing.T) {
+ yesterday := []byte(`{"generated_at":"2026-08-03T10:00:00Z","label":"ada-stargazing",
+ "routes":{"static":1,"resident":2,"hibernating":3,"awake":null,"asleep":null},
+ "load":"comfortable","capacity":"plenty",
+ "replication":{"healthy":true,"lag_seconds":5},
+ "adoption_running":true,"expired_canaries":0}`)
+ var b status.Box
+ if err := json.Unmarshal(yesterday, &b); err != nil {
+ t.Fatalf("yesterday's shape failed to parse: %v", err)
+ }
+ if b.Metrics != nil {
+ t.Fatal("absent metrics must decode as nil, not zero-value")
+ }
+ tomorrow := []byte(`{"generated_at":"2026-08-05T10:00:00Z","label":"ada-stargazing",
+ "routes":{"static":0,"resident":0,"hibernating":0,"awake":null,"asleep":null},
+ "load":null,"capacity":null,"replication":{"healthy":null,"lag_seconds":null},
+ "adoption_running":null,"expired_canaries":null,
+ "metrics":{"mem_total_bytes":1,"some_future_field":true}}`)
+ if err := json.Unmarshal(tomorrow, &b); err != nil {
+ t.Fatalf("tomorrow's shape failed to parse: %v", err)
+ }
+}
+
// ---- Task 3: fleet merge + publisher --------------------------------------
// teststore is a fresh DirStore over a scratch directory — every fleet test