{"record":{"id":"b1f170fc4c752c30","repo":"abhigyanpatwari/GitNexus","slug":"refusing-to-start-eval-server-on-non-loopback-host","errorCode":null,"errorMessage":"Refusing to start eval-server on non-loopback host ${host} without authentication. Set GITNEXUS_AUTH_TOKEN or bind to 127.0.0.1, localhost, or ::1.","messagePattern":"Refusing to start eval-server on non-loopback host (.+?) without authentication\\. Set GITNEXUS_AUTH_TOKEN or bind to 127\\.0\\.0\\.1, localhost, or ::1\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"gitnexus/src/cli/eval-server.ts","lineNumber":155,"sourceCode":"  host: string,\n  env: NodeJS.ProcessEnv,\n  cwd: string = process.cwd(),\n): { token?: string; warning?: string } {\n  try {\n    return { token: resolveEvalServerAuthToken(env, cwd) };\n  } catch (error) {\n    if (isEvalServerLoopbackHost(host)) {\n      const reason = error instanceof Error ? error.message : String(error);\n      return { warning: `${reason} Continuing without authentication on loopback host ${host}.` };\n    }\n    throw error;\n  }\n}\n\n/** Refuse exposure of the eval-server query surface without authentication. */\nexport function assertSecureEvalServerBinding(host: string, authToken: string | undefined): void {\n  if (!authToken && !isEvalServerLoopbackHost(host)) {\n    throw new Error(\n      `Refusing to start eval-server on non-loopback host ${host} without authentication. ` +\n        'Set GITNEXUS_AUTH_TOKEN or bind to 127.0.0.1, localhost, or ::1.',\n    );\n  }\n}\n\n/** Validate the exact Bearer header while keeping token comparison constant-time. */\nexport function isEvalServerBearerAuthorized(\n  authorization: string | string[] | undefined,\n  authToken: string | undefined,\n): boolean {\n  if (!authToken) return true;\n\n  const expected = Buffer.from(`Bearer ${authToken}`, 'utf8');\n  const supplied = typeof authorization === 'string' ? Buffer.from(authorization, 'utf8') : null;\n  const sameLength = supplied?.length === expected.length;\n  const candidate = sameLength && supplied ? supplied : Buffer.alloc(expected.length);\n  return crypto.timingSafeEqual(candidate, expected) && sameLength;","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/ac9a4e9abd8fd3058c070b72c23402a4f887929a/gitnexus/src/cli/eval-server.ts#L137-L173","documentation":"A startup security guard: assertSecureEvalServerBinding refuses to expose the eval-server query surface on a non-loopback host unless a bearer token is configured (GITNEXUS_AUTH_TOKEN from the shell, .env.local, or .env). Loopback hosts (127.0.0.1, localhost, ::1) may run unauthenticated; everything else — 0.0.0.0, LAN IPs, hostnames resolving off-box — must have auth or the process exits.","triggerScenarios":"Starting eval-server with --host 0.0.0.0 or a LAN IP while GITNEXUS_AUTH_TOKEN is unset/whitespace-empty; also binding a hostname that does not resolve to a loopback address.","commonSituations":"Running the eval server inside Docker with -p port publishing (container binds 0.0.0.0 by default); exposing it on a LAN for another machine to query; CI containers binding all interfaces; production-style deployments reusing the dev tool.","solutions":["Set GITNEXUS_AUTH_TOKEN (export in the shell, or put GITNEXUS_AUTH_TOKEN=... in .env.local/.env at the server cwd) and restart","Or keep it unauthenticated and bind explicitly to a loopback host: --host 127.0.0.1 (in Docker, also publish with 127.0.0.1:PORT:PORT on the host)","For remote access, prefer an authenticated reverse-proxy/tunnel over widening the bind","Generate a strong token: openssl rand -hex 32; clients must then send 'Authorization: Bearer <token>'"],"exampleFix":"# before\n$ gitnexus eval-server --host 0.0.0.0\nError: Refusing to start eval-server on non-loopback host 0.0.0.0 without authentication.\n# after\n$ export GITNEXUS_AUTH_TOKEN=$(openssl rand -hex 32)\n$ gitnexus eval-server --host 0.0.0.0","handlingStrategy":"validation","validationCode":"import { isIP } from 'node:net';\nconst LOOPBACK = new Set(['127.0.0.1', 'localhost', '::1']);\nfunction isSafeBind(host: string, token: string | undefined): boolean {\n  return Boolean(token) || LOOPBACK.has(host);\n}\n// check BEFORE starting the server\nif (!isSafeBind(process.env.HOST ?? '127.0.0.1', process.env.GITNEXUS_AUTH_TOKEN)) {\n  throw new Error('Refusing unauthenticated non-loopback bind');\n}","typeGuard":"const isLoopbackHost = (host: string): boolean =>\n  host === '127.0.0.1' || host === 'localhost' || host === '::1';","tryCatchPattern":"try {\n  assertSecureEvalServerBinding(host, token);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('non-loopback')) {\n    // set GITNEXUS_AUTH_TOKEN or switch --host to 127.0.0.1, then restart\n  }\n}","preventionTips":["Default to --host 127.0.0.1; widen only when consumers actually need reach","In Docker, publish as 127.0.0.1:PORT:PORT on the host unless remote access is intended","Always pair a widened bind with a strong GITNEXUS_AUTH_TOKEN (openssl rand -hex 32)","Treat the refusal as a control, not a bug — do not work around it by binding loopback proxies without auth"],"tags":["security","network","server-bind","authentication","eval-server"],"backgroundTag":"server-bind-auth-required","analyzedSha":"ac9a4e9abd8fd3058c070b72c23402a4f887929a","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}