Fix staging fleet node discovery

c609e1e881b5 · AtlantisPleb · · parent 239e5662583e

Fix staging fleet node discovery

Use stable private IPs for BEAM identities so DNS discovery and release node names match. Upgrade Bandit to 1.12.5 for the current HTTP/2 security fixes and replace a home-relative documentation path.

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

Not deployed through the forge lane

No push, promotion, build, or deploy receipt references this commit (receipts are scanned over a bounded recent window). Changes shipped by full node replacement carry their proof in the release gate receipt instead.

Changed files

  • modified docs/2026-08-20-linear-design-github-shape.md
  • modified docs/runtime-configuration.md
  • modified infra/staging/outputs.tf
  • modified infra/staging/templates/fleet-startup.sh.tftpl
  • modified mix.lock
  • modified test/openagents/staging_candidate_contract_test.exs

Diff

6 files changed, +21 -8

docs/2026-08-20-linear-design-github-shape.md modified +1 -1

@@ -10,7 +10,7 @@ The principle, stated by the owner:

10 10
This doc says how those two survive each other. It is a strategy note, not a
11 11
plan of record: the tracker for the component work is
12 12
`docs/2026-08-20-circle-ui-port.md`, and the URL/API compatibility target is
13
`~/work/sarah/docs/audits/2026-08-19-github-clone-harvest-candidates.md`.
13
`docs/github-api-issues-projects-assessment.md`.
14 14
15 15
## The two commitments are about different layers
16 16
docs/runtime-configuration.md modified +4 -2

@@ -219,8 +219,10 @@ discovery, node identity, cookie, and bounded distribution ports.

219 219
220 220
Ra and fleet deployment additionally require a stable `RELEASE_NODE`, a
221 221
`RELEASE_COOKIE` of at least 32 bytes, and `RELEASE_DISTRIBUTION=name` or
222
`longnames`. The readiness report records only whether those settings passed;
223
it never prints their values.
222
`longnames`. In staging, each fleet node uses its reserved private IP in
223
`RELEASE_NODE` because `DNSCluster` constructs peer identities from the A
224
records returned by `DNS_CLUSTER_QUERY`. The readiness report records only
225
whether those settings passed; it never prints their values.
224 226
225 227
## Local defaults
226 228
infra/staging/outputs.tf modified +2 -2

@@ -31,8 +31,8 @@ output "builder_image_repository" {

31 31
output "fleet_nodes" {
32 32
  description = "Exact BEAM node-to-instance map for runtime configuration."
33 33
  value = {
34
    for instance_name in sort(keys(local.nodes)) :
35
    "openagents@${instance_name}.staging.internal" => instance_name
34
    for instance_name, instance_ip in local.nodes :
35
    "openagents@${instance_ip}" => instance_name
36 36
  }
37 37
}
38 38
infra/staging/templates/fleet-startup.sh.tftpl modified +2 -2

@@ -39,7 +39,7 @@ builder_digest=$(metadata_attribute openagents-builder-digest)

39 39
source_sha=$(metadata_attribute openagents-sha)
40 40
runtime_secret=$(metadata_attribute openagents-runtime-secret)
41 41
builder_secret=$(metadata_attribute openagents-builder-secret)
42
instance_name=$(metadata instance/name)
42
instance_ip=$(metadata instance/network-interfaces/0/ip)
43 43
44 44
if [ -z "$image" ] || [ -z "$image_digest" ] || [ -z "$source_sha" ]; then
45 45
  echo "No staging candidate is assigned; leaving the node fenced."

@@ -137,7 +137,7 @@ cat >>/run/openagents/runtime.env <<EOF

137 137
DNS_CLUSTER_QUERY=openagents-fleet.staging.internal
138 138
OPENAGENTS_IMAGE_DIGEST=$image_digest
139 139
RELEASE_DISTRIBUTION=name
140
RELEASE_NODE=openagents@$instance_name.staging.internal
140
RELEASE_NODE=openagents@$instance_ip
141 141
EOF
142 142
143 143
export DOCKER_CONFIG=/run/openagents/docker-config
mix.lock modified +1 -1

@@ -1,6 +1,6 @@

1 1
%{
2 2
  "aten": {:hex, :aten, "0.6.0", "7a57b275a6daf515ac3683fb9853e280b4d0dcdd74292fd66ac4a01c8694f8c7", [:rebar3], [], "hexpm", "5f39a164206ae3f211ef5880b1f7819415686436e3229d30b6a058564fbaa168"},
3
  "bandit": {:hex, :bandit, "1.12.4", "10bbab488edf8162318d736c19c5837077b8fca2bf5d95b07b33830387124f62", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.5", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "84513318c5752a2a8017664450f889b47fae5d53d64698ddf1e4fb09a7449e8d"},
3
  "bandit": {:hex, :bandit, "1.12.5", "af205a8e550f304caae09a97d29fd3c79a7f337526ea7cd772d2ff11d2f7c800", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.5", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "c5684ca062fa407cac115aec3256383f3e2ec9fdced7904d59cf5a7bb7ed6181"},
4 4
  "castle": {:hex, :castle, "0.3.1", "e5d4f20696d878052a23c13158e1d372b24d9b30a6ea6f52fa6063c21c5ad67e", [:mix], [{:forecastle, "~> 0.1.3", [hex: :forecastle, repo: "hexpm", optional: false]}], "hexpm", "3ee9ca04b069280ab4197fe753562958729c83b3aa08125255116a989e133835"},
5 5
  "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"},
6 6
  "db_connection": {:hex, :db_connection, "2.10.2", "ae391e803a5adff104da913c2fc1c0c14a37f8b10001dcef568796e1fb7bf95c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "510b14482330f1af6490a2fa0efd8d4f1435d1529b165647df22ac0f2df0fa93"},
test/openagents/staging_candidate_contract_test.exs modified +11

@@ -44,6 +44,17 @@ defmodule OpenAgents.StagingCandidateContractTest do

44 44
    assert runtime_config =~ ~s("builder" -> :builder)
45 45
  end
46 46
47
  test "fleet discovery and release identities use the same stable private addresses" do
48
    startup = File.read!("infra/staging/templates/fleet-startup.sh.tftpl")
49
    outputs = File.read!("infra/staging/outputs.tf")
50
51
    assert startup =~ "instance_ip=$(metadata instance/network-interfaces/0/ip)"
52
    assert startup =~ "DNS_CLUSTER_QUERY=openagents-fleet.staging.internal"
53
    assert startup =~ "RELEASE_NODE=openagents@$instance_ip"
54
    refute startup =~ "RELEASE_NODE=openagents@$instance_name.staging.internal"
55
    assert outputs =~ ~s("openagents@${instance_ip}" => instance_name)
56
  end
57
47 58
  test "candidate publication binds exact immutable registry and artifact identities" do
48 59
    publisher = File.read!("ops/staging/publish-candidate.sh")
49 60
    sbom = File.read!("ops/staging/generate-sbom.sh")

This page updates live while a promote is in flight · changelog