eleven / messenger Public

server: box region auto-detects from cloud metadata (#737)

Hand-set STATUS_BOX_REGION drifted on day one (labelled Germany; the box
is in Helsinki). Resolution is now env override -> Hetzner metadata ->
AWS IMDSv2, zone mapped to a coarse continent+country, omitted when
nothing answers. Resolved at use-time so the dry-run's no-live-probes
guard actually guards.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 2a7ae742845a4b62eba0af07204dd05a5b79ca64 parent 284f575
1 file changed, +39 −7
  • deploy/status-publish.sh +39 −7
diff --git a/deploy/status-publish.sh b/deploy/status-publish.sh
index 391485a..b538957 100755
--- a/deploy/status-publish.sh
+++ b/deploy/status-publish.sh
@@ -27,10 +27,11 @@
# pairs hashed from the machine id ("ada-stargazing") — stable per box,
# never peer URLs, IPs, hostnames, or the machine id itself.
#
-# A box's `region` is COARSE BY DESIGN: an operator-set continent + country
-# pair ("EU · Germany", via STATUS_BOX_REGION) — never a city, datacenter,
-# IP, or hostname. Unset → the field is omitted entirely (the page shows
-# nothing); a region is never guessed from the box.
+# A box's `region` is COARSE BY DESIGN: a continent + country pair
+# ("EU · Finland") — never a city, datacenter, IP, or hostname. Resolved
+# from the box's own cloud metadata (see box_region below); the
+# STATUS_BOX_REGION env overrides; unknown → the field is omitted
+# entirely. Detected, mapped, or absent — never guessed.
#
# books.json — AGGREGATED INCOME ONLY. Active-subscription count (by tier —
# tier names are the public price book), MRR total in EUR, gross collected
@@ -81,9 +82,36 @@ MEMINFO=${STATUS_MEMINFO:-/proc/meminfo}
DATA_DIR=${STATUS_DATA_DIR:-/data}
# Coarse public region of this box — continent + country ONLY ("EU · Germany");
# see PRIVACY above. Optional: unset omits the field from fleet.json. Per-box
-# regions will come from mesh heartbeats when multi-box lands; this env is the
-# single-box seam until then.
-BOX_REGION=${STATUS_BOX_REGION:-}
+# regions will come from mesh heartbeats when multi-box lands. Resolution:
+# STATUS_BOX_REGION env (explicit override wins) → the box's own cloud
+# metadata service (Hetzner, then AWS IMDSv2), mapped zone→coarse region →
+# omitted entirely when nothing answers. Hand-set values drift (the box was
+# labelled Germany for a day; it lives in Helsinki) — asking the machine
+# where it is can't. The map stays coarse by design: continent + country.
+box_region() {
+ if [ -n "${STATUS_BOX_REGION:-}" ]; then printf '%s' "$STATUS_BOX_REGION"; return; fi
+ [ -n "$DRY_RUN" ] && return # dry-run never probes live box state
+ local zone
+ zone=$(curl -sm2 http://169.254.169.254/hetzner/v1/metadata/availability-zone 2>/dev/null || true)
+ case "$zone" in
+ hel*) printf 'EU · Finland'; return ;;
+ fsn*|nbg*) printf 'EU · Germany'; return ;;
+ ash*|hil*) printf 'NA · USA'; return ;;
+ sin*) printf 'AS · Singapore'; return ;;
+ esac
+ # AWS: IMDSv2 (token required; both calls fail closed in ~2s off-cloud)
+ local tok az
+ tok=$(curl -sm2 -X PUT http://169.254.169.254/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 60" 2>/dev/null || true)
+ [ -n "$tok" ] && az=$(curl -sm2 -H "X-aws-ec2-metadata-token: $tok" http://169.254.169.254/latest/meta-data/placement/region 2>/dev/null || true)
+ case "$az" in
+ eu-west-1) printf 'EU · Ireland' ;;
+ eu-west-2) printf 'EU · UK' ;;
+ eu-central-1) printf 'EU · Germany' ;;
+ eu-north-1) printf 'EU · Sweden' ;;
+ us-east-*|us-west-*) printf 'NA · USA' ;;
+ *) : ;; # unknown zone: omit, never guess
+ esac
+}
STATUS_ENV=${STATUS_ENV_FILE:-/data/status.env}
PAGES_DIR=${STATUS_PAGES_DIR:-/opt/lchat/status-pages}
STRIPE_API=${STATUS_STRIPE_API:-https://api.stripe.com}
@@ -331,6 +359,10 @@ build_fleet() {
# never guessed; escaped like the version strings so a quote can't break
# out of the SQL literal.
local boxes region_kv=""
+ # Resolved here, not at file top: DRY_RUN/argv must be parsed first so the
+ # dry-run's no-live-probes guard actually guards (and the dry-run fixture's
+ # direct BOX_REGION= assignment still wins over a re-resolve).
+ [ -z "${BOX_REGION:-}" ] && BOX_REGION=$(box_region)
if [ -n "$BOX_REGION" ]; then
region_kv=", 'region', '$(printf '%s' "$BOX_REGION" | sed "s/'/''/g")'"
fi