Go에서 Yggdrasil 네트워킹
개요
이 스킬을 사용하여 Yggdrasil 종단간 암호화 IPv6 메시에 연결하고 서비스하는 Go 프로그램을 구축하십시오: 클라이언트, HTTP/TCP/UDP 서버, SOCKS5 프록시, 포트 포워더, 그리고 검색 메타데이터를 게시하는 노드. 노드는 사용자 공간의 gVisor netstack에서 실행됩니다 — TUN 장치, 루트, 외부 yggdrasil 데몬, yggstack 바이너리가 필요 없습니다. 표준 Go 인터페이스(net.Conn, net.Listener, net.PacketConn, http.Transport)로 Yggdrasil을 통해 다이얼하고 리슨합니다.
임베딩 라이브러리는 github.com/voluminor/ratatoskr입니다: ratatoskr.New로 노드를 생성하고 Go API를 통해 모든 것을 제어하십시오. 패키지의 범위와 안정성은 아래에 요약되어 있습니다.
제한된 메시를 위한 설계
Yggdrasil은 데이터센터 패브릭이 아닌 종단간 암호화 오버레이 메시입니다. 링크는 고지연, 저대역폭, 손실성, 간헐적입니다; 지금 연결 가능한 피어가 1초 후에는 사라질 수 있으며, 트래픽은 여러 홉을 거칠 수 있습니다. 모든 Yggdrasil 솔루션을 약하고 불안정한 연결을 위한 것으로 구축하십시오. 이것은 선택적 다듬기가 아니라 올바른 코드의 기본 형태입니다.
- 무제한 금지. 모든 다이얼과 요청에 타임아웃(
context.WithTimeout); 제한된 재시도; 제한된 큐. 무한 대기 없음. - 동시성 제한. 진행 중인 작업 제한: SOCKS
MaxConnections, 포워드SetMaxUDPSessions,http.Transport.MaxConnsPerHost/MaxIdleConnsPerHost, 자체 워커 풀. 기본값은 모두 무제한입니다 — 실제 제한을 설정하십시오. - 큰 전송은 청크, 검증, 재개. 큰 파일을 한 번에 옮기지 마십시오. 작은 청크로 나누고(예: HTTP
Range), 각각 검증(크기/체크섬) 후 재개 가능하게 만들어 링크 끊김이 전체 전송이 아닌 하나의 청크 비용만 발생하도록 하십시오. - 백오프 + 지터, 멱등적으로 재시도. 작업 중간에 연결 끊김을 예상하십시오. 작업을 재개 가능/멱등적으로 만들고; 지터가 있는 지수 백오프; 시도 횟수 제한.
- 페이로드 작게 유지. 잡담 많은 프로토콜과 폴링을 피하고; 압축하고; 간결한 인코딩을 사용하십시오. NodeInfo/시길을 16KB 제한보다 훨씬 작게 유지하십시오.
- 링크에 맞게 버퍼 크기 조정. 패킷 버퍼에
node.MTU()사용(사용자 공간 MTU는 동적이며, 최소 1280). - 강타하지 말고 저하시키기. 실패 시 백오프하고 부분 결과를 반환하십시오; 피어를 공격적으로 재시도하거나 메시를 플러딩하지 마십시오.
순진한 "net/http로 큰 파일 제공" 또는 "하나의 거대한 요청/응답"은 잘못된 형태입니다. 올바른 형태는 제한되고, 청크화되고, 재개 가능하며, 기본적으로 내결함성이 있는 것입니다. 제한된 메시 전송 패턴은 references/integrations.md를 참조하십시오.
진실의 원천
활성 모듈이 권위를 가집니다 — 메모리, 오래된 README 텍스트, 이 스킬이 아닙니다. 버전별 지침을 작성하기 전에 프로젝트가 실제로 의존하는 것을 검사하십시오:
go list -m github.com/voluminor/ratatoskr # 선택된 버전
go doc github.com/voluminor/ratatoskr # 컴파일된 루트 API
go doc github.com/voluminor/ratatoskr/mod/peermgr ConfigObj
선택된 버전의 실제 소스를 읽으려면 경로를 추측하는 대신 모듈 캐시에서 해결하십시오:
go list -m -f '{{.Dir}}' github.com/voluminor/ratatoskr
README 텍스트의 최소 Go 버전을 고정하지 마십시오 — go.mod 지시어보다 뒤쳐질 수 있습니다. 선택된 Ratatoskr 버전의 활성 go.mod를 읽으십시오.
기본 워크플로우
- 애플리케이션 형태를 식별하십시오: 임베디드 노드, HTTP 클라이언트/서버, 원시 TCP/UDP, SOCKS5, 포트 포워딩, 피어 관리, 또는 NodeInfo/시길.
- 애플리케이션에는 루트 패키지
github.com/voluminor/ratatoskr를 사용하십시오.mod/core는core.Interface계약을 통해서만 접근하십시오; 저수준 패키지가 진정으로 요구하지 않는 한*core.Obj/UnsafeCore()를 피하십시오. context.Context를 소유하고,ratatoskr.ConfigObj.Ctx로 전달한 후 성공적인 시작 후에도defer node.Close()를 하십시오. 둘 다 필요합니다.- 피어를 정확히 한 곳에서 구성하십시오:
- 정적 Yggdrasil 피어:
cfg.Config.Peers - Ratatoskr 피어 관리자:
ratatoskr.ConfigObj.Peers(cfg.Config.Peers는 비워 두십시오, 그렇지 않으면New가ErrPeersConflict를 반환).
- 정적 Yggdrasil 피어:
- 아웃바운드 TCP/UDP에는
node.DialContext를, Yggdrasil 내부의 서비스에는node.Listen/node.ListenPacket을 사용하십시오. - URL과 호스트-포트 문자열에서 IPv6 주소를 대괄호로 감싸십시오:
http://[200:abcd::1]:8080, 절대http://200:abcd::1:8080이 아닙니다. - 링크를 약한 것으로 취급하십시오: 모든 호출에 타임아웃을 설정하고, 동시성을 제한하며, 큰 전송은 청크/검증/재개하십시오(제한된 메시를 위한 설계 참조).
- 실제 프로젝트로 코드를 생성할 때는 빌드된다고 가정하지 말고 컴파일 검사 또는 집중 테스트를 추가하십시오.
모듈 범위 및 안정성
Yggdrasil 주소는 노드 키에서 파생되므로 네트워크 표면은 작고 안정적입니다. 라이브러리가 완성된 것으로 취급하는 부분을 선호하십시오; 나머지는 명시적으로 표시하고 그 위에 구축하지 마십시오.
| 패키지 | 상태 | 사용 방법 |
|---|---|---|
루트 ratatoskr | 안정, 기본 API | New, EnableSOCKS, Ask/AskAddr, Snapshot, Close |
mod/core | 안정적인 계약 | 노드에 임베드된 core.Interface 메서드 |
mod/peermgr | 안정 | ratatoskr.ConfigObj.Peers |
mod/socks | 안정 | node.EnableSOCKS (직접 인스턴스화하지 마십시오) |
mod/resolver | 안정 | EnableSOCKS에 의해 자동 생성됨; 직접 사용은 고급 |
mod/forward | 안정 | forward.New → Add* → Start |
mod/sigils (+ info, services, public, inet) | 안정 | ratatoskr.ConfigObj.Sigils |
mod/ninfo | 안정 | node.Ask / node.AskAddr |
mod/probe | 고급 / 불안정 — UnsafeCore() 필요, API 변경 가능 | 토폴로지/경로 추적이 명시적으로 필요한 경우에만 |
mod/settings | 작업 중 — 스키마 미확정 | 피함; 대신 yggdrasil-go/src/config 직접 사용 |
cmd/* | 예제 앱, 가져올 수 있는 라이브러리 API 아님 | 패턴으로 읽고, 절대 가져오지 마십시오 |
루트 vs 서브모듈: 기본적으로 ratatoskr.New 사용(코어 + SOCKS + 피어 관리자 + ninfo + 시길을 하나의 Close로). 가장 작은 표면(원시 다이얼/리슨)만 필요할 경우 core.New를 통해 mod/core로 내려가거나, 사용자 정의 전송 및 테스트를 위해 core.Interface 주변에 서브모듈을 구성하십시오. 모든 서브모듈(peermgr/socks/forward/resolver/ninfo)은 core.Interface를 받습니다. references/overview.md 참조.
시스템 시길 — 여기에 먼저 게시
NodeInfo는 노드가 네트워크에 자신을 설명하는 방법입니다. 네 가지 내장("시스템") 시길은 모든 노드에 의해 자동으로 파싱되므로, 여기에 넣은 데이터는 다른 참가자가 인식합니다. 사용자 정의 시길을 만들기 전에 이것들을 먼저 사용하십시오; ratatoskr.ConfigObj.Sigils를 통해 전달하십시오. 각각을 아주 작게 유지하십시오 — 하나의 16KB NodeInfo 예산을 공유합니다.
| 시길 | 여기에 속하는 내용 | 사용 이유 |
|---|---|---|
info | 노드 ID: 이름, 유형, 위치, 연락처, 설명 | 다른 노드가 당신의 노드가 누구/무엇인지 알게 함. |
services | Yggdrasil 내에서 노출하는 포트, name → port | 포트 스캔 없이 서비스 검색 가능. |
public | 네트워크별 그룹화된 공개 피어링 URI | 다른 노드가 직접 피어링할 수 있게 함. |
inet | 실제 주소(도메인/IP) | Yggdrasil 노드를 클리어넷 ID에 매핑. |
검색 가능하고 표준적인 데이터를 여기에 넣어 전체 네트워크가 읽을 수 있게 하십시오. 네 가지 중 어떤 것도 포함하지 않는 데이터에 대해서만 사용자 정의 시길을 정의하십시오 — 그리고 당신의 코드를 실행하는 피어만이 그것을 파싱할 것임을 기억하십시오. 생성자는 검증하고 오류를 반환합니다; ratatoskr.New 전에 처리하십시오. 세부 사항, 정확한 제한, 타입화된 접근, 사용자 정의 시길 작성: references/nodeinfo-sigils.md 및 references/custom-sigils.md.
참조 맵
현재 작업에 필요한 파일만 읽으십시오:
- 임베디드 노드 기본, Ratatoskr vs yggstack, 아키텍처, 범위: references/overview.md
ratatoskr.New,ConfigObj, 생명주기, 스레드 안전성, 루트 메서드, 오류, 스냅샷: references/root-api.md- TCP, UDP,
DialContext,Listen,ListenPacket, HTTP 클라이언트/서버, 멀티캐스트/관리, 테스팅: references/networking.md - 정적 피어, 피어 관리자, SOCKS5, 리졸버, 포트 포워딩: references/peers-socks-forwarding.md
- NodeInfo,
Ask/AskAddr, 정확한 제한이 있는 내장 시길: references/nodeinfo-sigils.md - 타입화된 접근 및 자체 시길 작성/연결 (원시
map[string]any없음): references/custom-sigils.md - 클라이언트/서버 템플릿 및 프레임워크 통합 (net/http, fasthttp, gRPC, gnet 브릿지): references/integrations.md
- 고급/WIP 영역 (probe, settings) 및 CLI/예제 앱 패턴: references/advanced-and-examples.md
- 즉시 적용 가능한 애플리케이션 레시피: references/recipes.md
흔한 실수
| 실수 | 수정 |
|---|---|
| 임베디드 앱에 외부 Yggdrasil 데몬을 시작함 | ratatoskr.New를 사용하십시오; 사용자 공간 gVisor netstack을 실행합니다. |
라이브러리 통합에 yggstack을 권장함 | yggstack은 최종 사용자 바이너리입니다. Go API 제어에는 Ratatoskr를 사용하십시오. |
cfg.Config.Peers와 ConfigObj.Peers를 모두 설정함 | 정적 피어 또는 피어 관리자 중 하나를 선택하십시오; 둘 다 사용하면 → ErrPeersConflict. |
Ctx를 전달하면서 Close를 건너뜀 (또는 반대) | 둘 다 하십시오: 취소는 종료를 트리거하고, Close는 리소스를 해제합니다. |
| IPv6 대괄호 없이 URL을 구축함 | fmt.Sprintf("http://[%s]:%d", ip, port)를 사용하십시오. |
기본 쿼리에 ninfo.New를 생성함 | node.Ask / node.AskAddr를 사용하십시오; 루트 객체가 이미 ninfo를 구축합니다. |
| 런타임에 동적으로 시길을 조립하거나 등록함 | 시길은 코드에서만 조립됩니다; 중복되거나 키를 덮어쓰는 것은 설계상 조용한 병합이 아닌 하드 오류입니다. ConfigObj.Sigils를 통해 게시하십시오; res.Node.Extra에서 자체 키를 읽으십시오. |
기본적으로 UnsafeCore() / mod/probe / mod/settings를 사용함 | 루트 API에 머무르십시오; 이것들은 고급 또는 작업 중입니다. |
| README에서 Go 버전을 하드코딩함 | 선택된 모듈 버전의 활성 go.mod를 읽으십시오. |
검증
SKILL.md를 탐색 계층으로 유지하고 세부 API 내용은 references/에 넣으십시오. 게시 전에 evals/evals.json의 시나리오에 대해 변경 사항을 전향 테스트하십시오.