);
diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx
index 865275b1e3d..ce21cf19a7b 100644
--- a/src/app/not-found.tsx
+++ b/src/app/not-found.tsx
@@ -1,38 +1,29 @@
"use client";
-/**
- * Custom Not Found Page — FASE-04 Error Handling
- *
- * Displayed when a user navigates to a non-existent route.
- */
-
import Link from "next/link";
export default function NotFound() {
return (
404
-
+
Page not found
-
+
The page you're looking for doesn't exist or has been moved.
Go to Dashboard
diff --git a/src/instrumentation.ts b/src/instrumentation.ts
index 9f2a5ee5ecf..d537bd46808 100644
--- a/src/instrumentation.ts
+++ b/src/instrumentation.ts
@@ -8,9 +8,20 @@
* @see https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation
*/
+import crypto from "crypto";
+
+function ensureJwtSecret(): void {
+ if (!process.env.JWT_SECRET || process.env.JWT_SECRET.trim() === "") {
+ const generated = crypto.randomBytes(48).toString("base64");
+ process.env.JWT_SECRET = generated;
+ console.log("[STARTUP] JWT_SECRET auto-generated (random 64-char secret)");
+ }
+}
+
export async function register() {
// Only run on the server (not during build or in Edge runtime)
if (process.env.NEXT_RUNTIME === "nodejs") {
+ ensureJwtSecret();
// Console log file capture (must be first — before any logging occurs)
const { initConsoleInterceptor } = await import("@/lib/consoleInterceptor");
initConsoleInterceptor();
diff --git a/src/proxy.ts b/src/proxy.ts
index 8faf273d783..413f16815cc 100644
--- a/src/proxy.ts
+++ b/src/proxy.ts
@@ -6,12 +6,7 @@ import { isPublicRoute, verifyAuth, isAuthRequired } from "./shared/utils/apiAut
import { checkBodySize, getBodySizeLimit } from "./shared/middleware/bodySizeGuard";
import { isDraining } from "./lib/gracefulShutdown";
-// FASE-01: Fail-fast — no hardcoded fallback. Server must have JWT_SECRET configured.
-if (!process.env.JWT_SECRET) {
- console.error("[SECURITY] JWT_SECRET is not set. Authentication will fail.");
-}
-
-const SECRET = new TextEncoder().encode(process.env.JWT_SECRET);
+const SECRET = new TextEncoder().encode(process.env.JWT_SECRET || "");
export async function proxy(request) {
const { pathname } = request.nextUrl;
diff --git a/src/shared/utils/secretsValidator.ts b/src/shared/utils/secretsValidator.ts
index c753885638c..f2926356db9 100644
--- a/src/shared/utils/secretsValidator.ts
+++ b/src/shared/utils/secretsValidator.ts
@@ -32,8 +32,8 @@ const SECRET_RULES = [
{
name: "JWT_SECRET",
minLength: 32,
- required: true,
- description: "JWT signing secret for dashboard authentication",
+ required: false,
+ description: "JWT signing secret for dashboard authentication (auto-generated if not set)",
generateHint: "openssl rand -base64 48",
},
{
diff --git a/src/shared/validation/schemas.ts b/src/shared/validation/schemas.ts
index 6a82fce7222..9208957bd6b 100644
--- a/src/shared/validation/schemas.ts
+++ b/src/shared/validation/schemas.ts
@@ -69,6 +69,7 @@ export const updateSettingsSchema = z.object({
logRetentionDays: z.number().int().min(1).max(365).optional(),
cloudUrl: z.string().max(500).optional(),
baseUrl: z.string().max(500).optional(),
+ setupComplete: z.boolean().optional(),
});
// ──── Auth Schemas ────