abhigyanpatwari/GitNexus · critical · Error

${PUBLIC_ORIGIN_ENV} is set (${raw}), but 'gitnexus serve' h

Error message

${PUBLIC_ORIGIN_ENV} is set (${raw}), but 'gitnexus serve' has no authentication yet. It would admit browser writes from that origin, and requests without an Origin header (curl, any script) already reach POST /api/analyze and DELETE /api/repo unauthenticated — so a reachable deployment would let anyone index and delete repositories. Unset ${PUBLIC_ORIGIN_ENV} and bind loopback (the default), or reach the server through a proxy that authenticates for it.

What it means

At startup, `gitnexus serve` runs assertServeAuthForPublicOrigin and refuses to start (surfaces as serve.startFailed, exits non-zero) when GITNEXUS_PUBLIC_ORIGIN is set but serve has no authentication — in this version isServeAuthConfigured() returns false unconditionally, so any set value fails. GITNEXUS_PUBLIC_ORIGIN is precisely the setting that admits a non-loopback browser origin to the write routes, and requests without an Origin header (curl, scripts) already reach POST /api/analyze and DELETE /api/repo unauthenticated, so a reachable deployment would let anyone index and delete repositories. There is deliberately no override flag.

Source

Thrown at gitnexus/src/server/middleware.ts:249

/**
 * Refuse to start when {@link PUBLIC_ORIGIN_ENV} is set and no `serve`
 * authentication is configured.
 *
 * {@link PUBLIC_ORIGIN_ENV} is the setting that makes a public bind usable — it
 * is what admits a non-loopback browser origin to the write routes. Until
 * {@link isServeAuthConfigured} can return `true`, setting it opens the door
 * with nothing behind it, so the door does not open at all. There is
 * deliberately no override flag: an escape hatch is the thing an operator sets
 * once and forgets, which is exactly the state this guards against.
 *
 * @throws when {@link PUBLIC_ORIGIN_ENV} is set without authentication. `serve`
 *   surfaces it as `serve.startFailed` and exits non-zero.
 */
export function assertServeAuthForPublicOrigin(): void {
  const raw = process.env[PUBLIC_ORIGIN_ENV]?.trim();
  if (!raw || isServeAuthConfigured()) return;
  throw new Error(
    `${PUBLIC_ORIGIN_ENV} is set (${raw}), but 'gitnexus serve' has no authentication yet. ` +
      `It would admit browser writes from that origin, and requests without an Origin header ` +
      `(curl, any script) already reach POST /api/analyze and DELETE /api/repo unauthenticated — ` +
      `so a reachable deployment would let anyone index and delete repositories. Unset ` +
      `${PUBLIC_ORIGIN_ENV} and bind loopback (the default), or reach the server through a proxy ` +
      `that authenticates for it.`,
  );
}

/**
 * Report at startup what {@link createWriteOriginGuard} will admit, so an
 * operator can see it without reproducing a 403. A wildcard bind always warns —
 * gating that on {@link PUBLIC_ORIGIN_ENV} being constructible would diagnose a
 * misconfigured value worse than an absent one.
 */
export function logOriginPolicy(boundHost?: string): void {
  const raw = process.env[PUBLIC_ORIGIN_ENV]?.trim();
  const publicOrigin = createPublicOriginMatcher(raw);

View on GitHub (pinned to aac7515d2a)

Solutions

  1. Unset GITNEXUS_PUBLIC_ORIGIN and keep the default loopback bind; reach the server from the same machine or via SSH tunnel (ssh -L 4747:localhost:4747)
  2. If you must expose it publicly, put an authenticating reverse proxy (oauth2-proxy, mTLS gateway, etc.) in front and access it through the proxy — do not set the env var on the bare server
  3. Check for stray definitions: `env | grep GITNEXUS_PUBLIC_ORIGIN`, docker-compose.yml environment blocks, systemd Environment= lines, .env files loaded by wrappers
  4. If you are building/authoring a fork with serve auth implemented, make isServeAuthConfigured() return true for your configured auth — the guard then passes by design

Example fix

# before (docker-compose.yml)
environment:
  - GITNEXUS_PUBLIC_ORIGIN=https://gitnexus.example.com   # serve exits: serve.startFailed
# after — loopback + tunnel, no public origin
environment:
  - GITNEXUS_HOME=/data/gitnexus
# access remotely via: ssh -L 4747:localhost:4747 host
Defensive patterns

Strategy: validation

Validate before calling

// Before spawning `gitnexus serve`, check the guard's precondition yourself:
const PUBLIC_ORIGIN_ENV = 'GITNEXUS_PUBLIC_ORIGIN';
if (process.env[PUBLIC_ORIGIN_ENV]?.trim()) {
  throw new Error(
    `${PUBLIC_ORIGIN_ENV} must not be set: serve has no authentication; ` +
    `bind loopback or front it with an authenticating proxy`,
  );
}

Try / catch

// If you programmatically start serve (e.g. in tests/containers):
try {
  await startServe();
} catch (err) {
  if (err instanceof Error && err.message.includes('has no authentication yet')) {
    delete process.env.GITNEXUS_PUBLIC_ORIGIN; // fall back to safe loopback mode
    await startServe();
  } else throw err;
}

Prevention

When it happens

Trigger: Starting `gitnexus serve` (directly, via npm script, or in Docker) with GITNEXUS_PUBLIC_ORIGIN=https://gitnexus.example.com in the environment while no serve auth exists. The throw happens during startup, before the listener opens — e.g. docker-compose with `-e GITNEXUS_PUBLIC_ORIGIN=...`, a systemd unit, or a .env exported into the shell.

Common situations: Trying to use the web UI from another machine by setting the public-origin env; copy-pasting a cloud deployment guide written for a future version that has serve auth; CI smoke tests that set every documented env var; leftover env from an experiment. The variable is also ignored-with-warning when malformed, but set-and-valid is the case that hard-fails.

Understand the failure class

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20). Data as JSON: /api/errors/3d584e37bdd126bf. Report an issue: GitHub.