Repository navigation
fix: split platform control-plane env from site env in Context - #148
Conversation
MyApps on Elastx (prod-se) fail to fetch env vars from eyevinn-app-config-svc at container startup whenever Elastx's own catalog-manager/token-service/money-manager are down, even though the actual data source (the tenant's app-config-svc instance) is healthy. prod-se is a site of the prod environment, not an independent peer environment (infra-osaas/osaas/prod-estx.yml declares environment: "prod" on every Elastx platform ConfigMap, and pat-secret/mac-keys are byte-identical across both clusters). Context previously reused a single `environment` value for both site resolution (which cluster hosts the instance data) and platform/auth calls (subscription check + SAT mint), so a context configured with environment=prod-se called Elastx's own catalog-manager and token-service, which in turn hit Elastx's own money-manager over in-cluster DNS with no cross-cluster fallback. Splits Context into `environment` (site, used by deploy-manager calls which are genuinely per-cluster) and `platformEnv` (control plane, used by /mysubscriptions and /servicetoken). `platformEnv` defaults to PRIMARY_ENV only when `environment` is `prod` or `prod-se`; `dev` and `stage` are independent single-cluster environments and keep platformEnv equal to environment. Closes #147 Co-Authored-By: Claude <noreply@anthropic.com>
Research ReviewVerified
Needs Verification
Adjacent Stubs DetectedNone. Swept Orphaned Files DetectedNot applicable — this PR modifies existing, already-wired library source files ( Impact Analysis
Risks Not Tested
Research Suggestions
Reviewed by PR Research Validator. This review checks whether code is based on verified sources rather than assumptions. |
Summary
Closes #147.
MyApps running on Elastx (
prod-se) fail to fetch their env vars fromeyevinn-app-config-svcat container startup whenever Elastx's owncatalog-manager/token-service/money-managerare down (e.g. during migration/scale-down ops) — even though the actual data source (the tenant's app-config-svc instance) is healthy and reachable throughout. Confirmed root cause during a 2026-07-29 11:40–13:04 UTC production incident.prod-se(Elastx) is a site of the singleprodenvironment, not an independent peer environment —infra-osaas/osaas/prod-estx.ymldeclaresenvironment: "prod"on every Elastx platform ConfigMap, andpat-secret/mac-keysare byte-identical across both clusters.Contextpreviously reused oneenvironmentvalue for both site resolution (which cluster hosts the instance data — already fully data-driven viaservice.apiUrl) and platform/auth calls (subscription check + SAT mint). A context configured withenvironment=prod-setherefore called Elastx's own catalog-manager and token-service, which pull in Elastx's own money-manager over in-cluster DNS with no cross-cluster fallback — turning a transient Elastx control-plane blip into a MyApp startup outage.Changes
packages/core/src/context.ts: splitContextintoenvironment(site) and a newplatformEnv(control plane). AddedPRIMARY_ENV(process.env.OSC_PRIMARY_ENV || 'prod') andPROD_SITE_ENVS = ['prod', 'prod-se'].platformEnvdefaults toPRIMARY_ENVonly whenenvironmentisprodorprod-se—devandstageare independent single-cluster environments with their own control plane and are intentionally excluded, soplatformEnvstays equal toenvironmentfor them. An explicitconfig.platformEnvalways wins. AddedgetPlatformEnvironment()accessor. Switched/mysubscriptions(GET + POST) and/servicetokento useplatformEnv.packages/core/src/core.ts:getService()'s/mysubscriptionscall now usescontext.getPlatformEnvironment().packages/core/src/admin.ts:listSubscriptions()andremoveSubscription()'s/mysubscriptionscalls now usecontext.getPlatformEnvironment()(same auth/entitlement concern ascontext.ts, previously missed the same coupling).core.tssaveSecret/listSecrets/getSecret/updateSecret/deleteSecret(deploy.svc/mysecrets),admin.tslistReservedNodes(deploy.svc/mynodes), and all ofmyapp.ts(deploy.svc/myapps) — deploy-manager owns cluster-local K8s resources per site, so these must keep followingenvironment.packages/core/src/platform.ts'sPlatformclass (used bypackages/cli/src/admin/syncfork.tsandpackages/core/src/platform/maker.tsformaker.svc.*calls) uses a separateOSC_API_KEYauth model for admin/build tooling, not the PAT-based MyApps runtime path implicated in this incident.packages/ai/src/chat.ts/embeddings.ts'sai.svc.${environment}calls are an application service endpoint, not an auth/entitlement endpoint, and were not touched.Tests added in
packages/core/src/context.test.tscover: defaultplatformEnvforprod/prod-se/dev/stage, explicit override wins, andPROD_SITE_ENVScontents.packages/core/src/core.test.tsgained a test confirmingsaveSecretstill targetsdeploy.svc.<environment>(site) even whenenvironmentandplatformEnvdiverge onprod-se.Test plan
npx tsc --noEmit -p tsconfig.json(packages/core) — passesnpx eslint src/context.ts src/context.test.ts src/core.ts src/core.test.ts src/admin.ts— 0 errors (6 pre-existingno-explicit-anywarnings in unrelated lines ofcore.ts)npx prettier --checkon all changed files — passesnpx jest(packages/core, full suite) — 33/33 passing, including new testsLessons
A previous investigation session's
git pull --ff-onlyon all five runner repos (web/python/golang/dotnet/wasm-runner) returned quietly without actually fast-forwarding, leaving those checkouts 3-5 commits behindorigin/mainand producing a false "no--envflag propagated" conclusion.git pullsucceeding is not sufficient confirmation of being current — always follow withgit rev-list --count HEAD..origin/mainand confirm it's0before drawing conclusions from a local checkout. Applied here:client-tswas confirmed at0commits behind before any file was read.Separately, the original fix instructions assumed
platformEnvshould unconditionally default toPRIMARY_ENV. That's correct forprod/prod-sebut wrong fordev/stage, which are genuinely independent single-cluster environments with their own control plane — unconditionally forcing them ontoprodwould have broken dev/stage platform calls. ThePROD_SITE_ENVSgate (['prod', 'prod-se']) exists specifically to scope the redirect to the one pair of environments where it applies.Also worth flagging for a follow-up:
admin.ts'slistSubscriptions/removeSubscriptionhad the exact same/mysubscriptions+context.getEnvironment()coupling ascontext.ts, and would have silently kept the same bug for anyone managing subscriptions from aprod-se-configuredContextif not caught by grepping the wholepackages/core/srctree for all/mysubscriptionsand/servicetokencall sites rather than trusting the 4 call sites named in the original bug report.🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com