Fix two config gaps that break a from-scratch Fly deployment - #4
Merged
Merged
Conversation
A Fly deployment could not boot its core, and could not reach Admin or web chat once it did. Both are gaps in what the CLI hands the runtime. Core refuses to start in production unless SANDBOX_BACKEND is set, but only the AWS backend ever emitted it. Fly and docker both derive their core env from sandboxCoreEnv, so the variable is set there instead of in one backend: a configured sandbox.backend now always reaches core, and a fly target with a sandbox app defaults to the sprites substrate that sandbox.app describes. Docker keeps its previous behaviour, since its sandboxes are local rather than sprites, and an explicit env.core.SANDBOX_BACKEND still wins. PORTAL_IDENTITY_SECRET was declared for core and portal only, yet admin and web-ui also sign portal identities through the chassis. With the variable absent the chassis silently falls back to CORE_SIGNING_SECRET while core verifies with the real key, so every admin and web-ui request fails the identity check. The portal fails closed on that, surfacing as "Admin is temporarily unavailable" with nothing pointing at a key mismatch. deploy/core/fly.toml is regenerated from the derive function. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ReganBell
pushed a commit
that referenced
this pull request
Jul 29, 2026
A Fly deployment could not boot its core, and could not reach Admin or web chat once it did. Both are gaps in what the CLI hands the runtime. Core refuses to start in production unless SANDBOX_BACKEND is set, but only the AWS backend ever emitted it. Fly and docker both derive their core env from sandboxCoreEnv, so the variable is set there instead of in one backend: a configured sandbox.backend now always reaches core, and a fly target with a sandbox app defaults to the sprites substrate that sandbox.app describes. Docker keeps its previous behaviour, since its sandboxes are local rather than sprites, and an explicit env.core.SANDBOX_BACKEND still wins. PORTAL_IDENTITY_SECRET was declared for core and portal only, yet admin and web-ui also sign portal identities through the chassis. With the variable absent the chassis silently falls back to CORE_SIGNING_SECRET while core verifies with the real key, so every admin and web-ui request fails the identity check. The portal fails closed on that, surfacing as "Admin is temporarily unavailable" with nothing pointing at a key mismatch. deploy/core/fly.toml is regenerated from the derive function. Co-authored-by: YC Labs <yclabs@YCs-Mac-Studio.local> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two gaps in what the CLI hands the runtime. Each independently breaks a Fly deployment, and both were hit in sequence standing one up from scratch today.
1. Core never receives
SANDBOX_BACKENDon FlyCore refuses to start in production unless
SANDBOX_BACKENDis set (src/config.ts), but only the AWS backend emitted it (cli/src/backends/aws.ts). A Fly deploy therefore crash-loops to its 10-restart cap:Fixed in
sandboxCoreEnv, which is the layer all three backends derive core env from (fly.ts:181,docker.ts:483,aws.ts:240), rather than in one backend:sandbox.backendnow always reaches core, on every targetflytarget with asandbox.appdefaults tosprites— the substratesandbox.appalready describes, matching the fallback AWS uses for the same casesandbox.backendis explicitenv.core.SANDBOX_BACKENDstill wins, sinceconfiguredEnvis applied aftersandboxEnv2.
PORTAL_IDENTITY_SECRETnever reaches admin or web-uiIt was declared for
coreandportalonly, butadminandweb-uialso sign portal identities through the chassis. When it is absent the chassis silently substitutes a different key:Core verifies with
portalIdentitySecret ?? secretand does have the real key, so every admin and web-ui request fails the identity check. The portal fails closed on that, which surfaces as "Admin is temporarily unavailable", and web chat returns401 sign in— neither pointing at a key mismatch.Verified against a live deployment: with the secret delivered,
GET /v1/admin/whoamireturns{"isAdmin":true,"role":"org_admin",...}; without it, the same request 401s.authis deliberately not included — it imports the chassis (so it logs the same fallback line) but never signs a portal identity.Tests
Three tests in
cli/test/secrets.test.ts, each confirmed to fail without the corresponding source change:PORTAL_IDENTITY_SECRETreaches all four services in the trust boundaryflydeployment tells core which substrate to bootsandbox.backendwins, and non-fly targets keep their own defaultdeploy/core/fly.tomlis regenerated from the derive function, so the byte-for-byte test passes.npm run typecheckclean, oxlint clean, 474/475 CLI tests pass. The one failure — "AWS secret rotation holds the deploy lease across the complete write set" — fails identically onmainwithout this change and is unrelated.Worth considering separately
The chassis fallback is what made #2 invisible: a signing/verifying key mismatch should refuse to boot in production rather than degrade to 401s at runtime. Changing that is a behavioural change with wider blast radius, so it is not in this PR.
Needs an adversarial review pass — I wrote it, so I am the wrong reviewer.
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmithwith what you need. Autofix is disabled.