carlos / platform Public

host: fleet boxes heartbeat under their fleet prefix and never publish fleet.json

Task 5 of the customer-fleets plan. CARLOS_REGION's fleet/<name> placement
already adopts via reconcile.go's existing string-equality Region filter
(pinned with a new provision test, zero code change). CARLOS_FLEET marks an
agent as a fleet box: shouldSkipPublisherElection short-circuits statusLoop
before lease.Acquire is ever called, so a fleet box never contends for the
publish lease and never runs BuildFleet/PublishFleet, while statusTick's
unconditional WriteBox still self-reports every tick.

CARLOS_STATUS_PREFIX previously only namespaced the lease key; it now also
rebases every key the tick's store touches (prefixStore/newPrefixStore), so
a fleet box's own snapshot actually lands at
fleets/<name>/status/boxes/<label>.json instead of colliding with the
shared status/boxes/ path platform boxes use (they share a bucket per the
design's Trust section). Empty prefix is a pure passthrough - no behavior
change for existing deployments.

A new status contract test pins that BuildFleet's loadBoxes, which lists
exactly "status/boxes/", never picks up a fleet-prefixed snapshot -
owner-only visibility by construction, no allowlist to bypass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell cb728ff8e4d7626b8ae28efbe1e3cbef58920071 parent 0a40b94
5 files changed, +370 −28
  • README.md +26 −13
  • internal/host/agent.go +131 −15
  • internal/host/host_test.go +123 −0
  • internal/provision/reconcile_test.go +41 −0
  • internal/status/contract_test.go +49 −0
diff --git a/README.md b/README.md
index d02050170..3ec23b5eb 100644
--- a/README.md
+++ b/README.md
@@ -335,19 +335,32 @@ no single reader owns the whole contract:
- `CARLOS_STATUS_BUCKET` / `CARLOS_STATUS_PREFIX` / `CARLOS_STATUS_REGION`
— read by `carlos agent`'s status loop: the bucket each box writes its
snapshot to and, when it wins the lease, publishes the merged feed to;
- prefix and region also feed the cross-box status-lease election. **The
- serving side (the edge's `kind=status` route and the console's operator
- view) must be pointed at this same bucket** — nothing enforces that
- agreement automatically, publishing and serving are separately
- configured. To make the common case foolproof, an unset
- `CARLOS_STATUS_BUCKET` defaults to `CARLOS_DEPLOYMENT_BUCKET` (an
- explicit `CARLOS_STATUS_BUCKET` still always wins), so a single-bucket
- deployment needs only one env var for both release adoption and status.
- Both unset disables the whole loop — inert until configured, the same
- convention litestream replication uses. **Honest limitation**: that
- default only ever resolves to an S3 bucket — a deployment adopting from
- `CARLOS_DEPLOYMENT_DIR` (no bucket at all) does not publish fleet status
- yet; give it its own `CARLOS_STATUS_BUCKET` if it wants the page.
+ region also feeds the cross-box status-lease election. **The serving side
+ (the edge's `kind=status` route and the console's operator view) must be
+ pointed at this same bucket** — nothing enforces that agreement
+ automatically, publishing and serving are separately configured. To make
+ the common case foolproof, an unset `CARLOS_STATUS_BUCKET` defaults to
+ `CARLOS_DEPLOYMENT_BUCKET` (an explicit `CARLOS_STATUS_BUCKET` still
+ always wins), so a single-bucket deployment needs only one env var for
+ both release adoption and status. Both unset disables the whole loop —
+ inert until configured, the same convention litestream replication uses.
+ **Honest limitation**: that default only ever resolves to an S3 bucket —
+ a deployment adopting from `CARLOS_DEPLOYMENT_DIR` (no bucket at all)
+ does not publish fleet status yet; give it its own `CARLOS_STATUS_BUCKET`
+ if it wants the page. `CARLOS_STATUS_PREFIX` does double duty: it
+ namespaces the status-lease key, AND (customer-fleets plan, Task 5)
+ rebases every key the tick's store touches — a fleet box set to
+ `CARLOS_STATUS_PREFIX=fleets/<name>/` writes its own snapshot at
+ `fleets/<name>/status/boxes/<label>.json` instead of the shared
+ `status/boxes/` path, so it never collides with (or leaks into) the
+ platform's own `fleet.json`. Empty (every platform box today) is a pure
+ passthrough.
+- `CARLOS_FLEET` — read by `carlos agent`'s status loop (customer-fleets
+ plan, Task 5). Set (to the fleet's name) marks this agent as a fleet box:
+ it still self-reports its own snapshot every tick, but never contends for
+ the cross-box publish lease and never merges/publishes `status/fleet.json`
+ — that feed belongs to the platform's own boxes only. Unset (every
+ platform box today) is unaffected.
- `STATUS_BOX_REGION` — also read by `carlos agent`'s status loop: an
operator-set, coarse region word (e.g. `"EU · Ireland"`) carried onto
this box's snapshot verbatim; unset omits the field. Free text the
diff --git a/internal/host/agent.go b/internal/host/agent.go
index 53743493c..2327766fe 100644
--- a/internal/host/agent.go
+++ b/internal/host/agent.go
@@ -27,10 +27,24 @@
// the loop never starts, logging one line and returning — the same
// "inert until configured" convention adoptLoop uses for
// CARLOS_DEPLOYMENT_BUCKET/_DIR, and litestream's own env-gated
-// replication before that. CARLOS_STATUS_PREFIX and CARLOS_STATUS_REGION
-// feed lease.Acquire (the single-writer election for the publish half
-// of each tick); both may be empty (region falls back to the AWS
-// default chain).
+// replication before that. CARLOS_STATUS_REGION feeds lease.Acquire
+// (the single-writer election for the publish half of each tick); it
+// may be empty (falls back to the AWS default chain). CARLOS_STATUS_PREFIX
+// does double duty: it namespaces that same lease key (path.Join(prefix,
+// "status-lease")) AND, since Task 5 of the customer-fleets plan,
+// rebases every key statusTick's store touches (newPrefixStore) — a
+// fleet box set to CARLOS_STATUS_PREFIX=fleets/<name>/ writes its own
+// snapshot at fleets/<name>/status/boxes/<label>.json instead of the
+// shared status/boxes/ path every platform box also writes, so its
+// heartbeat never collides with (or leaks into) the platform's own
+// fleet.json. Empty (every platform box today) is a pure passthrough.
+// - CARLOS_FLEET — read by statusLoop (Task 5, customer-fleets plan). Set
+// (to the fleet's name — its value is otherwise unused here) marks this
+// agent as a fleet box: shouldSkipPublisherElection short-circuits
+// publish to false BEFORE lease.Acquire is ever called, so a fleet box
+// never contends for the cross-box publish lease and never runs
+// BuildFleet/PublishFleet — it still self-reports via statusTick's
+// unconditional WriteBox, just under its own CARLOS_STATUS_PREFIX.
// HONEST LIMIT: the CARLOS_DEPLOYMENT_BUCKET fallback only ever
// resolves to an S3 bucket. A deployment adopting from
// CARLOS_DEPLOYMENT_DIR (a plain directory — tests, or a single-box
@@ -947,6 +961,92 @@ func ResolveStatusBucket(getenv func(string) string) string {
return getenv("CARLOS_DEPLOYMENT_BUCKET")
}
+// shouldSkipPublisherElection reports whether this tick must never contend
+// for the cross-box publish lease at all — true exactly when CARLOS_FLEET is
+// set (Task 5, customer-fleets plan): a fleet box self-reports its own Box
+// snapshot every tick (statusTick's WriteBox call is unconditional) but must
+// never win — or even attempt — the election that would have it merge and
+// publish status/fleet.json, which is the platform's own boxes' shared feed.
+// Checked BEFORE lease.Acquire is ever called, not after: a fleet box never
+// makes the network call, not merely one that's discarded on return.
+func shouldSkipPublisherElection(getenv func(string) string) bool {
+ return getenv("CARLOS_FLEET") != ""
+}
+
+// prefixStore rebases every key a caller works with onto a fixed base
+// (CARLOS_STATUS_PREFIX's actual enforcement point, Task 5 of the
+// customer-fleets plan) before it ever reaches the underlying store. A
+// fleet box's own snapshot is written through one of these with prefix
+// "fleets/<name>/", so status.WriteBox's hardcoded "status/boxes/<label>.json"
+// key lands at "fleets/<name>/status/boxes/<label>.json" — outside the exact
+// "status/boxes/" prefix status.BuildFleet's loadBoxes lists, and so
+// invisible to the platform's own fleet.json by construction (pinned by
+// internal/status's contract test), with no change needed to the status
+// package itself. List/ListDirs strip the base back off their results, so a
+// key List hands back still round-trips through this SAME wrapper's Get/Put
+// without doubling the prefix.
+type prefixStore struct {
+ release.Store
+ base string // always "" or ends in "/"
+}
+
+// newPrefixStore wraps s so every key is rebased under prefix. An empty
+// prefix returns s unchanged — every platform box today leaves
+// CARLOS_STATUS_PREFIX unset, and this must be a pure no-op for them.
+func newPrefixStore(s release.Store, prefix string) release.Store {
+ if prefix == "" {
+ return s
+ }
+ if !strings.HasSuffix(prefix, "/") {
+ prefix += "/"
+ }
+ return prefixStore{Store: s, base: prefix}
+}
+
+func (p prefixStore) full(key string) string { return p.base + key }
+
+func (p prefixStore) Get(ctx context.Context, key string) ([]byte, string, error) {
+ return p.Store.Get(ctx, p.full(key))
+}
+
+func (p prefixStore) Put(ctx context.Context, key string, body []byte, ifMatch string) (string, error) {
+ return p.Store.Put(ctx, p.full(key), body, ifMatch)
+}
+
+func (p prefixStore) Delete(ctx context.Context, key string) error {
+ return p.Store.Delete(ctx, p.full(key))
+}
+
+// List rebases the query prefix the same way Get/Put rebase a key, then
+// strips its own base back off every result — the underlying store's keys
+// always start with p.base (that's what "queried under p.full(prefix)"
+// guarantees), so TrimPrefix here exactly undoes full() above, and the
+// returned keys round-trip through this wrapper's Get unchanged.
+func (p prefixStore) List(ctx context.Context, prefix string) ([]string, error) {
+ keys, err := p.Store.List(ctx, p.full(prefix))
+ if err != nil {
+ return nil, err
+ }
+ out := make([]string, len(keys))
+ for i, k := range keys {
+ out[i] = strings.TrimPrefix(k, p.base)
+ }
+ return out, nil
+}
+
+// ListDirs mirrors List's rebase-then-strip, for the delimiter-aware listing.
+func (p prefixStore) ListDirs(ctx context.Context, prefix string) ([]string, error) {
+ dirs, err := p.Store.ListDirs(ctx, p.full(prefix))
+ if err != nil {
+ return nil, err
+ }
+ out := make([]string, len(dirs))
+ for i, d := range dirs {
+ out[i] = strings.TrimPrefix(d, p.base)
+ }
+ return out, nil
+}
+
// statusLoop runs the fleet status page's 5-minute tick, forever, once armed
// — see the package doc comment above for the full env contract. Missing
// CARLOS_STATUS_BUCKET disables the loop with one log line, the same
@@ -977,12 +1077,18 @@ func statusLoop(reg *registry.Registry) {
prefix := os.Getenv("CARLOS_STATUS_PREFIX")
region := os.Getenv("CARLOS_STATUS_REGION")
boxRegion := os.Getenv("STATUS_BOX_REGION")
+ skipElection := shouldSkipPublisherElection(os.Getenv)
s, err := release.NewS3Store(ctx, bucket)
if err != nil {
log.Printf("agent: status loop disabled: %v", err)
return
}
+ // store is what statusTick actually reads/writes through: identical to s
+ // (a pure passthrough) unless CARLOS_STATUS_PREFIX is set, in which case
+ // every key it touches is rebased under that prefix — see newPrefixStore
+ // and the package doc comment's CARLOS_STATUS_PREFIX entry.
+ store := newPrefixStore(s, prefix)
// The effective bucket is worth a log line on its own: it's easy to set
// CARLOS_DEPLOYMENT_BUCKET and CARLOS_STATUS_BUCKET to different values
// by accident (or on purpose) and end up with the agent writing status
@@ -1068,20 +1174,30 @@ func statusLoop(reg *registry.Registry) {
Tunnel: tunnelState(),
}
- // path.Join (an S3 key, not a filesystem path — matches
- // activator.go's Config.hibernatePrefix convention) rather than a
- // bare "+" concat: guards against a trailing-slash
- // CARLOS_STATUS_PREFIX producing a doubled "//status-lease" key.
- held, acqErr := lease.Acquire(ctx, bucket, path.Join(prefix, "status-lease"), region)
- publish := acqErr == nil
- if acqErr != nil {
- var heldErr *lease.HeldError
- if !errors.As(acqErr, &heldErr) {
- log.Printf("agent: status tick: lease acquire: %v", acqErr)
+ // A fleet box (CARLOS_FLEET set) never gets here as a contender: it
+ // self-reports only, never attempting the election at all — no
+ // lease.Acquire call, no BuildFleet, no PublishFleet. See
+ // shouldSkipPublisherElection and the package doc comment's
+ // CARLOS_FLEET entry.
+ var held *lease.Held
+ publish := false
+ if !skipElection {
+ // path.Join (an S3 key, not a filesystem path — matches
+ // activator.go's Config.hibernatePrefix convention) rather than a
+ // bare "+" concat: guards against a trailing-slash
+ // CARLOS_STATUS_PREFIX producing a doubled "//status-lease" key.
+ var acqErr error
+ held, acqErr = lease.Acquire(ctx, bucket, path.Join(prefix, "status-lease"), region)
+ publish = acqErr == nil
+ if acqErr != nil {
+ var heldErr *lease.HeldError
+ if !errors.As(acqErr, &heldErr) {
+ log.Printf("agent: status tick: lease acquire: %v", acqErr)
+ }
}
}
- if err := statusTick(ctx, s, reg, p, publish); err != nil {
+ if err := statusTick(ctx, store, reg, p, publish); err != nil {
log.Printf("agent: status tick: %v", err)
}
diff --git a/internal/host/host_test.go b/internal/host/host_test.go
index fcca235b5..68e9343aa 100644
--- a/internal/host/host_test.go
+++ b/internal/host/host_test.go
@@ -304,6 +304,129 @@ func TestStatusTickWritesBoxAndPublishesOnlyWhenAsked(t *testing.T) {
}
}
+// ---- Task 5 (customer-fleets plan): publisher guard + fleet-prefixed store -
+
+// TestShouldSkipPublisherElection pins the CARLOS_FLEET contract: a fleet
+// box (CARLOS_FLEET set to anything non-empty) must skip the publisher
+// election, and a platform box (unset, the every-deployment-today case)
+// must not. statusLoop checks this BEFORE calling lease.Acquire — see the
+// call site — so this also documents that the decision needs no lease, no
+// bucket, no network at all: it's a pure function of one env var.
+func TestShouldSkipPublisherElection(t *testing.T) {
+ env := func(m map[string]string) func(string) string {
+ return func(k string) string { return m[k] }
+ }
+ cases := []struct {
+ name string
+ env map[string]string
+ want bool
+ }{
+ {"fleet box skips the election", map[string]string{"CARLOS_FLEET": "pi"}, true},
+ {"platform box still elects", map[string]string{}, false},
+ {"empty CARLOS_FLEET is not set", map[string]string{"CARLOS_FLEET": ""}, false},
+ }
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ if got := shouldSkipPublisherElection(env(c.env)); got != c.want {
+ t.Errorf("shouldSkipPublisherElection(%v) = %v, want %v", c.env, got, c.want)
+ }
+ })
+ }
+}
+
+// TestPrefixStoreRebasesKeys pins newPrefixStore's own contract in
+// isolation, before the fleet-heartbeat integration test below composes it
+// with statusTick: Put/Get/List/ListDirs all operate in "unprefixed" key
+// space from the caller's side, while the underlying store actually holds
+// everything under the fixed base — and a key List hands back must
+// round-trip through the SAME wrapper's Get without doubling the prefix.
+func TestPrefixStoreRebasesKeys(t *testing.T) {
+ ctx := context.Background()
+ under := release.NewDirStore(t.TempDir())
+ wrapped := newPrefixStore(under, "fleets/pi/")
+
+ if _, err := wrapped.Put(ctx, "status/boxes/pi-box.json", []byte(`{"ok":true}`), ""); err != nil {
+ t.Fatal(err)
+ }
+
+ // The underlying store must hold the REBASED key, not the caller's one.
+ if _, _, err := under.Get(ctx, "fleets/pi/status/boxes/pi-box.json"); err != nil {
+ t.Errorf("underlying store missing rebased key: %v", err)
+ }
+ if _, _, err := under.Get(ctx, "status/boxes/pi-box.json"); err == nil {
+ t.Error("underlying store must not hold the unrebased key")
+ }
+
+ // List, queried in the caller's unprefixed space, must find it and hand
+ // back a key that still round-trips through the SAME wrapper's Get.
+ keys, err := wrapped.List(ctx, "status/boxes/")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(keys) != 1 || keys[0] != "status/boxes/pi-box.json" {
+ t.Fatalf("List = %v, want exactly [status/boxes/pi-box.json]", keys)
+ }
+ if body, _, err := wrapped.Get(ctx, keys[0]); err != nil || string(body) != `{"ok":true}` {
+ t.Errorf("Get(%q) via the wrapper = %q, %v", keys[0], body, err)
+ }
+
+ // An empty prefix must be a pure passthrough: the same store, unwrapped.
+ if s := newPrefixStore(under, ""); s != release.Store(under) {
+ t.Errorf("newPrefixStore with empty prefix = %v, want the underlying store unchanged", s)
+ }
+}
+
+// TestFleetBoxHeartbeatLandsUnderFleetPrefixNeverPublishes is the end-to-end
+// pin for Task 5's whole contract, composing the pieces the two tests above
+// verify in isolation: a fleet box's tick (store wrapped in
+// CARLOS_STATUS_PREFIX="fleets/pi/", publish forced false the way
+// shouldSkipPublisherElection drives statusLoop) writes ITS OWN Box
+// snapshot at "fleets/pi/status/boxes/<label>.json" in the underlying
+// bucket — never at the shared "status/boxes/<label>.json" a platform box
+// uses, and never status/fleet.json either.
+func TestFleetBoxHeartbeatLandsUnderFleetPrefixNeverPublishes(t *testing.T) {
+ reg, err := registry.Open(filepath.Join(t.TempDir(), "registry.db"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := reg.Upsert(registry.Route{Host: "a.example.com", Kind: "static"}); err != nil {
+ t.Fatal(err)
+ }
+
+ under := release.NewDirStore(t.TempDir())
+ fleetStore := newPrefixStore(under, "fleets/pi/")
+ ctx := context.Background()
+ probes := status.Probes{MachineID: "pi-box"}
+
+ // publish=false is what shouldSkipPublisherElection forces statusLoop
+ // into for a fleet box — lease.Acquire is never even called.
+ if err := statusTick(ctx, fleetStore, reg, probes, false); err != nil {
+ t.Fatalf("statusTick: %v", err)
+ }
+
+ boxes, err := under.List(ctx, "fleets/pi/status/boxes/")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(boxes) != 1 {
+ t.Fatalf("fleets/pi/status/boxes/ = %v, want exactly one snapshot", boxes)
+ }
+
+ // The shared platform namespace must stay untouched: no box snapshot,
+ // and definitely no fleet.json (which would only ever appear had
+ // publish been true).
+ sharedBoxes, err := under.List(ctx, "status/boxes/")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(sharedBoxes) != 0 {
+ t.Errorf("status/boxes/ = %v, want none — a fleet box must never write the shared namespace", sharedBoxes)
+ }
+ if _, _, err := under.Get(ctx, "status/fleet.json"); err == nil {
+ t.Error("a fleet box must never publish status/fleet.json")
+ }
+}
+
// TestWrapListeningProbeNullsUnitBackedSockets is fix round 1's regression
// test: listeningProbe's raw /proc/net/unix read is confidently WRONG for
// systemd-socket-activated (registry.BackingUnit) routes, since the
diff --git a/internal/provision/reconcile_test.go b/internal/provision/reconcile_test.go
index 19f6aebba..5fe3bfb1d 100644
--- a/internal/provision/reconcile_test.go
+++ b/internal/provision/reconcile_test.go
@@ -167,6 +167,47 @@ func TestReconcileScope(t *testing.T) {
mustGet(t, reg, "shared.go.tito.io")
}
+// 2b. Fleet placement rides the same Region filter, string-equal, unchanged.
+//
+// TestReconcileFleetRegionMatchesByStringEquality pins the customer-fleets
+// plan's Task 5 Step 1: a record placed on a fleet (Region: "fleet/pi") is
+// adopted by a reconcile pass configured for that exact fleet region, and
+// left alone by a pass configured for a different (platform) region —
+// reconcile.go:125's `rec.Region != cfg.Region` is plain string equality,
+// so "fleet/pi" needs no new grammar to be recognized: it's simply another
+// opaque region string, exactly like "ie" or "us" already are. Exercises
+// the real Reconcile entry point (not a reimplementation of the filter) so
+// a future refactor of the scope check can't silently narrow it.
+func TestReconcileFleetRegionMatchesByStringEquality(t *testing.T) {
+ s, sqid := setup(t)
+ rec := Record{Schema: 1, Host: "acme.go.tito.io", Slug: "acme", App: "tito",
+ Account: sqid, Pool: "tito", Region: "fleet/pi", Channel: "stable", CreatedAt: time.Now()}
+ if err := CreateRecord(ctx, s, rec); err != nil {
+ t.Fatal(err)
+ }
+ reg := regWith(t)
+
+ // A pass configured for a platform region must not adopt a fleet-placed
+ // record — "eu" != "fleet/pi", same as any other region mismatch.
+ if err := Reconcile(ctx, s, reg, ReconcileConfig{Region: "eu", Pools: []string{"tito"}, DataDir: "/data"}); err != nil {
+ t.Fatal(err)
+ }
+ if rt, err := reg.Get("acme.go.tito.io"); err != nil || rt != nil {
+ t.Errorf("minted for the wrong region (eu != fleet/pi): %+v %v", rt, err)
+ }
+
+ // The fleet's own box — configured with the exact "fleet/pi" region —
+ // adopts it with zero code change: the same Region == cfg.Region check
+ // every platform region already uses.
+ if err := Reconcile(ctx, s, reg, ReconcileConfig{Region: "fleet/pi", Pools: []string{"tito"}, DataDir: "/data"}); err != nil {
+ t.Fatal(err)
+ }
+ got := mustGet(t, reg, "acme.go.tito.io")
+ if got.Region != "fleet/pi" {
+ t.Errorf("adopted route Region = %q, want fleet/pi", got.Region)
+ }
+}
+
// 3. Idempotent.
func TestReconcileIdempotent(t *testing.T) {
s, sqid := setup(t)
diff --git a/internal/status/contract_test.go b/internal/status/contract_test.go
index 0ad706fa6..862f239c4 100644
--- a/internal/status/contract_test.go
+++ b/internal/status/contract_test.go
@@ -283,6 +283,55 @@ func TestFleetSchemaAllowlisted(t *testing.T) {
assertOnlyKeys(t, j, allowedFleetKeys)
}
+// TestBuildFleetIgnoresFleetPrefixedSnapshots pins the customer-fleets
+// plan's Task 5 Step 3 contract: BuildFleet's loadBoxes lists EXACTLY
+// "status/boxes/" (fleet.go:197) — a fleet box's heartbeat, written under
+// "fleets/<name>/status/boxes/<label>.json" per the fleet box env contract
+// (CARLOS_STATUS_PREFIX, internal/host's newPrefixStore), sits outside that
+// prefix entirely and so never surfaces on the platform's own public
+// fleet.json. This is owner-only visibility BY CONSTRUCTION — there is no
+// allowlist rule to bypass, loadBoxes simply never reads that path — the
+// same shape FleetBox never copying Box's "metrics" key already relies on.
+func TestBuildFleetIgnoresFleetPrefixedSnapshots(t *testing.T) {
+ s := teststore(t)
+ now := time.Now()
+
+ // A fleet box's own snapshot, parked at the exact key its
+ // CARLOS_STATUS_PREFIX=fleets/pi/ produces — same Box shape a platform
+ // box's WriteBox would produce, just outside status/boxes/.
+ fleetBox := boxFixture("pi-box", "plenty", now)
+ body, err := json.Marshal(fleetBox)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, err := s.Put(context.Background(), "fleets/pi/status/boxes/pi-box.json", body, ""); err != nil {
+ t.Fatal(err)
+ }
+
+ f, err := status.BuildFleet(context.Background(), s, nil, now, 5*time.Minute)
+ if err != nil {
+ t.Fatalf("BuildFleet: %v", err)
+ }
+ if len(f.Boxes) != 0 {
+ t.Errorf("Boxes = %+v, want none — a fleet-prefixed snapshot must never surface on the public fleet page", f.Boxes)
+ }
+
+ // Contrast case: a platform box's own snapshot, written through the
+ // real WriteBox (the unprefixed key), MUST surface — proving the
+ // exclusion above is specifically the fleets/ prefix, not a broken
+ // loadBoxes that would hide everything.
+ if err := status.WriteBox(context.Background(), s, boxFixture("platform-box", "plenty", now)); err != nil {
+ t.Fatal(err)
+ }
+ f2, err := status.BuildFleet(context.Background(), s, nil, now, 5*time.Minute)
+ if err != nil {
+ t.Fatalf("BuildFleet: %v", err)
+ }
+ if len(f2.Boxes) != 1 || f2.Boxes[0].Label != "platform-box" {
+ t.Errorf("Boxes = %+v, want exactly platform-box (the fleet-prefixed snapshot still must not appear)", f2.Boxes)
+ }
+}
+
// TestNoDigitsInBandSubtrees verifies that the band words (load, capacity)
// never carry numeric digits. The privacy commitment: never publish raw
// fractions or counts, only words. A digit in these fields would violate