Bỏ qua để đến nội dung

Kiến trúc

AI CLI (claude-code, claude-cli, ...)
│ HTTP to 127.0.0.1:3817
┌──────────────────────────────────────────────┐
│ sotatek-proxy (Gin engine, loopback bind) │
│ │
│ recoveryLogger panic recovery │
│ requestLogger scrub Authorization, │
│ x-api-key, drop query │
│ MiddlewareGitRemote │
│ │ │
│ ├─ extract peer port from RemoteAddr │
│ ├─ inspect.GetPidByPeerPort → PID │
│ ├─ inspect.GetCwdByPid → CWD │
│ ├─ walk parents (≤3) for .git │
│ ├─ cache.Get(pid) [verify CWD] │
│ ├─ git.GetRemoteRaw(cwd) (2s timeout) │
│ └─ inject X-Git-Remote (base64) │
│ │
│ ReverseProxy (NoRoute) │
│ ├─ stamp User-Agent: sotatek-proxy/X │
│ ├─ preserve original in │
│ │ X-Forwarded-User-Agent │
│ └─ FlushInterval=-1 (SSE-safe) │
└────────────────────┬─────────────────────────┘
│ HTTP/2 (ForceAttemptHTTP2)
https://bifrost.sotatek.works/anthropic/v1/*

Thứ tự rất quan trọng. Mỗi lớp chạy theo thứ tự này khi đi vào, đảo ngược khi đi ra.

#MiddlewareMục đích
1recoveryLoggerPhục hồi sau panic; không bao giờ hiển thị request header.
2requestLoggerLog cấu trúc gọn gàng mỗi request: method, path, status, latency, client_ip, boolean has_auth/has_api_key, cờ has_query. Giá trị Authorization không bao giờ được ghi log.
3MiddlewareGitRemotePhân giải PID và CWD của client đang gọi, chạy git remote -v, mã hóa base64 kết quả, đặt X-Git-Remote. Xóa bất kỳ X-Git-Remote nào do client cung cấp trước.
4ReverseProxy (NoRoute)Chuyển tiếp đến upstream 1:1, đóng dấu User-Agent, stream SSE không có buffering.

Proxy phân giải TCP peer port của client (cổng CLI của bạn dùng để kết nối) thành PID, rồi thành CWD, sau đó đi lên tối đa 3 thư mục cha để tìm .git.

OSCài đặt
macOSlsof -iTCP:<port> -sTCP:ESTABLISHED -F pn (PID), lsof -p <pid> -d cwd -F n (CWD). Timeout thực thi 2 giây.
LinuxĐọc /proc/net/tcp[6] để map port → inode, quét /proc/*/fd/ tìm socket khớp, rồi đọc /proc/<pid>/cwd. Chỉ cùng người dùng.
WindowsGetExtendedTcpTable cho bảng kết nối IPv4/IPv6; PEB walk qua NtQueryInformationProcess cho CWD. Chỉ 64-bit.

Việc đi lên thư mục cha dừng lại tại các shell tương tác phổ biến (bash, zsh, fish, sh, dash, ksh, tcsh, csh) để proxy không bao giờ chiếm CWD của shell.

Cache PID → {cwd, git-remote} dùng sync.Map với TTL (mặc định 60 giây). Mỗi lần Get, caller đọc lại CWD hiện tại và mục cache bị xóa khi không khớp — điều này bảo vệ chống tái sử dụng PID.

Một sweep nền mỗi 5 phút sẽ xóa các mục đã hết hạn và các mục có PID không còn tồn tại (kill -0).

Shell-out git remote -v có timeout cứng 2 giây. Khi timeout hoặc không có remote, request được chuyển tiếp không có header X-Git-Remote thay vì bị chặn.

Proxy dùng httputil.ReverseProxy với FlushInterval=-1, buộc flush mỗi lần ghi. Các phản hồi SSE / chunked đến client khi upstream phát ra — không có buffering body. Khi client ngắt kết nối, upstream context bị hủy.

GET /_proxy/healthz

Trả về version, commit, uptime, và (khi được kết nối) thống kê cache.

{
"status": "ok",
"version": "v0.1.0",
"commit": "abc1234",
"uptime": "1h23m",
"cache": { "entries": 12, "hits": 480, "misses": 64, "hit_rate": 0.882 }
}