Give the pairing vault its own key, and let the gate build the assets

4aab3422638b · AtlantisPleb · · parent fa23f3047eca

Give the pairing vault its own key, and let the gate build the assets

Two deploy blockers that both cost a full gate run to discover.

The pairing vault has run on the GitHub vault's key in production since
#192 shipped. That fix was correct and deployed; it left a bridge in
runtime.exs so the deploy introducing MACHINE_TOKEN_ENCRYPTION_KEY would
boot before an operator provisioned the secret, and the bridge that was
meant to last one deploy became the configuration. VAULT-001 says each
vault seals under its own key and two of the three did not. The secret is
provisioned and readable by the fleet runtime account; this wires it in, so
the separation deploys with the next roll. The vault's decrypt-side
fallback keeps the at-most-ten-minute pairing population readable across
the switch.

The gate installs the asset dependencies before it starts. A release
worktree is created fresh and assets/node_modules is untracked, so
mix esbuild failed to resolve an import eight stages in and reported a
missing npm package as a deploy blocker. This has cost four gate runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KnhfrafYx5ZGaMbzZEJQ2d
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>

Deploy story

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

pushed
by user · WAL seq 399 · 2026-08-25T15:26:29.451797Z

Changed files

  • modified ops/ci/gate.sh
  • modified ops/deploy/fleet-startup.template.sh

Diff

2 files changed, +19 -0

ops/ci/gate.sh modified +13

@@ -75,6 +75,19 @@ for command_name in jq mix npm rg; do

75 75
  fi
76 76
done
77 77
78
# The asset dependencies. A release worktree is created fresh, `assets/node_modules`
79
# is not tracked, and nothing else in the gate installs it — so `mix esbuild`
80
# fails to resolve an import eight stages in and reports a missing package as a
81
# deploy blocker. Installed here, before any stage, because a gate that cannot
82
# build the assets has not tested the release.
83
if [ ! -d "$repo_root/assets/node_modules" ]; then
84
  echo "Installing asset dependencies"
85
  (cd "$repo_root/assets" && npm ci --no-audit --no-fund) || {
86
    echo "asset dependencies could not be installed" >&2
87
    exit 1
88
  }
89
fi
90
78 91
run_root=$(mktemp -d /tmp/openagents-release-gate.XXXXXX)
79 92
started_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
80 93
started_epoch=$(date +%s)
ops/deploy/fleet-startup.template.sh modified +6

@@ -39,6 +39,11 @@ export GITHUB_CLIENT_ID="$(secret sarah-production-github-client-id)"

39 39
export GITHUB_CLIENT_SECRET="$(secret sarah-production-github-client-secret)"
40 40
export GITHUB_TOKEN_ENCRYPTION_KEY="$(secret sarah-production-github-token-encryption-key-reserved)"
41 41
export VOICE_RECORDING_ENCRYPTION_KEY="$(secret sarah-production-voice-recording-key)"
42
# The pairing vault's own key (VAULT-001, issues #192 and #253). Until this
43
# was set, runtime.exs bridged the pairing vault to the GitHub vault's key,
44
# so two vaults sealed under one key and a GitHub rotation silently moved
45
# the pairing vault too. The bridge was meant to last one deploy.
46
export MACHINE_TOKEN_ENCRYPTION_KEY="$(secret openagents-machine-token-encryption-key)"
42 47
export RELEASE_COOKIE="$(secret sarah-release-cookie)"
43 48
export OPENAGENTS_FORGE_OPERATOR_TOKEN="$(secret sarah-forge-operator-token)"
44 49
export OPENAGENTS_POSTHOG_PROJECT_TOKEN="$(secret openagents-posthog-project-token)"

@@ -130,6 +135,7 @@ ENV_NAMES=(

130 135
  AI_GATEWAY_API_KEY
131 136
  GITHUB_CLIENT_ID GITHUB_CLIENT_SECRET
132 137
  GITHUB_TOKEN_ENCRYPTION_KEY VOICE_RECORDING_ENCRYPTION_KEY RELEASE_COOKIE
138
  MACHINE_TOKEN_ENCRYPTION_KEY
133 139
  OPENAGENTS_FORGE_OPERATOR_TOKEN OPENAGENTS_POSTHOG_PROJECT_TOKEN
134 140
  DATABASE_URL DNS_CLUSTER_QUERY
135 141
  GITHUB_OAUTH_SCOPES GITHUB_REDIRECT_URI GITHUB_TOKEN_DECRYPTION_KEYS_JSON

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