Use staging.openagents.com as the staging hostname

6d9797158763 · AtlantisPleb · · parent af183c5f97c7

Use staging.openagents.com as the staging hostname

The runtime config, Gate 5 profile, and docs pinned `stage.openagents.com`,
and RuntimeConfig refused to boot against anything else
("endpoint_host must be the staging hostname"). That hostname no longer
exists: the owner had its Cloud Run domain mapping deleted on 2026-08-19
because the GitHub OAuth application is registered against the `staging.`
subdomain, and `staging.openagents.com` is what actually serves.

So the check was enforcing a hostname nothing could satisfy, and the staging
service could not start.

Changed the canonical staging host in the validator, the Gate 5 profile
(PHX_HOST, allowed origins, and the OAuth callback move together, or the
origin check fails), the runtime-configuration and API-authentication docs,
the infra README, and the runtime config tests.

Left alone: github_oauth/runtime_config_test.exs and route_authority_test.exs
also mention the old host, but as an arbitrary callback/routing input rather
than the staging contract -- any hostname satisfies them, so changing them
would be churn.

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/api-authentication.md
  • modified docs/runtime-configuration.md
  • modified infra/staging/README.md
  • modified lib/openagents/runtime_config.ex
  • modified lib/openagents_web/allowed_origins.ex
  • modified lib/openagents_web/components/layouts/root.html.heex
  • modified ops/staging/gate-5-profile.sh
  • modified test/openagents/runtime_config_test.exs

Diff

8 files changed, +29 -29

docs/api-authentication.md modified +1 -1

@@ -17,7 +17,7 @@ curl \

17 17
  --header "Authorization: Bearer $OPENAGENTS_API_TOKEN" \
18 18
  --header "Content-Type: application/json" \
19 19
  --data '{"title":"Example"}' \
20
  https://stage.openagents.com/api/v3/repos/OpenAgentsInc/openagents.com/issues
20
  https://staging.openagents.com/api/v3/repos/OpenAgentsInc/openagents.com/issues
21 21
```
22 22
23 23
Do not put the token in a URL, command history, checked-in environment file, or
docs/runtime-configuration.md modified +2 -2

@@ -56,8 +56,8 @@ URLs, receipts, or checked-in environment files.

56 56
| Release | `OPENAGENTS_STAGING_CLEANUP_ENABLED` | `true` only at staging Gate 12 or later; always `false` elsewhere |
57 57
| Release | `OPENAGENTS_PRODUCTION_DEPLOY_ENABLED` | `false` until a later production decision |
58 58
| Release | `OPENAGENTS_IMAGE_DIGEST` | Exact `sha256:` image digest at staging Gate 12 and later; empty before that gate |
59
| Endpoint | `PHX_HOST` | Exactly `stage.openagents.com` in staging |
60
| Endpoint | `OPENAGENTS_ALLOWED_ORIGINS` | Comma-separated exact HTTPS origins including `https://stage.openagents.com` |
59
| Endpoint | `PHX_HOST` | Exactly `staging.openagents.com` in staging |
60
| Endpoint | `OPENAGENTS_ALLOWED_ORIGINS` | Comma-separated exact HTTPS origins including `https://staging.openagents.com` |
61 61
| Endpoint | `OPENAGENTS_HTTPS_ALIASES` | Comma-separated hostnames; empty means no aliases |
62 62
| Endpoint | `OPENAGENTS_SECURE_COOKIES` | `true` in staging and production |
63 63
| Endpoint | `SECRET_KEY_BASE` | Staging-only secret |
infra/staging/README.md modified +1 -1

@@ -160,7 +160,7 @@ harness before you apply cleanup so it cannot create new run data concurrently.

160 160
Gate 12 remains incomplete until the cloud apply, isolation validator, and a
161 161
live execution of the manifest-scoped disposable-run cleanup command are
162 162
proven. Do not populate
163
secrets, push an image, change DNS for `stage.openagents.com`, or deploy a
163
secrets, push an image, change DNS for `staging.openagents.com`, or deploy a
164 164
candidate as part of the infrastructure apply. Gate 13 performs those steps on
165 165
one exact, locally gated SHA after a separate review.
166 166
lib/openagents/runtime_config.ex modified +1 -1

@@ -214,7 +214,7 @@ defmodule OpenAgents.RuntimeConfig do

214 214
      environment in [:staging, :production] and secure_cookies != true ->
215 215
        error(:secure_cookies, "must be true")
216 216
217
      environment == :staging and host != "stage.openagents.com" ->
217
      environment == :staging and host != "staging.openagents.com" ->
218 218
        error(:endpoint_host, "must be the staging hostname")
219 219
220 220
      environment in [:staging, :production] and
lib/openagents_web/allowed_origins.ex modified +1 -1

@@ -12,7 +12,7 @@ defmodule OpenAgentsWeb.AllowedOrigins do

12 12
  @doc """
13 13
  Returns a list of allowed origins for production.
14 14
15
  `primary_host` is the canonical host (e.g. `stage.openagents.com`); it is
15
  `primary_host` is the canonical host (e.g. `staging.openagents.com`); it is
16 16
  always returned as `https://` first. `aliases` is a comma-separated string
17 17
  of `https://` origins. Each alias is validated: it must use `https` and
18 18
  must not contain a path.
lib/openagents_web/components/layouts/root.html.heex modified +16 -16

@@ -16,22 +16,22 @@

16 16
    prefers-color-scheme fallback governs; an explicit choice sets the
17 17
    attribute, which always wins. --%>
18 18
    <script phx-no-format>(() => {
19
      const stored = localStorage.getItem("phx:theme");
20
      const apply = (choice) => {
21
        if (choice === "system" || choice === null) {
22
          localStorage.removeItem("phx:theme");
23
          document.documentElement.removeAttribute("data-theme");
24
          document.documentElement.setAttribute("data-theme-choice", "system");
25
        } else {
26
          localStorage.setItem("phx:theme", choice);
27
          document.documentElement.setAttribute("data-theme", choice);
28
          document.documentElement.setAttribute("data-theme-choice", choice);
29
        }
30
      };
31
      apply(stored);
32
      window.addEventListener("phx:set-theme", (e) => apply(e.detail?.theme ?? e.target?.dataset?.theme));
33
      window.addEventListener("storage", (e) => e.key === "phx:theme" && apply(e.newValue));
34
    })();</script>
19
          const stored = localStorage.getItem("phx:theme");
20
          const apply = (choice) => {
21
            if (choice === "system" || choice === null) {
22
              localStorage.removeItem("phx:theme");
23
              document.documentElement.removeAttribute("data-theme");
24
              document.documentElement.setAttribute("data-theme-choice", "system");
25
            } else {
26
              localStorage.setItem("phx:theme", choice);
27
              document.documentElement.setAttribute("data-theme", choice);
28
              document.documentElement.setAttribute("data-theme-choice", choice);
29
            }
30
          };
31
          apply(stored);
32
          window.addEventListener("phx:set-theme", (e) => apply(e.detail?.theme ?? e.target?.dataset?.theme));
33
          window.addEventListener("storage", (e) => e.key === "phx:theme" && apply(e.newValue));
34
        })();</script>
35 35
    <script defer phx-track-static type="text/javascript" src={~p"/assets/js/app.js"}>
36 36
    </script>
37 37
  </head>
ops/staging/gate-5-profile.sh modified +3 -3

@@ -12,9 +12,9 @@ set -eu

12 12
: "${SECRET_KEY_BASE:?SECRET_KEY_BASE is required}"
13 13
14 14
export GITHUB_OAUTH_SCOPES="repo"
15
export GITHUB_REDIRECT_URI="https://stage.openagents.com/auth/github/callback"
15
export GITHUB_REDIRECT_URI="https://staging.openagents.com/auth/github/callback"
16 16
export GITHUB_TOKEN_DECRYPTION_KEYS_JSON="${GITHUB_TOKEN_DECRYPTION_KEYS_JSON:-{}}"
17
export OPENAGENTS_ALLOWED_ORIGINS="https://stage.openagents.com"
17
export OPENAGENTS_ALLOWED_ORIGINS="https://staging.openagents.com"
18 18
export OPENAGENTS_CODING_JOBS_DIR="/var/lib/openagents/coding-jobs"
19 19
export OPENAGENTS_DATABASE_IPV6="false"
20 20
export OPENAGENTS_DATABASE_MODE="url"

@@ -80,7 +80,7 @@ export OPENAGENTS_RA_EXPECTED_SIZE="3"

80 80
export OPENAGENTS_SECURE_COOKIES="true"
81 81
export OPENAGENTS_STAGING_GATE="5"
82 82
export OPENAGENTS_STAGING_CLEANUP_ENABLED="false"
83
export PHX_HOST="stage.openagents.com"
83
export PHX_HOST="staging.openagents.com"
84 84
export POOL_SIZE="${POOL_SIZE:-10}"
85 85
export VOICE_RECORDING_ENCRYPTION_KEY=""
86 86
test/openagents/runtime_config_test.exs modified +4 -4

@@ -41,7 +41,7 @@ defmodule OpenAgents.RuntimeConfigTest do

41 41
    assert config.staging_gate == 5
42 42
    refute encoded =~ secret
43 43
    refute encoded =~ oauth_secret
44
    refute encoded =~ "stage.openagents.com"
44
    refute encoded =~ "staging.openagents.com"
45 45
    refute encoded =~ "ecto://"
46 46
  end
47 47

@@ -287,10 +287,10 @@ defmodule OpenAgents.RuntimeConfigTest do

287 287
      pool_size: 10
288 288
    )
289 289
    |> Map.put(OpenAgentsWeb.Endpoint,
290
      url: [host: "stage.openagents.com", port: 443, scheme: "https"],
291
      check_origin: ["https://stage.openagents.com"]
290
      url: [host: "staging.openagents.com", port: 443, scheme: "https"],
291
      check_origin: ["https://staging.openagents.com"]
292 292
    )
293
    |> update_oauth(:redirect_uri, "https://stage.openagents.com/auth/github/callback")
293
    |> update_oauth(:redirect_uri, "https://staging.openagents.com/auth/github/callback")
294 294
    |> put_nested(:voice, :enabled, false)
295 295
    |> put_nested(:voice_recording, :enabled, false)
296 296
    |> put_nested(:work, :enabled, false)

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