{"id":51034729,"url":"https://github.com/zaeval/gh-proxy","last_synced_at":"2026-06-22T04:30:33.247Z","repository":{"id":364229714,"uuid":"1266966219","full_name":"zaeval/gh-proxy","owner":"zaeval","description":"Relay server that lets machines without direct GitHub access use gh, git and the GitHub REST/GraphQL API. Zero-dependency Node.js.","archived":false,"fork":false,"pushed_at":"2026-06-12T06:17:23.000Z","size":28,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-12T08:13:15.119Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zaeval.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-12T05:31:05.000Z","updated_at":"2026-06-12T06:17:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/zaeval/gh-proxy","commit_stats":null,"previous_names":["zaeval/gh-proxy"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/zaeval/gh-proxy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaeval%2Fgh-proxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaeval%2Fgh-proxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaeval%2Fgh-proxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaeval%2Fgh-proxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zaeval","download_url":"https://codeload.github.com/zaeval/gh-proxy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaeval%2Fgh-proxy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34635032,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-22T02:00:06.391Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-22T04:30:32.469Z","updated_at":"2026-06-22T04:30:33.241Z","avatar_url":"https://github.com/zaeval.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gh-proxy\n\n**GitHub에 직접 접근할 수 없는 PC에서 `gh`, `git`, GitHub REST/GraphQL API를 쓸 수 있게 해주는 중계(릴레이) 서버.**\n\n의존성 없는(zero-dependency) 단일 Node.js 파일로 동작합니다.\n\n```\n┌──────────────────┐         ┌──────────────────┐         ┌─────────────────┐\n│  클라이언트 PC    │  HTTP   │   gh-proxy 서버   │  HTTPS  │   github.com    │\n│ (GitHub 접근 불가)│ ──────► │ (GitHub 접근 가능)│ ──────► │ api.github.com  │\n│  gh / git / curl │         │   이 저장소       │         │ codeload, ...   │\n└──────────────────┘         └──────────────────┘         └─────────────────┘\n```\n\n## 동작 모드\n\n하나의 포트에서 두 가지 모드를 동시에 제공합니다.\n\n| 모드 | 사용법 | 용도 |\n|------|--------|------|\n| **① 포워드 프록시 (CONNECT 터널)** — 권장 | 클라이언트에서 `HTTPS_PROXY=http://\u003c프록시호스트\u003e:8788` 만 설정 | `gh`·`git`이 **수정 없이 그대로** 동작. TLS는 클라이언트↔GitHub 종단간 유지 |\n| **② 리버스 프록시 (REST 패스스루)** | `http://\u003c프록시호스트\u003e:8788/api/v3/...` 등으로 직접 호출 | `curl`/스크립트로 GitHub API 호출, `git clone http://\u003c프록시호스트\u003e:8788/owner/repo.git` |\n\n상세한 엔드포인트 명세는 **[docs/API-CONTRACT.md](docs/API-CONTRACT.md)** 를 참고하세요. 실행 중인 서버에서는 같은 문서를 브라우저에서 바로 볼 수 있습니다 — **`\u003cPUBLIC_BASE\u003e/contract`** (HTML 렌더링, `?raw=1`로 원본 마크다운). 이 엔드포인트는 토큰 없이 열람 가능합니다.\n\n## 빠른 시작\n\n### 서버 PC (GitHub에 접근 가능한 이 컴퓨터)\n\n```powershell\ngit clone https://github.com/zaeval/gh-proxy.git\ncd gh-proxy\ncopy .env.example .env     # PUBLIC_HOST를 외부에서 접근하는 호스트명으로 수정\nnpm start\n```\n\n`.env`에서 반드시 확인할 항목:\n\n```ini\nPORT=8788\n# 클라이언트가 이 서버에 접근할 때 쓰는 호스트명(:포트).\n# Link 헤더·JSON 본문의 api.github.com URL이 이 주소로 재작성됩니다.\nPUBLIC_HOST=my-pc.example.internal:8788\n```\n\n방화벽에서 해당 포트의 인바운드를 허용해야 합니다(Windows):\n\n```powershell\nNew-NetFirewallRule -DisplayName \"gh-proxy\" -Direction Inbound -Protocol TCP -LocalPort 8788 -Action Allow\n```\n\n### 클라이언트 PC (GitHub에 접근 불가능한 컴퓨터)\n\n연결 확인:\n\n```sh\ncurl http://\u003c프록시호스트\u003e:8788/healthz\n```\n\n**gh 사용 (모드 ①):**\n\n```powershell\n# PowerShell\n$env:HTTPS_PROXY = \"http://\u003c프록시호스트\u003e:8788\"\ngh api rate_limit\ngh repo clone owner/repo\n```\n\n```sh\n# bash\nexport HTTPS_PROXY=http://\u003c프록시호스트\u003e:8788\ngh api rate_limit\n```\n\n**gh 인증** — 클라이언트의 브라우저는 github.com에 접근할 수 없으므로:\n\n```sh\n# 방법 1: 토큰 직접 입력 (서버 PC 등에서 발급한 PAT)\necho \u003cPAT\u003e | gh auth login --with-token        # HTTPS_PROXY 설정 상태에서\n\n# 방법 2: 디바이스 플로우 — 표시되는 코드를 휴대폰 등 다른 기기에서\n#         https://github.com/login/device 에 입력\ngh auth login\n```\n\n**git 사용:**\n\n```sh\n# 방법 A: 프록시 환경변수 (HTTPS_PROXY 설정 상태에서 평소처럼)\ngit clone https://github.com/owner/repo.git\n\n# 방법 B: 리버스 프록시 경로로 직접 (환경변수 불필요)\ngit clone http://\u003c프록시호스트\u003e:8788/owner/repo.git\n```\n\n**curl로 API 직접 호출 (모드 ②):**\n\n```sh\ncurl -H \"Authorization: Bearer \u003c토큰\u003e\" http://\u003c프록시호스트\u003e:8788/api/v3/repos/cli/cli\ncurl -X POST -H \"Authorization: Bearer \u003c토큰\u003e\" -d '{\"query\":\"query{viewer{login}}\"}' \\\n     http://\u003c프록시호스트\u003e:8788/api/graphql\n```\n\n## 스킬 설치 — Claude Code \u0026 Codex (클라이언트 PC)\n\n클라이언트 PC의 **Claude Code** 또는 **Codex CLI**가 이 프록시 사용법을 알도록 `gh-proxy`\n스킬을 설치합니다. 두 도구는 동일한 `SKILL.md` 포맷을 쓰므로 같은 파일을 각자의 skills\n디렉터리(`~/.claude/skills/gh-proxy/`, `~/.codex/skills/gh-proxy/`)에 넣으면 됩니다.\n\n**실행 중인 서버에서 원터치 설치** — 서버가 자기 주소를 채워 넣은 설치 스크립트를 제공합니다:\n\n```sh\n# Linux / macOS / WSL  (둘 다 / 한쪽만)\ncurl -fsSL http://\u003c프록시호스트\u003e:8788/install.sh | sh\ncurl -fsSL http://\u003c프록시호스트\u003e:8788/install.sh | sh -s -- claude\ncurl -fsSL http://\u003c프록시호스트\u003e:8788/install.sh | sh -s -- codex\n```\n\n```powershell\n# Windows\nirm http://\u003c프록시호스트\u003e:8788/install.ps1 | iex\n```\n\n설치 안내 페이지(HTML)는 브라우저에서 **`\u003cPUBLIC_BASE\u003e/install`** 로 볼 수 있습니다.\n토큰이 설정된 프록시라면 설치 후 `GH_PROXY_URL`·`GH_PROXY_TOKEN` 환경변수를 지정하세요(스크립트는 토큰을 포함하지 않습니다).\n\n**저장소 체크아웃에서 설치** (스크립트 사용):\n\n```powershell\n.\\scripts\\install-skill.ps1 -ProxyUrl http://\u003c프록시호스트\u003e:8788 -Target both\n```\n\n```sh\n./scripts/install-skill.sh http://\u003c프록시호스트\u003e:8788 both\n```\n\n수동 설치: `curl -fsSL \u003cPUBLIC_BASE\u003e/skill -o ~/.claude/skills/gh-proxy/SKILL.md`\n(Codex는 `~/.codex/skills/gh-proxy/SKILL.md`). 자세한 안내는 `\u003cPUBLIC_BASE\u003e/install` 참고.\n\n## 환경변수 (.env)\n\n| 변수 | 기본값 | 설명 |\n|------|--------|------|\n| `PORT` | `8788` | 리스닝 포트 |\n| `BIND_HOST` | `0.0.0.0` | 바인딩 인터페이스 |\n| `PUBLIC_HOST` | `localhost:\u003cPORT\u003e` | **클라이언트가 접근하는 외부 호스트명(:포트)**. URL 재작성 기준 |\n| `PROXY_TOKEN` | (없음) | 프록시 공유 시크릿. 설정 시 모든 중계 요청에 토큰 필요 |\n| `GITHUB_TOKEN` | (없음) | Authorization 없는 REST/GraphQL 요청에 서버가 주입할 GitHub 토큰 |\n| `REWRITE_BODY_URLS` | `true` | JSON 본문 내 `api.github.com` URL을 `PUBLIC_HOST`로 재작성 |\n| `FOLLOW_REDIRECTS` | `true` | GitHub 리다이렉트(codeload 등)를 서버 측에서 추적 |\n| `MAX_REDIRECTS` | `5` | 서버측 리다이렉트 추적 한도 |\n| `UPSTREAM_TIMEOUT_MS` | `30000` | 업스트림 요청 타임아웃 |\n| `CONNECT_TIMEOUT_MS` | `10000` | CONNECT 터널 연결 타임아웃 |\n| `EXTRA_ALLOWED_HOSTS` | (없음) | 터널 허용 호스트 추가(쉼표 구분, `*.` 와일드카드 지원) |\n| `LOG_REQUESTS` | `true` | 요청 로그 출력 |\n| `TLS_CERT_FILE` / `TLS_KEY_FILE` | (없음) | 프록시 자체 TLS (GH_HOST 에뮬레이션 모드에 필요) |\n\n## 보안 주의사항\n\n- 이 프록시는 **신뢰할 수 있는 내부망**에서 쓰는 것을 전제로 합니다. 외부에 노출되는 환경이라면 반드시 `PROXY_TOKEN`을 설정하세요.\n- `GITHUB_TOKEN` 주입 기능을 켜면 프록시에 접근 가능한 누구나 그 토큰의 권한으로 GitHub를 사용할 수 있습니다. **`PROXY_TOKEN`과 함께** 사용하세요.\n- 터널링은 GitHub 관련 도메인 허용 목록(`github.com`, `*.github.com`, `*.githubusercontent.com`, `ghcr.io`, `githubcopilot.com` 등)으로 제한되어 오픈 프록시로 악용될 수 없습니다.\n\n## 백그라운드 실행\n\n**Windows (작업 스케줄러):**\n\n```powershell\n$action = New-ScheduledTaskAction -Execute \"node\" -Argument \"server.js\" -WorkingDirectory \"C:\\path\\to\\gh-proxy\"\n$trigger = New-ScheduledTaskTrigger -AtStartup\nRegister-ScheduledTask -TaskName \"gh-proxy\" -Action $action -Trigger $trigger -RunLevel Limited\nStart-ScheduledTask -TaskName \"gh-proxy\"\n```\n\n**Linux (systemd):**\n\n```ini\n# /etc/systemd/system/gh-proxy.service\n[Unit]\nDescription=gh-proxy GitHub relay\nAfter=network.target\n\n[Service]\nWorkingDirectory=/opt/gh-proxy\nExecStart=/usr/bin/node server.js\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n```\n\n## 기존 웹서버(nginx 등) 뒤에 경로 프리픽스로 배포하기\n\n이미 도메인과 TLS를 가진 nginx가 있다면 `https://example.com/proxy/gh` 같은 경로 아래에 붙일 수 있습니다.\n\n```nginx\nlocation = /proxy/gh { return 301 /proxy/gh/; }\nlocation ^~ /proxy/gh/ {\n    proxy_pass http://127.0.0.1:8788/;   # trailing slash → prefix 제거\n    proxy_http_version 1.1;\n    proxy_set_header Connection \"\";\n    proxy_set_header X-Forwarded-Proto $scheme;\n\n    client_max_body_size 0;          # git push / 릴리스 자산 업로드\n    proxy_request_buffering off;     # 업로드 스트리밍\n    proxy_buffering off;             # tarball 등 다운로드 스트리밍\n    proxy_read_timeout 300s;\n    proxy_send_timeout 300s;\n}\n```\n\n`.env`의 `PUBLIC_HOST`에는 **경로까지 포함한 전체 URL**을 넣습니다:\n\n```ini\nPUBLIC_HOST=https://example.com/proxy/gh\n```\n\nLink 헤더·본문 URL이 이 주소 기준으로 재작성되어 페이지네이션·git clone이 그대로 동작합니다.\n\n\u003e **주의**: 경로 프리픽스 배포에서는 모드 ①(CONNECT, `HTTPS_PROXY`)을 쓸 수 없습니다 — nginx는 CONNECT를 중계하지 않습니다. 이 경우 모드 ②(REST/GraphQL/git 경로)가 주 사용법이며, `HTTPS_PROXY`가 필요하면 프록시 포트(8788)에 직접 접근 가능해야 합니다. 또한 인터넷에 공개되는 배포라면 반드시 `PROXY_TOKEN`을 설정하세요.\n\n## Docker / WSL 배포\n\n이미지에 런타임 의존성이 없어 Docker 빌드가 매우 가볍습니다.\n\n```sh\ncp .env.example .env        # PUBLIC_HOST, (필요 시) PROXY_TOKEN 설정\ndocker compose up -d --build\n# 기본 compose는 127.0.0.1:8788 로 노출 (standalone)\n```\n\n**프론트 nginx가 Docker 컨테이너인 경우** — gh-proxy를 같은 Docker 네트워크에 올리면\nnginx가 호스트 게이트웨이 IP 대신 **컨테이너 이름으로 직접** 도달할 수 있어, WSL/호스트\nIP가 바뀌어도 끊기지 않습니다. nginx가 속한 외부 네트워크에 join 하는 compose 예시:\n\n```yaml\n# docker-compose.ucut.yml  (배포 호스트 전용, 커밋하지 않음)\nservices:\n  gh-proxy:\n    build: .\n    container_name: gh-proxy\n    env_file: [.env]\n    environment: { BIND_HOST: 0.0.0.0 }\n    restart: unless-stopped\n    networks: [nginxnet]\nnetworks:\n  nginxnet:\n    external: true\n    name: \u003c프론트-nginx-의-네트워크\u003e   # 예: nginx-proxy_default\n```\n\n```sh\ndocker compose -f docker-compose.ucut.yml up -d --build\n```\n\n그리고 nginx는 컨테이너 이름으로 프록시합니다:\n\n```nginx\nlocation ^~ /proxy/gh/ { proxy_pass http://gh-proxy:8788/; ... }\n```\n\n\u003e 이 저장소의 실제 배포(`ucut.in`)가 이 구성입니다: WSL Docker의 nginx가\n\u003e `nginx-proxy_default` 네트워크에서 `gh-proxy` 컨테이너로 프록시하고, 백엔드는\n\u003e 컨테이너 안에서 GitHub로 중계합니다. (WSL은 Windows 네트워크를 공유하므로\n\u003e 컨테이너에서 github.com 도달 가능.)\n\n## 알려진 제약\n\n- `GH_HOST=\u003c프록시호스트\u003e` 방식(GitHub Enterprise 에뮬레이션)은 `gh`가 **HTTPS를 강제**하므로 평문 HTTP로는 동작하지 않습니다\n  (`http: server gave HTTP response to HTTPS client` — gh v2.92.0에서 확인).\n  이 방식을 쓰려면 `TLS_CERT_FILE`/`TLS_KEY_FILE`로 프록시에 클라이언트가 신뢰하는 인증서를 설정해야 합니다.\n  **권장 방식은 `HTTPS_PROXY`(모드 ①)이며, 평문 HTTP로도 안전하게 동작합니다** (TLS가 터널 내부에서 종단간 유지).\n- WebSocket이 필요한 기능(`gh codespace` 등)은 모드 ①(CONNECT)로만 동작합니다.\n\n## 라이선스\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzaeval%2Fgh-proxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzaeval%2Fgh-proxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzaeval%2Fgh-proxy/lists"}