feat(lifecycle): run non-tunnel upgrades under the provided r_* credential#133
Merged
Conversation
…ntial
Closes the documented UpgradeHandler KNOWN GAP: the platform's
non-tunnel upgradeRequest already carries {from, to, appId, schema,
credential:{username, token}} (field names mirror InstallRequest
verbatim), but the SDK's decodeVersionRequest read only from/to — a
deployed module's (from, to] migrations silently fell back to the env
pool instead of running under the per-(app, module) r_* role against
the app schema.
- UpgradeRequest = VersionRequest + InstallRequest, adopting the
api-side wire shape with no wire change.
- injectLifecycleContext extracted from injectInstallContext and shared
by install, upgrade, and downgrade, so the credential + search_path
semantics cannot drift between the lifecycle verbs. Empty-credential
shape compat ({}) and the env-base host/port/database split are
identical to install.
- Bare {from, to} bodies (dev-tunnel path) behave exactly as before:
no schema or credential injected, env-pool fallback.
- Downgrade gets the same treatment for free by sharing the decoder —
a deployed downgrade has the identical gap.
Lifecycle reachability through the lambda envelope was verified, not
changed: the routes live on m.router, which both lambda.Start and the
dev lambda-invoke shim wrap via runtime.NewLambdaHandler, and
X-MS-Internal-Secret survives the identity-header strip for
internalAuth to validate.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A deployed-module lifecycle call arrives through the lambda envelope — the envelope IS the platform authentication (shim secret gate / Lambda IAM) and there is no per-session tunnel token to present, so internalAuth rejected it (header_present=false). Honor auth.PayloadTrusted exactly like RequireProxy already does; the mark is set only by runtime.NewLambdaHandler, never from inbound request data. E2E-found on the local deploy sim. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
What
Closes the documented UpgradeHandler KNOWN GAP (referenced from api-platform's
module_lifecycle.goupgradeRequestdoc comment): a deployed (non-tunnel) upgrade now runs its(from, to]migrations under the provided per-(app, module)r_*credential withsearch_path= the provided app schema — exactly like Install already does.Pairs with the api-platform deployed-lifecycle-transport PR: the api side already sends
{from, to, appId, schema, credential:{username, token}}on the non-tunnel path (field names mirrorinstallRequestverbatim, by design, so this adoption needs no wire change).How
UpgradeRequest=VersionRequest+InstallRequest— the api-sideupgradeRequestshape, verbatim.injectLifecycleContextout ofinjectInstallContext; install, upgrade, and downgrade now share the single context-folding implementation, so credential/schema semantics cannot drift between the lifecycle verbs. Same rules as install: env-URL supplies host/port/database, body supplies username/token,"credential": {}falls through to the dev pool.Module.Tx(the ScopeApp runner) already resolves the pool fromdb.CredentialFrom(ctx)andSET LOCALssearch_pathfromdb.SchemaFrom(ctx)— no runner changes needed.{from, to}body (the dev-tunnel path) behaves byte-for-byte as before — no schema/credential injected, env-pool fallback.Lambda-envelope reachability (checked, no change needed)
The lifecycle routes are mounted on
m.router(mountSystemRoutes), and both the real Lambda transport (lambda.Start(runtime.NewLambdaHandler(m.router))) and the dev/__mirrorstack/lambda-invokeshim wrap that same router.X-MS-Internal-Secretis exempt from the envelope's identity-header strip (msAuthSecretHeaders), sointernalAuthon/__mirrorstack/platform/*validates it normally. Lifecycle install/upgrade are therefore servable through the envelope as-is.Platform-side note (out of scope here)
EnsureModuleRole(api-platform) does not setALTER ROLE ... IN DATABASE ... SET search_path. That is harmless for this path (the SDK explicitlySET LOCALs the schema inside every migration transaction) and for runtime invokes that carryAppSchemain the envelope — but any invoke arriving withoutAppSchemaleaves ther_*role on the cluster-default"$user", publicsearch_path, which is what the hand-built E2E had to patch with a manualALTER ROLE. Recommend the api-platform transport PR either always populateAppSchemaor add theALTER ROLEdefault as defense-in-depth.Tests
{from, to}→ no schema, no credential (dev path pinned unchanged)."credential": {}→ falls through like install.go build ./...,go vet ./...,go test ./...green.🤖 Generated with Claude Code