{"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":"exception","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/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/cli/eval-server.ts#L137-L173","documentation":"The eval-server query surface exposes tool results (query/context/impact/detect_changes) that can read the knowledge graph; binding it to a non-loopback host without a bearer token would expose that surface to anything on the network. `assertSecureEvalServerBinding` refuses to start in that posture. Loopback hosts (127.x, ::1, localhost) are exempt.","triggerScenarios":"Running `gitnexus eval-server --host 0.0.0.0` (or a LAN hostname) without GITNEXUS_AUTH_TOKEN set in the environment, `.env`, or `.env.local`.","commonSituations":"Running the eval-server inside Docker or a devbox with a container-facing bind address; CI that binds 0.0.0.0 for health checks; forgetting to provide the token in a non-loopback deployment.","solutions":["Set GITNEXUS_AUTH_TOKEN in the shell environment or in `.env`/`.env.local`.","Bind to a loopback host instead: `--host 127.0.0.1`, `--host localhost`, or `--host ::1`.","Ensure the host string resolves to a loopback address before the assertion runs."],"exampleFix":"# before\ngitnexus eval-server --host 0.0.0.0\n# after\nGITNEXUS_AUTH_TOKEN=\"$TOKEN\" gitnexus eval-server --host 0.0.0.0","handlingStrategy":"validation","validationCode":"import { assertSecureEvalServerBinding, isEvalServerLoopbackHost } from './eval-server.js';\n// Resolve the token early so the failure is clear\nconst token = resolveEvalServerAuthToken(process.env);\nif (!isEvalServerLoopbackHost(host)) {\n  if (!token) throw new Error('GITNEXUS_AUTH_TOKEN required for non-loopback bind');\n}\nassertSecureEvalServerBinding(host, token);","typeGuard":"const isLoopbackHost = (h) =>\n  h === 'localhost' || h === '::1' || (/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(h) && h.startsWith('127.'));","tryCatchPattern":null,"preventionTips":["Always set GITNEXUS_AUTH_TOKEN when binding --host to anything other than 127.0.0.1/localhost/::1.","In Docker/CI, inject the token from a secret store rather than a committed file.","Prefer loopback binds unless network exposure is intentional."],"tags":["eval-server","security","auth","network","fail-closed"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}