- Added `StreamAck`-based selective retransmission logic for reliable stream delivery. - Introduced per-stream ARQ states (`expectedSeq`, `lost`, `received`) for out-of-order handling and lost frame tracking. - Implemented mechanisms to send `StreamAck` with `AckSeq` and `LostSeqs` attributes in response to `StreamData`. - Enhanced retransmission logic for unacknowledged frames in `streamSender`, ensuring robust recovery for lost data. - Updated progress notes in `progress.md` to reflect ARQ implementation.
28 KiB
HopGate Progress / 진행 현황
이 문서는 HopGate 아키텍처 대비 현재 구현 상태와 이후 추가해야 할 작업을 정리한 Milestone 문서입니다. (ko)
This document tracks implementation progress against the HopGate architecture and lists remaining milestones. (en)
1. High-level Status / 상위 수준 상태
- 아키텍처 문서 및 README 정리 완료 (ko/en 병기). Architecture and README are documented in both Korean and English.
- 서버/클라이언트 엔트리 포인트, DTLS 핸드셰이크, 기본 PostgreSQL/ent 스키마까지 1차 뼈대 구현 완료. First skeleton implementation is done for server/client entrypoints, DTLS handshake, and basic PostgreSQL/ent schema.
- 기본 Proxy 동작(HTTP ↔ DTLS 터널링), Admin API 비즈니스 로직, ACME 기반 인증서 관리는 구현 완료된 상태. Core proxying (HTTP ↔ DTLS tunneling), admin API business logic, and ACME-based certificate management are implemented.
- 스트림 ARQ, Observability, Hardening, ACME 고급 전략 등은 아직 남아 있는 다음 단계 작업이다. Stream-level ARQ, observability, hardening, and advanced ACME operational strategies remain as next-step work items.
2. Completed Work / 완료된 작업
2.1 Documentation / 문서
-
아키텍처 개요:
ARCHITECTURE.md- ko/en 병기, 전체 구조/디렉터리/흐름/다음 단계 정리. (ko)
- Bilingual, documents overall structure, directories, flows, and next steps. (en)
-
프로젝트 개요:
README.md- 사용법, DTLS 핸드셰이크 테스트 방법, Admin Plane 요약, 주의사항. (ko/en)
- Usage, DTLS handshake test guide, admin plane summary, caveats. (en)
-
커밋 규칙:
COMMIT_MESSAGE.md[type] short description [BREAK]형식, 타입 우선순위 정의, BREAK 규칙. (ko/en)- Defines commit message format, type priorities, and
[BREAK]convention. (en)
-
아키텍처 그림용 프롬프트:
architecture.prompt- 외부 도구(예: 나노바나나 Pro)가 참조할 상세 다이어그램 지침. (en 설명 위주)
2.2 Server / Client Entrypoints
-
서버 메인:
cmd/server/main.go- 서버 설정 로드 (
LoadServerConfigFromEnv). - PostgreSQL 연결 및 ent 스키마 init (
store.OpenPostgresFromEnv). - Debug 모드 시 self-signed localhost cert 생성 (
dtls.NewSelfSignedLocalhostConfig). - DTLS 서버 생성 (
dtls.NewPionServer) 및 Accept + Handshake 루프 (PerformServerHandshake). - ent 기반
DomainValidator+domainGateValidator를 사용해(domain, client_api_key)조합과 DNS/IP(옵션) 검증을 수행.
- 서버 설정 로드 (
-
클라이언트 메인:
cmd/client/main.go- CLI + env 병합 설정 (우선순위: CLI > env).
server_addr,domain,api_key,local_target,debug.
- DTLS 클라이언트 생성 (
dtls.NewPionClient)Debug=true시InsecureSkipVerify=trueTLS 설정 사용.
- DTLS 핸드셰이크 수행 (
dtls.PerformClientHandshake)- 성공 시 도메인/로컬 타깃 로그 출력.
- CLI + env 병합 설정 (우선순위: CLI > env).
2.3 Config / Env Handling
-
공통 설정:
internal/config/config.goServerConfigHTTPListen,HTTPSListen,DTLSListen,Domain,ProxyDomains,Debug,Logging.- env:
HOP_SERVER_HTTP_LISTEN,HOP_SERVER_HTTPS_LISTEN,HOP_SERVER_DTLS_LISTEN,HOP_SERVER_DOMAIN,HOP_SERVER_PROXY_DOMAINS,HOP_SERVER_DEBUG.
ClientConfigServerAddr,Domain,ClientAPIKey,LocalTarget,Debug,Logging.- env:
HOP_CLIENT_SERVER_ADDR,HOP_CLIENT_DOMAIN,HOP_CLIENT_API_KEY,HOP_CLIENT_LOCAL_TARGET,HOP_CLIENT_DEBUG.
.env로더 (loadDotEnvOnce) + 각종 helper (getEnvBool, CSV 파싱 등).
-
DB 설정:
internal/store/postgres.goConfigFromEnv()로 DB 설정 로딩:HOP_DB_DSN,HOP_DB_MAX_OPEN_CONNS,HOP_DB_MAX_IDLE_CONNS,HOP_DB_CONN_MAX_LIFETIME.
-
.env샘플:.env.example- Logging/Loki, 서버 포트, 클라이언트 설정, DB 설정 예시 포함.
2.4 DTLS Layer / Handshake
-
인터페이스:
internal/dtls/dtls.goSession,Server,Client.
-
pion/dtls 전송 구현:
internal/dtls/transport_pion.goNewPionServer(PionServerConfig)- UDP 리스너 + DTLS 서버 (
piondtls.Listen).
- UDP 리스너 + DTLS 서버 (
NewPionClient(PionClientConfig)- Timeout/TLSConfig 설정,
piondtls.Dial사용.
- Timeout/TLSConfig 설정,
-
핸드셰이크 로직:
internal/dtls/handshake.go- 메시지:
handshakeRequest{domain, client_api_key},handshakeResponse{ok, message, domain}. DomainValidator인터페이스.PerformServerHandshake/PerformClientHandshake구현 완료.
- 메시지:
-
self-signed TLS:
internal/dtls/selfsigned.go- localhost CN, SAN(DNS/IP) 포함 self-signed cert 생성.
-
Domain Validator:
- 인터페이스 정의:
internal/dtls/handshake.goValidateDomainAPIKey(ctx, domain, clientAPIKey string) error.
- 실제 구현:
internal/admin/domain_validator.go- ent.Client + PostgreSQL 기반으로
Domain테이블 조회. - 도메인 문자열은
"host"또는"host:port"모두 허용하되, DB 조회 시에는 host 부분만 사용. (domain, client_api_key)조합이 정확히 일치하는지 검증.
- ent.Client + PostgreSQL 기반으로
- DTLS 핸드셰이크 DNS/IP 게이트:
cmd/server/main.gocanonicalizeDomainForDNS+domainGateValidator를 사용해, 클라이언트가 제시한 도메인의 A/AAAA 레코드가HOP_ACME_EXPECT_IPS에 설정된 IPv4/IPv6 IP 중 하나 이상과 일치하는지 검사한 뒤 DB 기반DomainValidator에 위임.HOP_ACME_EXPECT_IPS가 비어 있는 경우에는 DNS/IP 검증을 생략하고 DB 검증만 수행.
- 기존 Dummy 구현:
internal/dtls/validator_dummy.go는 이제 개발/테스트용 참고 구현으로만 유지.
- 인터페이스 정의:
2.5 Admin Plane Skeleton / 관리 Plane 스켈레톤
-
DomainService 인터페이스:
internal/admin/service.goRegisterDomain(ctx, domain, memo) (clientAPIKey string, err error)UnregisterDomain(ctx, domain, clientAPIKey string) error
-
HTTP Handler:
internal/admin/http.goAuthorization: Bearer {ADMIN_API_KEY}검증.- 엔드포인트:
POST /api/v1/admin/domains/registerPOST /api/v1/admin/domains/unregister
- JSON request/response 구조 정의 및 기본 에러 처리.
- 실제 서비스(
DomainService) 및 라우터 wiring, ent 기반 구현이 완료되어 도메인 등록/해제가 동작.
2.6 DB / ent
-
ent 스키마:
ent/schema/domain.goDomainentity:id(UUID, PK)domain(unique)client_api_key(unique, max 64)memo,created_at,updated_at.
-
ent 코드 생성 완료:
tools/gen_ent.sh,ent/*- PostgreSQL dialect 사용.
client.Schema.Create(ctx)로 테이블 자동 생성(DB init).
-
PostgreSQL 연결 헬퍼:
internal/store/postgres.goOpenPostgres(ctx, logger, cfg)ent/dialect/sql.Open("postgres", DSN)- pool 설정, ping, ent.Driver wrapping,
Schema.Create.
OpenPostgresFromEnv(ctx, logger)- 서버에서 바로 호출 가능.
2.7 Logging / Build / Docker
-
구조적 로깅:
internal/logging/logging.go- JSON 단일라인 로그,
level,ts,msg,Fields. - Loki/Promtail + Grafana 스택에 최적화.
- JSON 단일라인 로그,
-
빌드/도커:
Makefile—make server,make client,make docker-server.server타겟은 Tailwind 기반 에러 페이지 CSS 빌드를 위한errors-css타겟을 선행 실행 (npm run build:errors-css).
Dockerfile.server— multi-stage build, Alpine runtime.- Build stage 에 Node.js + npm 을 설치하고,
npm install && npm run build:errors-css를 통해 에러 페이지용 CSS를 빌드한 뒤 Go 서버 바이너리를 생성.
- Build stage 에 Node.js + npm 을 설치하고,
.dockerignore—images/제외.
-
아키텍처 이미지:
images/architecture.jpeg
2.8 Error Pages / 에러 페이지
-
에러 페이지 템플릿:
internal/errorpages/templates/*.html- HTTP 상태 코드별 HTML:
400.html,404.html,500.html,525.html.
- TailwindCSS 기반 레이아웃 및 스타일 적용 (영문/한글 메시지 병기).
go:embed로 서버 바이너리에 포함되어 기본값으로 사용.
- HTTP 상태 코드별 HTML:
-
에러 페이지 정적 에셋:
internal/errorpages/assets- TailwindCSS 빌드 결과:
errors.css(내장 CSS). - 로고 등 브랜드 리소스:
logo.svg등 (내장 가능). - 런타임에서는
/__hopgate_assets__/...prefix 로 HopGate 서버가 직접 서빙:- 1순위:
HOP_ERROR_ASSETS_DIR가 설정된 경우 해당 디렉터리에서 정적 파일 로드. - 2순위: 설정되지 않은 경우
internal/errorpages/assets에 embed 된 에셋 사용.
- 1순위:
- TailwindCSS 빌드 결과:
-
에러 페이지 렌더링 로직:
internal/errorpages/errorpages.go,cmd/server/main.gowriteErrorPage(w, r, status)→errorpages.Render호출.- HTML 로딩 우선순위:
-
HOP_ERROR_PAGES_DIR/<status>.html(env 미설정 시./errors/<status>.html)
-
internal/errorpages/templates/<status>.html(go:embed 기본 템플릿)
-
- 주요 사용처:
- 잘못된 ACME HTTP-01 요청 (400/404).
- 허용되지 않은 Host 요청 (404).
- DTLS 세션 부재/포워딩 실패 → 525 TLS/DTLS Handshake Failed 페이지.
3. Remaining Work / 남은 작업
3.1 Admin Plane Implementation / 관리 Plane 구현
-
DomainService 실제 구현 추가:
internal/admin/service.go- ent.Client + PostgreSQL 기반
RegisterDomain/UnregisterDomain구현. - domain + client_api_key 유효성 검증 로직 포함.
- ent.Client + PostgreSQL 기반
-
Admin API와 서버 라우터 연결:
cmd/server/main.gohttp.ServeMux혹은 router에admin.Handler.RegisterRoutes연결.- Admin API용 HTTP/HTTPS 엔드포인트 구성.
-
Admin API 키 관리
- env 혹은 설정에
ADMIN_API_KEY추가 및 로딩. - Admin Handler에 주입.
- env 혹은 설정에
3.2 DomainValidator Implementation / DomainValidator 구현
-
DomainValidator의 실제 구현 추가 (예:internal/admin/domain_validator.go).- ent.Client 를 사용해
Domain테이블 조회. (domain, client_api_key)조합 검증.- DummyDomainValidator 를 실제 구현으로 교체.
- ent.Client 를 사용해
-
DTLS Handshake 와 Admin Plane 통합
- Admin Plane 에서 관리하는 Domain 테이블을 사용해, 핸드셰이크 시
(domain, client_api_key)조합을 DB 기준으로 검증. - 도메인 문자열은
"host"또는"host:port"형태 모두 허용하되, DB 조회용 canonical 도메인에서는 host 부분만 사용.
- Admin Plane 에서 관리하는 Domain 테이블을 사용해, 핸드셰이크 시
3.3 Proxy Core / HTTP Tunneling
-
서버 측 Proxy 구현 확장:
internal/proxy/server.go- 현재
ServerProxy/Router인터페이스와NewHTTPServer만 정의되어 있고, 실제 HTTP/HTTPS 리스너와 DTLS 세션 매핑 로직은cmd/server/main.go의newHTTPHandler/dtlsSessionWrapper.ForwardHTTP안에 위치합니다. - Proxy 코어 로직을 proxy 레이어로 이동하는 리팩터링은 아직 진행되지 않았습니다. (3.6 항목과 연동)
- 현재
-
클라이언트 측 Proxy 구현 확장:
internal/proxy/client.go- DTLS 세션에서
protocol.Request수신 → 로컬 HTTP 호출 →protocol.Response전송 루프 구현. - timeout/취소/에러 처리.
- DTLS 세션에서
-
서버 main 에 Proxy wiring 추가:
cmd/server/main.go- DTLS handshake 완료된 세션을 Proxy 라우팅 테이블에 등록.
- HTTPS 서버와 Proxy 핸들러 연결.
-
클라이언트 main 에 Proxy loop wiring 추가:
cmd/client/main.go- handshake 성공 후
proxy.ClientProxy.StartLoop실행.
- handshake 성공 후
3.3A Stream-based DTLS Tunneling / 스트림 기반 DTLS 터널링
초기 HTTP 터널링 설계는 단일 JSON Envelope + 단일 DTLS 쓰기 방식(요청/응답 바디 전체를 한 번에 전송)이었고,
대용량 응답 바디에서 UDP MTU 한계로 인한 sendto: message too long 문제가 발생할 수 있었습니다.
이 한계를 제거하기 위해, 현재 코드는 DTLS 위 애플리케이션 프로토콜을 스트림/프레임 기반으로 재설계하여 StreamOpen / StreamData / StreamClose 를 사용합니다.
The initial tunneling model used a single JSON envelope + single DTLS write per HTTP message, which could hit UDP MTU limits (sendto: message too long) for large bodies.
The current implementation uses a stream/frame-based protocol over DTLS (StreamOpen / StreamData / StreamClose), and this section documents its constraints and further improvements (e.g. ARQ).
고려해야 할 제약 / Constraints:
- 전송 계층은 DTLS(pion/dtls)를 유지합니다. The transport layer must remain DTLS (pion/dtls).
- JSON 기반 단일 Envelope 모델에서 벗어나, HTTP 바디를 안전한 크기의 chunk 로 나누어 전송해야 합니다. We must move away from the single-envelope JSON model and chunk HTTP bodies under a safe MTU.
- UDP 특성상 일부 프레임 손실/오염에 대비해, 해당 chunk 만 재전송 요청할 수 있는 ARQ 메커니즘이 필요합니다. Given UDP characteristics, we need an application-level ARQ so that only lost/corrupted chunks are retransmitted.
아래 단계들은 feature/udp-stream 브랜치에서 구현할 구체적인 작업 항목입니다.
The following tasks describe concrete work items to be implemented on the feature/udp-stream branch.
3.3A.1 스트림 프레이밍 프로토콜 설계 (JSON 1단계)
3.3A.1 Stream framing protocol (JSON, phase 1)
-
스트림 프레임 타입 정리 및 확장:
internal/protocol/protocol.go- 이미 정의된 스트림 관련 타입을 1단계에서 적극 활용합니다.
Reuse the already defined stream-related types in phase 1:
MessageTypeStreamOpen,MessageTypeStreamData,MessageTypeStreamCloseEnvelope,StreamOpen,StreamData,StreamClose
StreamData에 per-stream 시퀀스 번호를 추가합니다. Add a per-stream sequence number toStreamData:- 예시 / Example:
type StreamData struct { ID StreamID `json:"id"` Seq uint64 `json:"seq"` // 0부터 시작하는 per-stream sequence Data []byte `json:"data"` }
- 예시 / Example:
- 이미 정의된 스트림 관련 타입을 1단계에서 적극 활용합니다.
Reuse the already defined stream-related types in phase 1:
-
스트림 ACK / 재전송 제어 메시지 추가:
internal/protocol/protocol.go- 선택적 재전송(Selective Retransmission)을 위해
StreamAck메시지와MessageTypeStreamAck를 추가합니다. AddStreamAckmessage andMessageTypeStreamAckfor selective retransmission:const ( MessageTypeStreamAck MessageType = "stream_ack" ) type StreamAck struct { ID StreamID `json:"id"` // 대상 스트림 / target stream AckSeq uint64 `json:"ack_seq"` // 연속으로 수신 완료한 마지막 Seq / last contiguous sequence LostSeqs []uint64 `json:"lost_seqs"` // 누락된 시퀀스 목록(선택) / optional list of missing seqs WindowSize uint32 `json:"window_size"` // 선택: 허용 in-flight 프레임 수 / optional receive window } Envelope에StreamAck *StreamAck필드를 추가합니다. ExtendEnvelopewith aStreamAck *StreamAckfield.
- 선택적 재전송(Selective Retransmission)을 위해
-
MTU-safe chunk 크기 정의
- DTLS/UDP 헤더 및 Protobuf/length-prefix 오버헤드를 고려해 안전한 payload 크기(4KiB)를 상수로 정의합니다. Define a safe payload size constant (4KiB) considering DTLS/UDP headers and Protobuf/length-prefix framing.
- 이 값은
internal/protocol/protocol.go의StreamChunkSize로 정의되었습니다. Implemented asStreamChunkSizeininternal/protocol/protocol.go. - 이후 HTTP 바디 스트림 터널링 구현 시, 모든
StreamData.Data는 이 크기 이하 chunk 로 잘라 전송해야 합니다. In the stream tunneling implementation, everyStreamData.Datamust be sliced into chunks no larger than this size.
3.3A.2 애플리케이션 레벨 ARQ 설계 (Selective Retransmission)
3.3A.2 Application-level ARQ (Selective Retransmission)
-
수신 측 ARQ 상태 관리 구현
- 스트림별로
expectedSeq, out-of-order chunk 버퍼(received), 누락 시퀀스 집합(lost)을 유지하면서, in-order / out-of-order 프레임을 구분해 HTTP 바디 버퍼에 순서대로 쌓습니다. - For each stream, maintain
expectedSeq, an out-of-order buffer (received), and a lost-sequence set (lost), delivering in-order frames directly to the HTTP body buffer while buffering/reordering out-of-order ones.
- 스트림별로
-
수신 측 StreamAck 전송 정책 구현
- 각
StreamData수신 시점에AckSeq = expectedSeq - 1과 현재 윈도우에서 누락된 시퀀스 일부(LostSeqs, 상한 개수 적용)를 포함한StreamAck{AckSeq, LostSeqs}를 전송해 선택적 재전송을 유도합니다. - On every
StreamDataframe, sendStreamAck{AckSeq, LostSeqs}whereAckSeq = expectedSeq - 1andLostSeqscontains a bounded set (up to a fixed limit) of missing sequence numbers in the current receive window.
- 각
-
송신 측 재전송 로직 구현 (StreamAck 기반)
- 응답 스트림 송신 측에서 스트림별
streamSender를 두고,outstanding[seq] = payload로 아직 Ack 되지 않은 프레임을 추적합니다. StreamAck{AckSeq, LostSeqs}수신 시:seq <= AckSeq인 항목은 모두 제거하고,LostSeqs에 포함된 시퀀스에 대해서만StreamData{ID, Seq, Data}를 재전송합니다.
- A per-stream
streamSendertracksoutstanding[seq] = payloadfor unacknowledged frames. Upon receivingStreamAck{AckSeq, LostSeqs}, it deletes allseq <= AckSeqand retransmits only frames whose sequence numbers appear inLostSeqs.
- 응답 스트림 송신 측에서 스트림별
Note: 현재 구현은 StreamAck 기반 선택적 재전송(Selective Retransmission) 까지 포함하며, 별도의 RTO(재전송 타이머) 기반 백그라운드 재전송 루프는 향후 확장 여지로 남겨둔 상태입니다. Note: The current implementation covers StreamAck-based selective retransmission; a separate RTO-based background retransmission loop is left as a potential future enhancement.
3.3A.3 HTTP ↔ 스트림 매핑 (서버/클라이언트)
3.3A.3 HTTP ↔ stream mapping (server/client)
-
서버 → 클라이언트 요청 스트림:
cmd/server/main.goForwardHTTP는 스트림 기반 HTTP 요청/응답을 처리하도록 구현되어 있으며, 동작은 다음과 같습니다.ForwardHTTPis implemented in stream mode and behaves as follows:- HTTP 요청 수신 시:
- 새로운
StreamID를 발급합니다 (세션별 증가). Generate a newStreamIDper incoming HTTP request on the DTLS session. StreamOpen전송:- 요청 메서드/URL/헤더를
StreamOpen의Header혹은 pseudo-header 로 encode. Encode method/URL/headers intoStreamOpen.Headeror a pseudo-header scheme.
- 요청 메서드/URL/헤더를
- 요청 바디를 읽으면서
StreamData{ID, Seq, Data}를 지속적으로 전송합니다. Read the HTTP request body and send it as a sequence ofStreamDataframes. - 바디 종료 시
StreamClose{ID, Error:""}를 전송합니다. When the body ends, sendStreamClose{ID, Error:""}.
- 새로운
- 응답 수신:
- 클라이언트에서 오는 역방향
StreamOpen으로 HTTP status/header 를 수신하고, 이를http.ResponseWriter에 반영합니다. Receive response status/headers via reverse-directionStreamOpenand map them tohttp.ResponseWriter. - 연속되는
StreamData를 수신할 때마다http.ResponseWriter.Write로 chunk 를 바로 전송합니다. For eachStreamData, write the chunk directly to the HTTP response. StreamClose수신 시 응답 종료 및 스트림 자원 정리. OnStreamClose, finish the response and clean up per-stream state.
- 클라이언트에서 오는 역방향
- HTTP 요청 수신 시:
-
클라이언트에서의 요청 처리 스트림:
internal/proxy/client.go- 서버로부터 들어오는
StreamOpen{ID, ...}을 수신하면, 새로운 goroutine 을 띄워 해당 ID에 대한 로컬 HTTP 요청을 수행합니다. On receivingStreamOpen{ID, ...}from the server, spawn a goroutine to handle the local HTTP request for that stream ID. - 스트림별로
io.Pipe또는 채널 기반 바디 리더를 준비하고,StreamData프레임을 수신할 때마다 이 파이프에 쓰도록 합니다. Prepare anio.Pipe(or channel-backed reader) per stream and write incomingStreamDatachunks into it. - 로컬 HTTP 클라이언트 응답은 반대로:
For the local HTTP client response:
- 응답 status/header →
StreamOpen(client → server) - 응답 바디 → 여러 개의
StreamData - 종료 시점에
StreamClose전송 SendStreamOpen(status/headers), then a sequence ofStreamData, followed byStreamClosewhen done.
- 응답 status/header →
- 서버로부터 들어오는
3.3A.4 JSON → 바이너리 직렬화로의 잠재적 전환 (2단계)
3.3A.4 JSON → binary serialization (potential phase 2)
- JSON 기반 스트림 프로토콜의 1단계 구현/안정화 이후, 직렬화 포맷 재검토 및 Protobuf 전환
- 현재는 JSON 대신 Protobuf length-prefix
Envelope포맷을 기본으로 사용합니다. The runtime now uses a Protobuf-based, length-prefixedEnvelopeformat instead of JSON. - HTTP/스트림 payload 는 여전히 MTU-safe 크기(예: 4KiB,
StreamChunkSize)로 제한되어 있어, 단일 프레임이 과도하게 커지지 않습니다. HTTP/stream payloads remain bounded to an MTU-safe size (e.g. 4KiB viaStreamChunkSize), so individual frames stay small.
- 현재는 JSON 대신 Protobuf length-prefix
- length-prefix 이진 프레임(Protobuf)으로 전환
- 동일한 logical model (
StreamOpen/StreamData(seq)/StreamClose/StreamAck)을 유지한 채, wire-format 을 Protobuf length-prefix binary 프레이밍으로 교체했고, 이는protobufCodec으로 구현되었습니다. We now keep the same logical model while using Protobuf length-prefixed framing viaprotobufCodec.
- 동일한 logical model (
- 이 전환은
internal/protocol내 직렬화 레이어를 얇은 abstraction 으로 감싸 구현했습니다.internal/protocol/codec.go에WireCodec인터페이스와 Protobuf 기반DefaultCodec을 도입해, 호출자는protocol.DefaultCodec만 사용하고, JSON codec 은 보조 용도로만 남아 있습니다. Ininternal/protocol/codec.go, theWireCodecabstraction and Protobuf-basedDefaultCodecallow callers to use onlyprotocol.DefaultCodecwhile JSON remains as an auxiliary codec.
3.4 ACME Integration / ACME 연동
-
internal/acme/acme.go실제 구현- lego 기반 ACME 매니저 구현.
- 메인 도메인 + 프록시 도메인용 인증서 발급/갱신.
- HTTP-01 챌린지 처리(webroot 방식).
-
서버 main 에 ACME 기반
*tls.Config주입- DTLS / HTTPS 리스너에 ACME 인증서 적용 (Debug 모드에서는 DTLS 에 self-signed, HTTPS 에 ACME 사용).
-
ACME 고급 기능 및 운영 전략 보완
- TLS-ALPN-01 챌린지 지원 여부 검토 및 필요 시 lego 설정/핸들러 추가.
- 인증서 발급/갱신 실패 시 재시도/백오프 및 경고 로그/알림을 포함한 에러 처리 전략 정의.
- Debug(스테이징 CA) / Production(실 CA) 환경 전환 플로우와 도메인/환경별 ACME 설정 매트릭스를 문서화.
3.5 Observability / 관측성
-
Prometheus 메트릭 노출 및 서버 wiring
cmd/server/main.go에 Prometheus/metrics엔드포인트 추가 (예: promhttp.Handler).- DTLS 핸드셰이크 성공/실패 수, HTTP 요청 수, HTTP 요청 지연, Proxy 에러 수에 대한 메트릭을 정의합니다.
- 메트릭 라벨은 메서드/상태 코드/결과/에러 타입 등에 한정되며, 도메인/클라이언트 ID/request_id 는 구조적 로그 필드로만 노출됩니다.
-
Loki/Grafana 대시보드 및 쿼리 예시
- Loki/Promtail 구성을 가정한 주요 로그 쿼리 예시 정리(도메인, 클라이언트 ID, request_id 기준).
- Prometheus 메트릭 기반 기본 대시보드 템플릿 작성 (DTLS 상태, 프록시 트래픽, 에러율 등).
3.6 Hardening / 안정성 & 구성
-
설정 유효성 검사 추가
- 필수 env 누락/오류에 대한 명확한 에러 메시지.
-
에러 처리/재시도 정책
- DTLS 재연결, Proxy 재시도, DB 재시도 정책 정의.
-
보안 검토
- Admin API 인증 방식 재검토 (예: IP allowlist, 추가 인증 수단).
- 클라이언트 API Key 저장/회전 전략.
-
Proxy 서버 추상화 및 Router 리팩터링
internal/proxy/server.go의ServerProxy및Router인터페이스를 실제 HTTP ↔ DTLS 터널링 경로에 적용.- 현재
cmd/server/main.go에 위치한 Proxy 코어 로직을 proxy 레이어로 이동.
4. Milestones / 마일스톤
Milestone 1 — DTLS Handshake + Admin + DB (기본 인증 토대)
- DTLS transport & handshake skeleton 구현 (server/client).
- Domain ent schema + PostgreSQL 연결 & schema init.
- DomainService 실제 구현 + DomainValidator 구현.
- Admin API + ent + PostgreSQL 연결 (실제 도메인 등록/해제 동작).
Milestone 2 — Full HTTP Tunneling (프락시 동작 완성)
- 서버 Proxy 코어 구현 및 HTTPS ↔ DTLS 라우팅.
- 현재
cmd/server/main.go의newHTTPHandler/dtlsSessionWrapper.ForwardHTTP경로에서 동작합니다.
- 현재
- 클라이언트 Proxy 루프 구현 및 로컬 서비스 연동.
cmd/client/main.go+ClientProxy.StartLoop()를 통해 DTLS 세션 위에서 로컬 서비스와 연동됩니다.
- End-to-end HTTP 요청/응답 터널링 E2E 테스트.
Milestone 3 — ACME + TLS/DTLS 정식 인증
- ACME 매니저 구현 (lego 기반).
- HTTPS/DTLS 리스너에 ACME 인증서 주입.
- ACME 고급 기능 및 운영 전략 정리 (예: TLS-ALPN-01, 인증서 롤오버/장애 대응 전략).
Milestone 4 — Observability & Hardening
-
Prometheus/Loki/Grafana 통합.
- Prometheus 메트릭 정의 및
/metrics엔드포인트는 이미 구현 및 동작 중이며, Loki/Promtail/Grafana 대시보드 및 운영 통합 작업은 아직 남아 있습니다.
- Prometheus 메트릭 정의 및
-
에러/리트라이/타임아웃 정책 정교화.
-
보안/구성 최종 점검 및 문서화.
이 progress.md 파일은 아키텍처/코드 변경에 따라 수시로 업데이트하며, Milestone 기준으로 완료 여부를 체크해 나가면 된다.
This progress.md file should be updated as the architecture and code evolve, using the milestones above as a checklist.