diff --git a/.env.example b/.env.example index 5c5d5264..ff460c59 100644 --- a/.env.example +++ b/.env.example @@ -17,6 +17,14 @@ DEPLOYMENT_MODE="local" # Optional build-time release identifier. Use the same value for every replica # in one rollout; when omitted, each build generates a unique identifier. NEXT_DEPLOYMENT_ID="" +# Optional build-time override of the Next.js output mode ("standalone" or +# "export"). Unset defaults to the standard output on Vercel (its build +# adapter cannot consume standalone output) and "standalone" elsewhere. +NEXT_OUTPUT_MODE="" +# Auto-injected by Vercel during its builds; do not set manually. It switches +# the Next.js output mode away from standalone because Vercel's build adapter +# cannot consume standalone output. +VERCEL="" ALLOW_INSECURE_LOCAL_PRODUCTION="false" # Retained to opt deployment-gated media/image proxy surfaces into HTTP. ALLOW_LOCAL_NETWORK_PROXY="" diff --git a/docs/environment-variables.md b/docs/environment-variables.md index 2a3a0ff0..cc94b4ed 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -52,13 +52,14 @@ corepack pnpm byok:generate ## Deployment safety -| Variable | Purpose | -| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `DEPLOYMENT_MODE` | Selects local or hosted deployment safeguards and shared-store expectations. It does not block user-configured HTTP or private-network targets. | -| `NEXT_DEPLOYMENT_ID` | Optional build-time release ID shared by every replica in one rollout; drives Next.js version-skew protection and PWA cache rotation. | -| `ALLOW_INSECURE_LOCAL_PRODUCTION` | Explicitly allows production `local` mode without `ACCESS_PASSWORD`. Use only for private deployments that are not exposed to the internet. | -| `ALLOW_LOCAL_NETWORK_PROXY` | Allows HTTP on deployment-gated media/image proxy surfaces. Private addresses themselves are no longer blocked; provider, search, RAG, plugin, and MCP policies do not depend on this flag. | -| `TRUST_PROXY_HEADERS` | Trust forwarded proxy headers only when the hosting platform strips spoofed values. | +| Variable | Purpose | +| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `DEPLOYMENT_MODE` | Selects local or hosted deployment safeguards and shared-store expectations. It does not block user-configured HTTP or private-network targets. | +| `NEXT_DEPLOYMENT_ID` | Optional build-time release ID shared by every replica in one rollout; drives Next.js version-skew protection and PWA cache rotation. | +| `NEXT_OUTPUT_MODE` | Optional build-time override of the Next.js output mode (`standalone` or `export`). Unset defaults to the standard output on Vercel, whose build adapter cannot consume standalone builds, and `standalone` for Docker and self-hosted builds. | +| `ALLOW_INSECURE_LOCAL_PRODUCTION` | Explicitly allows production `local` mode without `ACCESS_PASSWORD`. Use only for private deployments that are not exposed to the internet. | +| `ALLOW_LOCAL_NETWORK_PROXY` | Allows HTTP on deployment-gated media/image proxy surfaces. Private addresses themselves are no longer blocked; provider, search, RAG, plugin, and MCP policies do not depend on this flag. | +| `TRUST_PROXY_HEADERS` | Trust forwarded proxy headers only when the hosting platform strips spoofed values. | `TRUST_PROXY_HEADERS` affects request identity used by deployment diagnostics and rate limiting. Leave it `false` unless Neo Chat is behind a trusted proxy diff --git a/next.config.ts b/next.config.ts index 2eca848f..06164b70 100644 --- a/next.config.ts +++ b/next.config.ts @@ -48,6 +48,19 @@ function getDeploymentId(phase: string): string { return resolveDeploymentId(process.env, fallbackDeploymentId); } +function resolveOutputMode(): NextConfig["output"] { + // Vercel's build adapter expects .next/next-server.js.nft.json, which + // standalone builds do not emit; Vercel packages functions itself, so it + // keeps the default output while Docker and self-hosted builds use + // standalone. NEXT_OUTPUT_MODE overrides the platform default explicitly. + const override = process.env.NEXT_OUTPUT_MODE?.trim(); + if (override && override !== "standalone" && override !== "export") { + console.warn(`Ignoring unsupported NEXT_OUTPUT_MODE: "${override}"`); + } + if (override === "standalone" || override === "export") return override; + return process.env.VERCEL ? undefined : "standalone"; +} + function createNextConfig(phase: string): NextConfig { const deploymentId = getDeploymentId(phase); const isE2EServer = @@ -55,7 +68,7 @@ function createNextConfig(phase: string): NextConfig { return { /* config options here */ - output: "standalone", + output: resolveOutputMode(), ...(isE2EServer && { distDir: ".next-e2e", typescript: { tsconfigPath: "tsconfig.e2e.json" }, diff --git a/src/__tests__/envExample.test.ts b/src/__tests__/envExample.test.ts index a6913e4c..53dc89bc 100644 --- a/src/__tests__/envExample.test.ts +++ b/src/__tests__/envExample.test.ts @@ -9,6 +9,8 @@ const REQUIRED_ENV_KEYS = [ "BYOK_ALLOW_EPHEMERAL_KEY", "DEPLOYMENT_MODE", "NEXT_DEPLOYMENT_ID", + "NEXT_OUTPUT_MODE", + "VERCEL", "ALLOW_INSECURE_LOCAL_PRODUCTION", "ALLOW_LOCAL_NETWORK_PROXY", "TRUST_PROXY_HEADERS",