Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/inject-deployment-secret.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/vite-plugin": patch
---

Provide the deployment secret to server builds (solidjs/solid#3239): the generated server-function handler module now leads with `globalThis.__SOLID_SECRET__ ??= "<random-per-build>"`, giving the runtime's encrypted no-JS flash cookie a key with zero configuration. One value is generated per plugin instance, so a production build bakes a single secret into the emitted server chunk (shared by every instance of that deployment) and a dev session holds one for its lifetime. Server output only — the handler module is already hard-gated against client graphs — and an explicit `configureServerFunctionsServer({ secret })` still outranks it.
26 changes: 26 additions & 0 deletions src/server-functions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// conditions resolve the right half per environment. Any runtime satisfying
// that contract can be swapped in through `options.runtime` (SolidStart's,
// or your own).
import { randomBytes } from 'crypto';
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
import path from 'path';
import {
Expand Down Expand Up @@ -352,6 +353,23 @@ export function serverFunctions(
};
let currentServer: ViteDevServer | undefined;

// THE DEPLOYMENT SECRET (solidjs/solid#3239): the runtime's flash cookie
// — the no-JS form outcome — carries the submitted input, so it is
// AES-GCM encrypted under a key derived from a deployment-wide secret,
// and without one the outcome is withheld entirely. This plugin provides
// that secret with zero configuration through the internal
// `globalThis.__SOLID_SECRET__ ??=` contract: generated once per plugin
// instance, so a production build bakes one value into the emitted server
// chunk — every instance of that deployment shares it (a per-process
// value would silently lose flashes behind a load balancer) — and a dev
// session holds one for its lifetime (a restart invalidates in-flight
// flashes, which are 60-second one-shot cookies; the next render just
// reads "no flash"). Server output only, never the client graph. The
// `??=` keeps an explicit `configureServerFunctionsServer({ secret })` —
// or a value injected by an outer harness — authoritative.
const deploymentSecret = randomBytes(32).toString('hex');
const deploymentSecretSnippet = `globalThis.__SOLID_SECRET__ ??= ${JSON.stringify(deploymentSecret)};`;

const clientOptions: Pick<CompileOptions, 'directive' | 'definitions'> = {
directive,
definitions: {
Expand Down Expand Up @@ -415,6 +433,14 @@ export function serverFunctions(
// import is only emitted when the option is on, so disabled setups keep
// a server-component-free graph.
return [
// The deployment secret. Imports are hoisted above it, but nothing
// reads the global at module evaluation — the runtime resolves it
// lazily per encode/decode — so leading textually is just the honest
// placement. This module is loaded before any dispatch on both
// surfaces, and the generated SSR handler imports it at module load,
// so the secret is in place for the flash's encode (the form POST)
// and its decode (the render that follows the redirect) alike.
deploymentSecretSnippet,
// The user's `configure` module comes first: a side-effect import in
// the handler graph, evaluated before any dispatch on both surfaces
// (dev middleware and prod handler) and bundled into the handler
Expand Down
Loading