GitNexus([email protected], 레포를 그래프로 인덱싱해서 AI 에이전트가 MCP로 질의하게 해주는 npm 도구)를 tools 네임스페이스에 상시 서비스로 띄운 기록. 설계 논의부터 디스크 장애·VM 리사이즈까지, 실제로 겪은 삽질을 순서대로 남긴다.

1. 사전 확인 — 네임스페이스/노출 범위/배치

2. 이미지 — 공식 Dockerfile.cli를 못 쓴 이유

GitNexus 저장소의 Dockerfile.cli를 그대로 쓰려고 했는데, 주석을 읽어보니 의도적으로 web UI 번들을 뺀 API 전용 이미지였다(Render의 분리형 아키텍처용 — API 서버 + 별도 web 프록시 컨테이너). 반면 npm 패키지 자체(registry.npmjs.org/gitnexus/-/gitnexus-1.6.12.tgz)를 까보면 package/web/에 115개 파일(빌드된 UI 번들)이 그대로 들어있다는 걸 확인했다 — 즉 npm install -g [email protected]만으로 웹 UI까지 다 포함된다. 처음 요청받은 대로 단순한 Dockerfile을 직접 작성했다(node:22-bookworm-slim + 빌드 도구 + 전역 설치).

3. 보안 — serve는 기본적으로 인증이 없다

실측으로 확인한 중요한 사실 두 가지:

① GITNEXUS_PUBLIC_ORIGIN을 설정하면 서버가 시작 자체를 거부한다. 로컬에서 직접 컨테이너를 돌려 확인:

Failed to start GitNexus server:
  GITNEXUS_PUBLIC_ORIGIN is set (...), but 'gitnexus serve' has no
  authentication yet. ... reach the server through a proxy that
  authenticates for it.

공식 README에도 같은 내용이 있었지만, 문서를 그대로 믿지 않고 실제로 컨테이너를 띄워서 재현했다. 그래서 이 값은 아예 안 넣는다.

② 결과적으로 쓰기 라우트(POST /api/analyze, DELETE /api/repo)는 loopback 오리진만 허용. 와일드카드 바인드(--host 0.0.0.0)에서 서버 자체 로그가 이렇게 경고한다:

[gitnexus serve] Bound to a wildcard address (0.0.0.0); browser write
routes accept only loopback origins (localhost/127.0.0.1/[::1]).

GITNEXUS_MCP_AUTH_TOKEN은 /api/mcp 라우트만 보호하는 별개 메커니즘이고(로그로 "Bearer authentication enabled for serve /api/mcp" 확인), 이 쓰기 제약과는 무관하다.

추가로 나중에 밝혀진 것(10번 항목): 이 단계에서는 "읽기는 되고 쓰기만 막힌다"고 판단했는데, 실제로는 번들 UI가 페이지 로드 시점에 읽기/쓰기 구분 없이 자기 자신의 localhost만 확인하도록 돼 있어서 NodePort로는 읽기 열람조차 안 됐다. 초기 판단이 틀렸던 부분 — 10번 항목에 정정 기록.

"그럼 서버로 띄운 의미가 없는 거 아닌가?"

이 제약을 발견하고 실제로 나온 질문. 답은: GitNexus는 애초에 웹 UI가 메인 용도가 아니다 — README 자체가 "CLI + MCP(권장) vs 웹 UI(데모/일회성 탐색용)"로 구분해놓았다. 그래서: