eleven / messenger Public

server: fix mesh_peer_boxes' stats prefix to match where PutStats writes (#842)

Found by re-verifying the exact S3 key path before flipping the mesh
on for real: mesh_peer_boxes listed "s3://bucket/<prefix>-stats/", but
PutStats (internal/activator/heartbeat.go's statsKey) actually writes
to "s3://bucket/<prefix>/mesh/members-stats/<peer>" — hc.meshPrefix()
(activator.go) is "<prefix>/mesh/members", and statsKey appends
"-stats" to that WHOLE path, not to the bare hibernation prefix.

The mismatch was invisible in every test so far because both the
correct and wrong prefix are empty today (the mesh has never been on,
so nothing has ever published to either) — a passing "returns []"
result proved nothing about which path was actually being read.

Verified properly this time: uploaded a synthetic stats object to the
real path Go would use, using the shared activator-prod credentials,
then confirmed the fixed function finds and correctly parses it using
the status-publisher's actual restricted (read-only, *-stats/* scoped)
credentials — the exact ones it runs with in production. Cleaned up
the test object afterward. Dry-run fixtures regenerate identical to
committed (this code path never runs in dry-run mode).

The Terraform grant from #822 needs no change: its "*-stats/*"
StringLike pattern already matches the real path correctly (it never
assumed a specific prefix depth).
Co-authored-by: Claude <noreply@anthropic.com>
James Adam 7ec8238fc1bcc0ccd9896195be0b496857a0b13c parent cdd2fc9
1 file changed, +11 −3
  • deploy/status-publish.sh +11 −3
diff --git a/deploy/status-publish.sh b/deploy/status-publish.sh
index 1d7d78e..aceeafe 100755
--- a/deploy/status-publish.sh
+++ b/deploy/status-publish.sh
@@ -358,12 +358,20 @@ mesh_peer_boxes() {
return
fi
- local bucket region prefix
+ local bucket region prefix mesh_prefix
bucket=$(env_str "$ROUTER_ENV" LCHAT_HIBERNATE_BUCKET)
prefix=$(env_str "$ROUTER_ENV" LCHAT_HIBERNATE_PREFIX)
if [ -z "$bucket" ] || [ -z "$prefix" ]; then echo '[]'; return; fi
region=$(env_str "$ROUTER_ENV" LCHAT_HIBERNATE_REGION)
region=${region:-eu-west-1}
+ # Must match hc.meshPrefix() (activator.go) exactly — the membership
+ # heartbeat lives at "<prefix>/mesh/members/<peer>", and statsKey
+ # (internal/activator/heartbeat.go) appends "-stats" to that WHOLE path,
+ # not to the bare hibernation prefix: "<prefix>/mesh/members-stats/<peer>".
+ # (Caught 2026-07-13 before the flip: an earlier version of this function
+ # listed "<prefix>-stats/" instead — empty either way today since nothing
+ # has ever published there, so it silently proved nothing.)
+ mesh_prefix="${prefix}/mesh/members"
# `aws s3 ls` (the high-level command), never `aws s3api list-objects*` —
# the latter crashes on this fleet's aws-cli/Python combination (a Python
@@ -375,7 +383,7 @@ mesh_peer_boxes() {
# already relies on). `s3 ls` hits the same ListObjectsV2 API underneath;
# its plain-text output already has everything needed (date, time, key).
local listing
- listing=$(aws s3 ls "s3://$bucket/${prefix}-stats/" --region "$region" 2>/dev/null) || true
+ listing=$(aws s3 ls "s3://$bucket/${mesh_prefix}-stats/" --region "$region" 2>/dev/null) || true
[ -n "$listing" ] || { echo '[]'; return; }
# A plain string compare against the cutoff is exact as long as both sides
@@ -394,7 +402,7 @@ mesh_peer_boxes() {
[ -n "$key" ] || continue
[ "$date_part $time_part" '>' "$cutoff" ] || continue # stale — treat like a dead mesh member
tmp="$TMP/mesh-stat-$(printf '%s' "$key" | tr '/' '_').json"
- aws s3 cp "s3://$bucket/${prefix}-stats/$key" "$tmp" --region "$region" >/dev/null 2>&1 || continue
+ aws s3 cp "s3://$bucket/${mesh_prefix}-stats/$key" "$tmp" --region "$region" >/dev/null 2>&1 || continue
body=$(cat "$tmp" 2>/dev/null) || continue
[ -n "$body" ] || continue
peers=$(sqj "SELECT json_insert(