Skip to content

Commit 9e9db38

Browse files
MajorTalclaude
andcommitted
docs(astro): build-time env vars do NOT propagate to the SSR runtime
Kychon flagged this in their @run402/astro 1.2.2 verification: process.env.KYCHON_ANON_KEY exported in the build shell surfaces as length 0 inside the SSR Lambda. The Run402-managed channel (RUN402_*) IS auto-injected; user secrets need a different path. Documented three options: run402 secrets set (canonical request-time secret path), x-run402-* request headers (per-tenant values the gateway already knows), bake into bundle at build (public + stable values). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 82d353f commit 9e9db38

1 file changed

Lines changed: 12 additions & 0 deletions

File tree

astro/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,18 @@ await cache.invalidate(`/${slug}`); // sub-second freshness
6969

7070
Every SSR response includes `x-run402-request-id`, `x-run402-release-id`, `x-run402-function`, `x-run402-cache` (`HIT` / `MISS` / `BYPASS`), `x-run402-cache-reason` (on bypass), `x-run402-cache-age` (on hit), `x-run402-locale`. When the function throws an uncaught exception, the response carries `x-run402-error-code: R402_SSR_RUNTIME_ERROR` and `x-run402-request-id` that you pass to `run402 logs --request-id <req>` for the full stack.
7171

72+
### Build-time env vars do NOT propagate to the SSR runtime
73+
74+
The SSR Lambda runs in a separate process from your build step. Anything exported in your `astro build` shell (e.g. `KYCHON_ANON_KEY`, `STRIPE_PUBLISHABLE_KEY`, a CI-injected secret) is visible during the build only — `process.env.YOUR_VAR` from inside an SSR-rendered page returns an empty string at request time. This is the correct security posture (build secrets shouldn't ship to a multi-tenant runtime), but it's surprising in retrospect.
75+
76+
Three options if your SSR route needs request-time config:
77+
78+
1. **Run402 secrets** — values you store via `run402 secrets set <key>` are injected into the Lambda env as `process.env.<KEY>` at deploy activation. This is the canonical request-time secret path.
79+
2. **Request headers** — for per-tenant / per-request values that the gateway already knows (project_id, release_id, locale, user_id, role), read them directly from the Web `Request` headers: `request.headers.get("x-run402-project-id")`, `getUserId(request)` / `getRole(request)` from `@run402/functions`.
80+
3. **Bake into the bundle at build** — for values that are public and stable across the lifetime of a release (e.g. an analytics site ID), import them in the page module so they get inlined into the bundled SSR source.
81+
82+
The Run402 anon key + service key + project ID + JWT secret + API base ARE auto-injected at deploy time (you'll see `RUN402_ANON_KEY`, `RUN402_SERVICE_KEY`, `RUN402_PROJECT_ID`, `RUN402_JWT_SECRET`, `RUN402_API_BASE` in `process.env` from inside the SSR runtime — those are the platform-managed channel).
83+
7284
## `<Run402Picture>` — runtime CMS images
7385

7486
For images coming from a DB row at SSR time (the common CMS pattern), use `<Run402Picture asset={page.hero_asset}>`. The `asset` prop is the `AssetRef` JSONB that `r.assets.put()` returned at upload time — store the whole object, not just the URL, then render directly.

0 commit comments

Comments
 (0)