Skip to content

Codex CLI 연결

OpenAI Codex CLI를 shunt로 향하게 하고 ChatGPT/Codex OAuth 계정 풀에 걸쳐 로드 밸런싱하기.

Updated View as Markdown

다른 연결 가이드는 모두 Claude Code를 백엔드로 라우팅합니다. 이 페이지는 그 반대 방향입니다: OpenAI Codex CLI를 shunt로 향하게 하고, shunt가 ChatGPT/Codex OAuth 계정 풀에 걸쳐 로드 밸런싱하게 합니다. shunt는 Codex CLI의 OpenAI Responses 트래픽을 그대로(verbatim) 릴레이하므로 — Anthropic 변환이 없습니다 — CLI는 chatgpt.com과 직접 통신할 때와 동일한 와이어 프로토콜로 shunt와 통신합니다.

이는 인바운드 Codex 엔드포인트의 실전편입니다. 그 페이지는 동작 명세(페일오버 표, 소진 시맨틱, 리로드 규칙)이고, 아래 단계는 처음부터 끝까지의 연결 안내입니다. 여기 인용한 Codex CLI 설정 키는 OpenAI의 구성 레퍼런스인증 문서에서 가져왔습니다.

1. shunt에서 엔드포인트 활성화

인바운드 엔드포인트는 옵트인입니다. 테이블을 추가하고 chatgpt_oauth 프로바이더를 가리키게 하세요(기본 프로바이더 이름은 codex입니다):

# shunt config.toml
[server.codex_endpoint]
provider = "codex"        # chatgpt_oauth 프로바이더여야 함; "codex"는 내장 기본값

codex는 내장 chatgpt_oauth 프로바이더이므로, 엔드포인트를 활성화하는 데 [providers.codex] 블록은 필요 없습니다. 다른 이름의 프로바이더를 가리키게 하려면 [providers.<name>] 테이블로 선언하고(구성 레퍼런스 참고) 위의 provider = "<name>"을 설정하세요.

shunt check                # 엔드포인트의 프로바이더가 존재하고 chatgpt_oauth인지 검증
shunt run

[server.codex_endpoint]가 없으면 어떤 라우트도 등록되지 않고 shunt의 기본 HTTP 화면은 그대로 유지됩니다. 있으면 시작 검증이 알 수 없는 providerauthchatgpt_oauth가 아닌 프로바이더를 거부합니다 — 이 엔드포인트는 운영자의 Codex bearer를 주입하므로 chatgpt_oauth 프로바이더만 자격이 있습니다. 모든 키는 구성 레퍼런스를 참고하세요.

2. Codex CLI를 shunt로 향하게 하기

Codex CLI는 사용하는 base URL이 무엇이든 그 뒤에 /responses를 붙이므로(Codex는 OpenAI Responses 와이어 프로토콜을 사용합니다 — wire_api = "responses"가 유일하게 지원되는 값입니다), shunt는 세 개의 라우트를 등록하며 아래의 어떤 클라이언트 형태든 그중 하나에 도달합니다:

Codex CLI ~/.codex/config.toml 도달하는 shunt 라우트
커스텀 프로바이더 base_url = ".../v1" POST /v1/responses
openai_base_url = ".../v1" POST /v1/responses
chatgpt_base_url = ".../backend-api/codex" POST /backend-api/codex/responses

권장 — 커스텀 모델 프로바이더. requires_openai_auth = false이면 CLI 관점에서는 인증이 없는 상태이므로 로컬 codex login이 전혀 필요 없고 — 계정은 shunt가 공급합니다 — shunt 토큰을 제시하는 두 방식(3단계) 모두를 지원합니다:

# ~/.codex/config.toml
model_provider = "shunt"          # 활성 프로바이더로 선택
model = "gpt-5.6-sol"             # 자격이 부여된 슬러그 — 5단계 참고

[model_providers.shunt]
name = "shunt"
base_url = "http://127.0.0.1:3001/v1"
wire_api = "responses"            # 유일하게 지원되는 값이자 기본값
requires_openai_auth = false      # 인증은 shunt가 처리; CLI는 여기서 ChatGPT/API 로그인이 불필요

더 단순하게 — base URL 오버라이드(프로바이더 블록 없음):

# ~/.codex/config.toml — 하나를 고르세요
openai_base_url  = "http://127.0.0.1:3001/v1"                 # + OPENAI_API_KEY = 사용자의 shunt 토큰
chatgpt_base_url = "http://127.0.0.1:3001/backend-api/codex"  # 루프백 전용 (ChatGPT 로그인 모드)

openai_base_url + OPENAI_API_KEY=<shunt-token>은 LiteLLM/llmgateway 방식입니다 — CLI가 토큰을 Authorization: Bearer로 보내고 shunt가 이를 받아들이므로(3단계), 이 형태는 [server.auth]가 있어도 동작합니다. chatgpt_base_url은 CLI를 ChatGPT 로그인 모드로 유지하며 자체 ChatGPT 토큰을 보내는데, shunt는 이를 클라이언트 토큰으로 받아들일 수 없습니다 — 게이팅되지 않은(루프백) shunt에서 사용하세요. 두 방식 모두 CLI를 자체 OpenAI/ChatGPT 인증 모드로 실행하므로 로컬 codex login이 필요하지만, 위의 커스텀 프로바이더는 그것조차 피합니다.

CLI 자체의 로컬 ~/.codex/auth.json 로그인은 어느 계정이 응답하는지와 무관합니다 — 모든 요청은 shunt의 풀에서 계정을 가져옵니다. 루프백 base_url은 평문 http://로 두어도 되지만, 원격이라면 https://를 사용하세요. shunt 프로바이더에 supports_websockets = true를 설정하지 마세요 — 이 엔드포인트는 HTTP/SSE 전용입니다(아래 참고).

3. shunt 클라이언트 토큰 제시 ([server.auth]가 설정된 경우)

shunt에 [server.auth]가 구성되어 있다면 — 루프백을 넘어서는 모든 경우에 권장됩니다 — shunt 클라이언트 토큰을 두 가지 방식 중 하나로 제시하세요. shunt는 둘 다 받아들입니다.

A. OpenAI 스타일 Bearer 키로 — LiteLLM/llmgateway 방식. shunt 토큰을 API 키로 설정하면 CLI가 이를 Authorization: Bearer <shunt-token>으로 보냅니다:

# ~/.codex/config.toml — 내장 openai 프로바이더 (프로바이더 블록 불필요).
# base URL은 OPENAI_BASE_URL 환경 변수가 아니라 여기에 설정하세요(2단계의 주의 참고).
openai_base_url = "http://127.0.0.1:3001/v1"
# 그다음 토큰만 env로 제시 — CLI가 이를 Authorization: Bearer로 보냅니다
export OPENAI_API_KEY="<shunt-token>"
# 또는 커스텀 프로바이더 — env_key가 Bearer가 됩니다
[model_providers.shunt]
base_url = "http://127.0.0.1:3001/v1"
wire_api = "responses"
env_key = "SHUNT_TOKEN"      # $SHUNT_TOKEN을 읽어 Authorization: Bearer로 전송

B. x-shunt-token 헤더로(또는 [server.auth].header가 지정하는 이름). 헤더를 붙일 수 있는 것은 커스텀 프로바이더뿐입니다. env_http_headers로 비밀을 config.toml 밖에 두세요:

[model_providers.shunt]
name = "shunt"
base_url = "http://127.0.0.1:3001/v1"
wire_api = "responses"
requires_openai_auth = false
env_http_headers = { "x-shunt-token" = "SHUNT_TOKEN" }   # $SHUNT_TOKEN을 읽음
export SHUNT_TOKEN="<token>"

(또는 http_headers = { "x-shunt-token" = "<token>" }로 직접 써 넣어도 됩니다.) 어느 쪽이든 토큰은 [server.auth]의 목록과 대조되며, 토큰이 없거나 틀리면 401 authentication_error를 반환합니다. [server.auth] 자체가 없으면 엔드포인트는 도달할 수 있는 누구에게나 열려 있습니다 — 루프백에는 괜찮지만 공유 게이트웨이에는 적절하지 않습니다. 클라이언트의 bearer/헤더는 shunt에 인증하는 데에 사용되며, 제거되고 Codex 백엔드로는 절대 전달되지 않습니다.

4. shunt에서 계정 풀 프로비저닝

이 엔드포인트는 Codex 멀티 계정의 풀을 그대로 재사용합니다 — 계정은 CLI의 호스트가 아니라 shunt의 호스트에서 프로비저닝하세요:

codex login                       # ChatGPT 계정으로 로그인 (브라우저 플로우)
shunt login codex --name main     # 그것을 shunt 스토어로 가져오기
# 풀에 계정을 추가할 때마다 각자의 로그인 + 가져오기가 필요합니다(별도의 ChatGPT
# 계정): `codex login` 후 `shunt login codex --name backup`, 이런 식으로 반복합니다.
# shunt config.toml
[[providers.codex.accounts]]
name = "main"

[[providers.codex.accounts]]
name = "backup"     # 선언한 이름은 모두 위에서 프로비저닝되어야 합니다. 프로비저닝되지 않은
                    # 계정은 페일오버에서 건너뜁니다(자격 증명 해석에 실패하므로)

선택은 세션 스티키입니다: Codex CLI 자체의 session-id 요청 헤더가 계정의 키가 되므로, 하나의 대화는 그 계정이 정상인 동안 같은 계정에 머무르다가 페일오버합니다(429 → 로테이션, 401 → 갱신 + 재시도, 5xx → 쿨다운 + 로테이션). 성공한 풀링 응답에는 x-shunt-account: <name> 헤더가 실립니다. [server.auth]가 활성화된 공유 게이트웨이에서는 스티키 키가 인증된 클라이언트별로 스코프되므로, 우연히 같은 session-id를 보내는 서로 다른 클라이언트도 서로 다른 스티키 키를 사용합니다 — 클라이언트 간 스티키 키 충돌을 방지합니다. (키는 서로 구별되지만, 서로 다른 두 키가 같은 계정으로 해싱될 수는 있습니다.)

[[providers.codex.accounts]]하나도 구성되지 않았고 스토어도 비어 있으면, 엔드포인트는 기본 ~/.codex/auth.json 자격 증명 하나로 폴백합니다 — 풀도 페일오버도 없습니다 — 따라서 [server.codex_endpoint]를 설정하는 즉시 Codex 로그인 하나만으로도 동작합니다.

5. 자격이 부여된 모델 선택

6. 검증

세 라우트 중 어디로든 보낸 원시 Responses 요청은 그대로 릴레이되어야 하며, (풀링된 경우) x-shunt-account 헤더를 반환해야 합니다:

curl -N -i -X POST http://127.0.0.1:3001/v1/responses \
  -H "content-type: application/json" \
  -H "x-shunt-token: <token>" \
  -d '{"model":"gpt-5.6-sol","input":"say hi","stream":true}'
  • content-type: text/event-streamx-shunt-account: 헤더가 있는 200 ⇒ 풀이 요청을 처리했고 SSE가 변경 없이 릴레이된 것입니다.
  • 401 authentication_errorx-shunt-token이 없거나 유효하지 않습니다(3단계).
  • 모델을 지목하는 400 ⇒ 그 계정에 해당 슬러그의 자격이 없습니다(5단계).

그다음 실제 Codex CLI로 한 턴을 실행해 보세요 — CLI 쪽은 base URL 외에 코드 변경 없이 풀을 왕복 합니다. 이 경로가 /v1/messages 경로와 어떻게 다른지는 인바운드 Codex 엔드포인트를, 등록된 라우트는 HTTP 엔드포인트를 참고하세요.

HTTP/SSE 전용

대상 프로바이더에 websocket = true가 설정되어 있어도 이 엔드포인트는 항상 HTTP 전송을 사용하므로, Codex CLI의 shunt 프로바이더에서는 supports_websockets를 꺼진 기본값 그대로 두세요. 실험적 Codex WebSocket v2 전송은 이 엔드포인트의 범위 밖이며 후속 작업으로 추적되고 있습니다.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close