carlos / platform Public

status: the self-serve block in the private box report: box id, lanes and per-host facts

BoxMetrics gains BoxID (the edges writer's value), Lanes (CARLOS_MOVE_LANES)
and Hosts (status.HostFacts: the row's lifetime, the replica's and the
files' sizes with their collection times, over-cap, truncated and the
strays), each additive and omitempty, each alone attaching the block, none
reaching fleet.json.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev b5d4511e19d608188615120f153b8c71aa2b48a6 parent 61059d4
2 files changed, +106 −8
  • internal/status/snapshot.go +62 −8
  • internal/status/snapshot_test.go +44 −0
diff --git a/internal/status/snapshot.go b/internal/status/snapshot.go
index 4dd9ef44a..6e8d98bfa 100644
--- a/internal/status/snapshot.go
+++ b/internal/status/snapshot.go
@@ -57,11 +57,19 @@ type Probes struct {
// or "" for hardware no cloud owns. It is the console architecture
// page's join key โ€” see BoxMetrics.InstanceID.
InstanceID string
- Region string // operator-set, coarse; "" omits the field
- MemFrac *float64 // 0..1 memory utilization
- DiskFrac *float64 // 0..1 proximity to the disk floor
- Listening func(sock string) *bool // kernel socket table read; nil fn = unknown
- LagSecs *int64 // seconds since litestream last progressed
+ // BoxID is the box id this box's edges file publishes (the edges
+ // writer's value), Lanes the staging buckets CARLOS_MOVE_LANES names, and
+ // Hosts the per-host facts a self-serve move's door reads (self-serve
+ // moves plan 2 Task 6). All three land in BoxMetrics only, never in the
+ // public feed. "" and nil are unknown and publish nothing.
+ BoxID string
+ Lanes []string
+ Hosts map[string]HostFacts
+ Region string // operator-set, coarse; "" omits the field
+ MemFrac *float64 // 0..1 memory utilization
+ DiskFrac *float64 // 0..1 proximity to the disk floor
+ Listening func(sock string) *bool // kernel socket table read; nil fn = unknown
+ LagSecs *int64 // seconds since litestream last progressed
// AdoptRunning is whether release adoption is actually armed on this box
// โ€” the carlos-adopt.timer being ACTIVE, not merely enabled. nil is
// "never asked / couldn't tell", and like every other nil here it must
@@ -512,7 +520,21 @@ type BoxMetrics struct {
// asking the metadata service which machine it is cannot drift from a
// naming convention, because it is not using one.
InstanceID string `json:"instance_id,omitempty"`
- Tunnel string `json:"tunnel,omitempty"` // ""|"connected"|"reconnecting"
+ // BoxID is the box id this box's edges file publishes. A self-serve
+ // move pins its participants by it and by InstanceID, and every gate
+ // checks a pinned participant is still the box that reports under its
+ // label (moves.ParticipantStillPresent). Private like InstanceID.
+ BoxID string `json:"box_id,omitempty"`
+ // Lanes are the staging buckets this box has a lane to
+ // (CARLOS_MOVE_LANES): the self-serve door refuses a move whose target
+ // bucket the source box does not name. Omitted when none is set.
+ Lanes []string `json:"lanes,omitempty"`
+ // Hosts is, per instance this box hosts, what a self-serve move's door
+ // reads before it offers a move: the row's lifetime, the replica's size
+ // and the files' size as the archive would count them (HostFacts).
+ // Hostname-keyed, so private like RouteVersions. Omitted when empty.
+ Hosts map[string]HostFacts `json:"hosts,omitempty"`
+ Tunnel string `json:"tunnel,omitempty"` // ""|"connected"|"reconnecting"
// GeoIP is present on every box running a GeoIP-aware binary (geoip
// spec ยง5.5); its absence is how the rollout verdict tells an older
// binary, which does not strip forged geo headers.
@@ -1045,7 +1067,8 @@ func CollectBox(routes []registry.Route, p Probes, now time.Time) Box {
len(routeVersions) > 0 || len(routeServing) > 0 || len(aliasReadings) > 0 || len(p.Upstreams) > 0 ||
len(routeDBBytes) > 0 || p.Layout != nil || len(p.ConfigRefusals) > 0 || len(p.ReleaseRefusals) > 0 || len(p.ProofWaits) > 0 || p.Rollout != nil || p.ConfigGuard != "" ||
p.Alerts != nil || p.GeoIP != nil || len(p.BuildStates) > 0 || p.BuildStatesErr != "" ||
- p.UpdateSettings != 0 || len(p.Updates) > 0 {
+ p.UpdateSettings != 0 || len(p.Updates) > 0 ||
+ p.BoxID != "" || len(p.Lanes) > 0 || len(p.Hosts) > 0 {
box.Metrics = &BoxMetrics{
ConsoleProcesses: p.ConsoleProcesses,
ActivationID: p.ActivationID, ActivationComplete: p.ActivationComplete,
@@ -1063,7 +1086,11 @@ func CollectBox(routes []registry.Route, p Probes, now time.Time) Box {
Load1: p.Load1, DiskFreeBytes: p.DiskFreeBytes,
Release: p.Release, Tunnel: p.Tunnel, GeoIP: p.GeoIP,
Platform: p.Platform, SystemChannel: p.SystemChannel, System: p.System,
- InstanceID: p.InstanceID,
+ InstanceID: p.InstanceID,
+ BoxID: p.BoxID,
+ Lanes: append([]string(nil), p.Lanes...),
+ // The caller builds a fresh map every tick.
+ Hosts: p.Hosts,
Alerts: p.Alerts,
RouteVersions: routeVersions,
RouteServing: routeServing,
@@ -1155,3 +1182,30 @@ type GeoIPState struct {
Type string `json:"type,omitempty"`
Error string `json:"error,omitempty"`
}
+
+// HostFacts is one instance's facts on the box that hosts it, collected by
+// the agent's status tick for the self-serve move's door (self-serve moves
+// plan 2 Task 6). Times are Unix seconds. CollectedAt is when the facts were
+// collected, ReplicaCollectedAt when the replica listing they count was
+// taken (the listing is taken once per tick for every host). Incarnation is
+// the row's lifetime. FilesBytes and FilesEntries count the files the
+// offline move's archive would carry, by the archive's own rules, without
+// stopping at its cap: OverCap says the count passed the cap, Truncated
+// that the walk stopped at its time budget or at twice the cap before it
+// finished. Strays are the entries the instance keeps beside its files
+// directory. Known is true only when every part was collected; Err says
+// what was not.
+type HostFacts struct {
+ CollectedAt int64 `json:"collected_at"`
+ Known bool `json:"known"`
+ Err string `json:"err,omitempty"`
+ Incarnation uint64 `json:"incarnation"`
+ ReplicaBytes int64 `json:"replica_bytes"`
+ ReplicaObjects int `json:"replica_objects"`
+ ReplicaCollectedAt int64 `json:"replica_collected_at"`
+ FilesBytes int64 `json:"files_bytes"`
+ FilesEntries int `json:"files_entries"`
+ OverCap bool `json:"over_cap,omitempty"`
+ Truncated bool `json:"truncated,omitempty"`
+ Strays []string `json:"strays,omitempty"`
+}
diff --git a/internal/status/snapshot_test.go b/internal/status/snapshot_test.go
index 286e2dfea..3085cd494 100644
--- a/internal/status/snapshot_test.go
+++ b/internal/status/snapshot_test.go
@@ -3,6 +3,7 @@ package status
import (
"context"
"encoding/json"
+ "fmt"
"strings"
"testing"
"time"
@@ -189,3 +190,46 @@ func TestCollectBoxCarriesTheServingBuild(t *testing.T) {
t.Error("a platform alias carries a serving build")
}
}
+
+// TestBoxMetricsCarryTheSelfServeBlock pins self-serve moves plan 2 Task 6's
+// Probes fields (BoxID, Lanes, Hosts): each alone attaches the private block,
+// all reach the wire under their json names, a box with none writes no key
+// for them, and the public fleet feed never carries them.
+func TestBoxMetricsCarryTheSelfServeBlock(t *testing.T) {
+ at := time.Date(2026, 10, 22, 9, 0, 0, 0, time.UTC)
+ hosts := map[string]HostFacts{"shop.example.com": {CollectedAt: at.Unix(), Known: true, Incarnation: 3, ReplicaBytes: 10, ReplicaObjects: 2,
+ ReplicaCollectedAt: at.Unix(), FilesBytes: 7, FilesEntries: 1, Strays: []string{"/data/shop.example.com-blobs"}}}
+ for _, p := range []Probes{{BoxID: "uk-a"}, {Lanes: []string{"data-uk"}}, {Hosts: hosts}} {
+ if CollectBox(nil, p, at).Metrics == nil {
+ t.Fatalf("%+v alone attaches no metrics block", p)
+ }
+ }
+ b := CollectBox(nil, Probes{BoxID: "uk-a", InstanceID: "i-1", Lanes: []string{"data-uk"}, Hosts: hosts}, at)
+ out, _ := json.Marshal(b.Metrics)
+ for _, want := range []string{`"box_id":"uk-a"`, `"instance_id":"i-1"`, `"lanes":["data-uk"]`, `"hosts":{"shop.example.com":`, `"incarnation":3`,
+ `"replica_collected_at":` + fmt.Sprint(at.Unix()), `"files_entries":1`, `"strays":["/data/shop.example.com-blobs"]`} {
+ if !strings.Contains(string(out), want) {
+ t.Errorf("metrics json lacks %s: %s", want, out)
+ }
+ }
+ none, _ := json.Marshal(CollectBox(nil, Probes{Release: "v1"}, at).Metrics)
+ for _, k := range []string{"box_id", "lanes", "hosts"} {
+ if strings.Contains(string(none), `"`+k+`"`) {
+ t.Errorf("a box without the block writes %s: %s", k, none)
+ }
+ }
+ s := release.NewDirStore(t.TempDir())
+ if err := WriteBox(context.Background(), s, b); err != nil {
+ t.Fatal(err)
+ }
+ f, err := BuildFleet(context.Background(), s, nil, at, 5*time.Minute)
+ if err != nil {
+ t.Fatal(err)
+ }
+ fb, _ := json.Marshal(f)
+ for _, leak := range []string{"shop.example.com", "data-uk", "i-1", "box_id"} {
+ if strings.Contains(string(fb), leak) {
+ t.Fatalf("fleet.json leaked %s: %s", leak, fb)
+ }
+ }
+}