Communitygithub.com

lan-direct

앱의 트래픽은 실제 ISP를 통해 라우팅하고, AI 에이전트는 VPN에 연결된 상태로 유지하는 스킬입니다. 소켓 바인딩 기반으로 동작하며 관리자 권한이나 전역 설정이 필요 없습니다.

lan-direct란 무엇인가요?

lan-direct 스킬은 Claude Code와 Codex 환경에서 AI 에이전트가 VPN에 연결된 상태를 유지하면서도 애플리케이션의 트래픽만 실제 ISP(인터넷 서비스 제공자)를 통해 라우팅할 수 있게 해줍니다. 이 스킬의 핵심 메커니즘은 **소켓별 바인딩(per-socket binding)**으로, 라우팅 테이블, 시스템 프록시 설정, VPN 클라이언트 설정을 전혀 변경하지 않습니다. 대신 자식 프로세스에 환경 변수를 설정하거나 도구별 소스 바인딩 플래그를 전달하여 작동합니다. 이 스킬은 Node.js(Next.js 포함)를 위해 `lan-bind.mjs` 프리로드(preload)를 제공하며, 다른 스택(Python, Go, .NET, PHP 등)은 `HTTP_PROXY` 환경 변수를 통해 프록시를 사용합니다. 또한 Playwright 기반 브라우저 MCP(`lan-browser.mjs`)를 제공하여 브라우저가 ISP를 통해 특정 호스트에 접속할 수 있도록 합니다. 모든 설정은 프로세스 범위 내에서 이루어지며, 작업 종료 후 프록시를 중지하고 환경 변수를 제거하면 원래 상태로 복원됩니다.

지원 대상✓Claude Code✓Codex CLI~Cursor
npx skills add farshadmomo/lan-direct

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

lan-direct은(는) 무엇을 하나요?

앱의 트래픽을 ISP를 통해 라우팅하고, **당신(에이전트)**은 VPN에 연결된 상태로 유지합니다.

Claude Code와 Codex 모두에서 작동합니다. 메커니즘은 소켓별 바인딩(per-socket binding)이며, 이는 실행 환경(harnass)과 무관합니다.

불변 규칙

라우팅 테이블, WinINET/시스템 프록시 설정, VPN 클라이언트 구성을 절대 수정하지 마세요. 다음만 허용합니다:

  • 자식 프로세스에 범위가 지정된 환경 변수 설정, 또는
  • 도구별 소스 바인딩 플래그 전달.

둘 다 소켓별로 적용되며 옵트인(opt-in) 방식이므로 자신의 연결에는 영향을 미치지 않습니다. 이것이 이 스킬의 핵심입니다. 만약 route add 명령어를 사용한다면, 이 원칙을 위반한 것입니다 (문서 끝의 비상 탈출구 섹션 참조).

VPN 연결을 끊으라고 제안하지 마십시오. 이 스킬이 해결하려는 문제가 바로 그 상황을 피하는 것입니다.

워크플로우

1. 감지 및 확인

powershell -NoProfile -ExecutionPolicy Bypass -File scripts\lan-detect.ps1

Linux나 macOS에서는 대신 다음을 사용하세요. 동일한 JSON, 동일한 종료 코드 계약입니다:

bash scripts/lan-detect.sh

다음 JSON을 출력합니다: lanIp, gateway, lanDns, subnet, tunnelActive, vpnAdapter, directEgress, lanEgress, fakeIpDns, bypassWorks. bypassWorks가 true일 때만 종료 코드 0을 반환합니다. 이는 추론이 아닌 두 개의 라이브 프로브로 바이패스를 증명합니다.

  • tunnelActive: false -> VPN이 켜져 있지 않습니다. 알리고 중단하세요. 바이패스할 것이 없습니다.
  • 터널이 활성화된 상태에서 bypassWorks: false -> 아래 문제 해결로 이동하세요. 진행하지 마십시오. 트래픽이 조용히 VPN에 남아 거짓 통과를 보고하게 됩니다.

2. 메커니즘 선택

Node 백엔드(Next.js 포함)? 프록시를 건너뛰십시오 - 스택별 레시피 아래의 Node 레시피로 바로 이동하세요. 환경 변수 하나만 있으면 되고, 프록시 프로세스가 필요 없으며, 네이티브 fetch에 도달하는 유일한 방법입니다.

그 외의 모든 경우: LAN 바인딩 프록시를 시작하세요. 서버 프로세스에는 --interface 플래그가 없으므로, 이것이 ISP 이그레스를 얻는 방법입니다.

node scripts/lan-proxy.mjs --bind <lanIp> --port 8899          # 백그라운드 실행
node scripts/lan-proxy.mjs --bind <lanIp> --port 8899 --dns <lanDns>   # fakeIpDns: true인 경우

127.0.0.1만 수신 대기하며, 터널링하는 모든 호스트를 기록하므로 앱이 실제로 호출한 내용을 볼 수 있습니다.

3. 백엔드를 프록시를 가리키도록 재시작

HTTP_PROXY=http://127.0.0.1:8899
HTTPS_PROXY=http://127.0.0.1:8899
NO_PROXY=localhost,127.0.0.1,<subnet>

NO_PROXY는 중요합니다. 이것이 없으면 백엔드의 자체 로컬 서비스 호출이 불필요하게 프록시를 통해 전송됩니다. (TCP의 로컬 DB(MySQL, Postgres, Mongo)는 HTTP가 아니며 어느 쪽이든 영향을 받지 않습니다.)

이 환경 변수들은 백엔드 실행 명령에만 설정하고, 전역으로 설정하지 마십시오. 아래 스택별 레시피를 참조하세요 - 여러 스택에서는 HTTP_PROXY만으로는 충분하지 않습니다.

4. 실행 및 확인

아웃바운드 호출이 실패했던 엔드포인트를 호출하세요. 둘 다 확인하십시오:

  • 호출이 이제 성공합니다.
  • 프록시 로그에 다이얼한 호스트가 표시됩니다.

일치하는 로그 줄 없이 성공하면 요청이 프록시를 거치지 않은 것입니다.

5. 정리

프록시를 중지하고 환경 변수 없이 백엔드를 재시작하세요. 실행 취소할 다른 작업은 없습니다. 전역 상태가 변경되지 않았습니다.

Git Bash의 pkill -f lan-proxy는 Windows node.exe를 종료하지 않습니다. 다음을 사용하세요:

Get-CimInstance Win32_Process -Filter "Name='node.exe'" |
  Where-Object { $_.CommandLine -like '*lan-proxy*' } |
  ForEach-Object { Stop-Process -Id $_.ProcessId -Force }

남아 있는 인스턴스는 해롭지 않지만, 다음 실행 시 포트 충돌로 실패하게 만듭니다.

스택별 레시피

스택프록시 제공 방법
Python requests / httpx환경 변수, 그대로 사용
Go, .NET HttpClient환경 변수, 그대로 사용
PHP CLIlibcurl이 프로세스 환경에서 http_proxy / https_proxy 읽음
Node (Next.js 포함)프록시가 아님 - 아래의 lan-bind.mjs 프리로드 사용
Apache/XAMPP 아래 PHP아래 참조 - 서비스가 셸을 상속하지 않음
curl, wget, npm, ssh, git프록시 건너뛰고 직접 바인딩 (아래 직접 바인딩 참조)

Node - 프리로드 사용, 프록시 사용 안 함

네이티브 fetch는 Node에 내장된 undici에서 실행되며, Node <= 23에서는 HTTP_PROXY를 무시하고 https.Agent도 허용하지 않습니다. Node 22.20에서 확인: 일반 환경 변수, NODE_USE_ENV_PROXY=1, --use-env-proxy 모두 아무 효과 없음 - 프록시 로그는 비어 있고 트래픽은 조용히 VPN을 통해 나갑니다. 이는 Next.js, openai SDK 및 네이티브 fetch를 사용하는 모든 것에 영향을 미칩니다.

프록시를 완전히 건너뛰고 프리로드를 사용하세요. 프리로드는 프로세스의 모든 아웃바운드 소켓을 바인딩합니다:

LAN_BIND=<lanIp> node --import "file:///<skill-dir>/scripts/lan-bind.mjs" script.js
LAN_BIND=<lanIp> NODE_OPTIONS="--import file:///<skill-dir>/scripts/lan-bind.mjs" npm run dev

<skill-dir>은 이 스킬의 자체 기본 디렉토리이며 슬래시를 사용합니다. 일반적으로 Claude Code에서는 C:/Users/<you>/.claude/skills/lan-direct, Codex에서는 C:/Users/<you>/.agents/skills/lan-direct입니다. --import는 Windows에서 절대 file:// URL이 필요합니다. 백슬래시가 포함된 원시 Windows 경로는 실패합니다.

NODE_OPTIONS는 포크된 워커로 전파되므로 Next.js 및 Vite 개발 서버를 포함합니다.

이것은 네이티브 fetch, openai SDK, axios, node-fetch, got 및 일반 http/https에 한 번에 도달합니다. 의존성, 프록시 프로세스, HTTP_PROXY가 필요 없습니다. 루프백 대상을 건너뛰므로 로컬 DB나 개발 서버는 계속 작동합니다.

프리로드를 사용할 수 없는 경우에만: Node >= 24는 환경 변수와 함께 NODE_USE_ENV_PROXY=1을 인식하고, axios는 모든 버전에서 이를 인식합니다.

Apache / XAMPP 아래 PHP

httpd.conf의 SetEnv/PassEnv는 $_SERVER만 채웁니다. libcurl은 프로세스 환경을 읽으므로 이를 가져오지 않습니다. 다음 중 하나를 사용하세요:

  • 이미 http_proxy/https_proxy가 내보내진 셸에서 Apache 시작 (예: xampp\apache_start.bat), 또는
  • curl_init() 전에 putenv('https_proxy=http://127.0.0.1:8899');, 또는
  • curl_setopt($ch, CURLOPT_PROXY, '127.0.0.1:8899'); 직접.

file_get_contents()는 이들 중 어느 것도 인식하지 않습니다. 'proxy' => 'tcp://127.0.0.1:8899'가 포함된 스트림 컨텍스트가 필요합니다.

직접 바인딩

도구가 자체 소켓을 바인딩할 수 있는 경우 프록시가 필요하지 않습니다:

도구플래그
curl--interface <lanIp>
wget--bind-address=<lanIp>
npm--local-address=<lanIp>
ssh-b <lanIp>
Chromium / Playwright바인드 플래그 없음 - 아래 포트 8900의 브라우저 MCP 사용

브라우저 / Playwright MCP

Chromium에는 소스 바인딩 플래그가 없으므로 브라우저는 프록시를 통해서만 ISP에 도달할 수 있습니다. lan-browser.mjs를 사용하세요. 이것은 자체 프록시를 시작하고 소유하는 Playwright MCP 서버이므로 브라우저가 프록시 없이 시작될 수 없습니다.

이 스킬과 함께 제공됩니다. scripts/install.ps1이 등록합니다:

powershell -NoProfile -ExecutionPolicy Bypass -File <skill-dir>\scripts\install.ps1

ISP를 사용하는 호스트는 세 가지 병합된 소스에서 가져오므로 재설치가 거의 필요하지 않습니다: 스킬 디렉토리의 lan-hosts.txt (제공된 기본값, git pull 시 교체), ~/.lan-direct-hosts (사용자 소유, 절대 덮어쓰지 않음), 그리고 --lan-hosts a,b (실행 시). 한 줄에 하나의 규칙, # 주석 허용, 접미사 일치.

사용자가 다른 호스트를 ISP를 통해 라우팅하기를 원하면, ~/.lan-direct-hosts에 줄을 추가하고 브라우저를 다시 시작하라고 알리십시오. 다음 업데이트에서 덮어쓸 lan-hosts.txt를 편집하지 마십시오.

사용자가 수동으로 등록하려는 경우, --가 PowerShell의 매개변수 바인더를 통과해야 합니다. 인수를 배열로 전달하거나 bash에서 실행하세요:

# 클론에서
claude mcp add playwright-split --scope user -- \
  node <skill-dir>/scripts/lan-browser.mjs --lan-hosts ".ir"

# npm에서 - 체크아웃이 이동해도 유지되므로 사용자가 설치한 경우 선호
claude mcp add playwright-split --scope user -- npx -y lan-direct browser

패키지가 설치된 경우 다른 CLI 하위 명령: lan-direct detect는 플랫폼에 맞는 감지 스크립트를 선택하고, lan-direct route <host>는 브라우저를 시작하지 않고 "이 호스트가 ISP를 통해 갈까요, 아니면 터널을 통해 갈까요?"에 답합니다. 사용자가 사이트가 로드되지 않는다고 보고할 때 사용하십시오. 잘못된 규칙과 다운된 사이트는 외부에서 동일하게 보입니다.

서버는 LAN IP를 감지하고, lan-detect에서 바이패스가 깨졌다고 말하면 시작을 거부하며, 이미 수신 중인 프록시가 있으면 재사용하고 MCP가 종료될 때 시작한 것을 종료합니다. 자체 로그는 stderr로 이동합니다. stdout은 JSON-RPC를 위해 깨끗하게 유지됩니다.

위의 백엔드 테스트에 사용된 8899가 아닌 포트 8900을 사용합니다. 재사용은 포트만으로 결정되므로, 번호를 공유하면 모든 것을 바인딩하는 백엔드 프록시가 브라우저를 조용히 캡처하고 모든 트래픽을 ISP를 통해 라우팅할 수 있습니다.

하나의 브라우저에서 혼합 탐색이 가능합니다. --lan-hosts는 프록시가 호스트별로 결정하도록 만듭니다. 일치하는 호스트는 ISP를 통해 이그레스하고, 다른 모든 것은 바인딩되지 않은 채로 터널을 통해 이동합니다. 따라서 "hoosha와 youtube 확인"은 도구 전환이 필요 없습니다. hoosha는 ISP를 통해, youtube는 VPN을 통해, 동일한 세션에서 이동합니다. 프록시 로그는 모든 줄에 결정을 표시합니다:

CONNECT  lan  hoosha.com:443
CONNECT  vpn  youtube.com:443

목록을 정확하게 유지하십시오. 목록에서 누락된 국내 호스트는 터널로 가서 빈 응답과 함께 실패하고, 잘못 추가된 외국 호스트는 ISP로 가서 필터링될 수 있습니다. 사이트가 연결 끊김처럼 보이는 방식으로 실패하면, 사이트가 다운되었다고 가정하기 전에 로그가 어떤 경로를 선택했는지 확인하십시오.

자동 감지 대신 명시적 목록을 사용하는 이유: 차단된 호스트에 터널을 통해 연결하면 TCP 수준에서 성공하고(측정: connect@14ms) 프록시가 이미 200 Connection Established에 응답한 후 약 860ms 후에야 실패합니다. 폴백은 TLS 핸드셰이크를 버퍼링하고 재생하며 모든 국내 요청에 대해 지연을 지불해야 함을 의미합니다.

Playwright는 루프백을 프록시를 통해 라우팅합니다. Chrome은 일반적으로 localhost를 우회하지만, Playwright는 프록시가 설정되면 이를 재정의하므로 http://127.0.0.1:3000은 직접 가지 않고 lan-proxy.mjs에 도착합니다. 프록시 로그에서 확인되었습니다. 프록시의 루프백 가드는 로컬 개발 서버에 계속 연결할 수 있도록 유지합니다. 이것이 없으면 모든 요청이 EADDRNOTAVAIL과 함께 실패합니다. --proxy-bypass=<-loopback>를 추가하지 마십시오. 여기서는 아무것도 변경하지 않고 그림만 혼란스럽게 만듭니다.

문제 해결

터널이 켜져 있는데 directEgress가 lanEgress와 같습니다. VPN이 킬 스위치(kill-switch) 또는 엄격한 경로(strict-route)(바운드 소켓을 차단하는 WFP 필터)를 시행하고 있습니다. VPN 클라이언트에서 해결하고, 우회하지 마십시오. 엄격한 경로를 비활성화하거나 필요한 호스트에 대한 직접/바이패스 라우팅 규칙을 추가하십시오. nekoray/sing-box: strict_route를 끄거나 direct 아웃바운드 규칙을 추가하십시오.

Linux에서는 킬 스위치 없이도 이런 일이 발생할 수 있습니다. 소스 주소만으로는 Windows처럼 라우팅 테이블을 재정의하지 않으므로 정책 라우팅이 트래픽을 터널에 유지합니다. 해결책은 LAN IP에서 물리적 게이트웨이로 트래픽을 보내는 규칙(ip rule add from <lanIp> table <n> 및 해당 테이블의 기본 경로)입니다. 이것은 루트 권한이 필요하며 시스템 전체 변경이므로 설명하고 사용자에게 결정을 맡기십시오. 이 스킬의 다른 모든 것은 권한이 없고 프로세스 범위 내에 있습니다.

fakeIpDns: true. OS 확인자가 터널 내에서만 의미가 있는 198.18.x.x 플레이스홀더를 반환하고 있습니다. 프록시에 --dns <lanDns>를 전달하여 LAN 자체 DNS를 통해 확인하십시오.

이그레스 프로브가 아무것도 반환하지 않습니다. 프로브 호스트에 연결할 수 없습니다. 링크를 확인하고 다시 실행하십시오.

비상 탈출구

백엔드를 진정으로 프록시할 수 없는 경우(프록시 지원이 없는 컴파일된 바이너리), 남은 유일한 옵션은 호스트별 정적 경로입니다:

route add <targetIp> mask 255.255.255.255 <gateway> metric 1   # 관리자 권한 필요
route delete <targetIp>                                        # 실행 취소, 항상

이것은 전역 설정입니다. 해당 대상에 대해 모든 프로세스를 터널에서 끌어냅니다. 사용자 자신도 동일한 호스트에 접촉하는 경우 포함됩니다. 상승된 권한이 필요하며, 삭제될 때까지 유지되며 잊어버리기 쉽습니다. 사용하기 전에 물어보고, 같은 세션에서 삭제하십시오.

관련 스킬