# AetherStream 아키텍처 명세 **문서 번호**: ZD-SPEC-ARCH-002 **최종 갱신**: 2026-11-03 (프로토콜 v2 기준 전면 개정) **기준 커밋**: `ec71d7c` (main) 작업 트리. 코드 인용(`파일:줄`)은 이 시점 기준이며, 다른 팀의 동시 수정으로 줄 번호가 바뀔 수 있습니다. **근거 문서**: [`AUDIT_2026-11-03.md`](AUDIT_2026-11-03.md), [`ROADMAP.md`](ROADMAP.md), [`PROTOCOL.md`](PROTOCOL.md) > 와이어 레이아웃(바이트 오프셋, 옵코드 상세)은 이 문서에 중복 기재하지 않습니다. 반드시 [`PROTOCOL.md`](PROTOCOL.md)를 기준으로 합니다. > 성능 수치는 기재하지 않습니다. 지연 목표치: 기준선 측정 후 설정 ([`CLAIMS.md`](CLAIMS.md)). ### 상태 표기 | 상태 | 의미 | | :--- | :--- | | 구현됨 | 코드가 존재하고 라이브 경로에 연결됨 (성능·호환성 검증을 의미하지 않음) | | 부분 | 코드가 존재하나 결함이 있거나 일부만 연결됨 | | 미구현 | 코드 없음 | | 로드맵 | 미구현이며 [`ROADMAP.md`](ROADMAP.md)의 특정 마일스톤에 배정됨 | | 진행 중 (2026-11-03) | 현재 담당 팀이 구현 중. 완료로 간주하지 않음 | --- ## 1. 개요 AetherStream은 Windows 호스트의 화면·오디오를 캡처·인코딩하여 UDP로 클라이언트(Windows 데스크톱, Android, iOS)에 전송하고, 클라이언트의 키보드·마우스·컨트롤러 입력을 호스트로 역전송하는 시스템입니다. v2 구조의 핵심 변경점은 다음과 같습니다. 1. **단일 UDP 포트 47998**: 핸드셰이크·미디어·입력·제어를 하나의 포트로 통합. v1 입력 포트 47999는 무인증 `SendInput`을 허용했으므로 폐지. 2. **인증 + 암호화**: SPAKE2 기반 PIN 페어링 → 페어링된 기기 모드, 모든 미디어·입력·제어 패킷을 ChaCha20-Poly1305 SECURE 봉투(`0xE0`)로 전송. 3. **공용 코어**: 프로토콜·암호·재조립을 Rust 크레이트 `aether-core`(`core/`) 하나로 구현하고 모든 클라이언트가 C ABI로 링크. (감사 §4.3: 동일 로직의 Rust/C#/Kotlin/Swift 4중 구현이 이미 분기됨) 4. **WAN 연결 준비**: Cloudflare 랑데부 Worker + 8자리 룸코드, 스트림 소켓 자체에서의 STUN. ## 2. 전체 데이터 흐름 ``` 호스트 (server/, Rust) 클라이언트 ───────────────────────────── ───────────────────────────── DXGI DuplicateOutput ── GPU 텍스처 │ MF H.264 HW MFT ──(실패)──> OpenH264 SW │ H.264 Annex-B 슬라이스 분할 (VIDEO_FRAME_SLICE 0x10) │ aether-core: SECURE 0xE0 봉인 (k_h2c) ──── UDP 47998 ────> aether-core: 개봉·재전송 검사·재조립 │ WASAPI 루프백 ── AUDIO_FRAME 0x18 ──┘ 플랫폼 디코더 → 화면 출력 │ SendInput / ViGEm <── 개봉 (k_c2h) <──── UDP 47998 <──── INPUT_EVENT 0x20 (SECURE) ``` ## 3. 호스트 파이프라인 (`server/`) 호스트 모듈 단위 상세는 [`HOST_SERVER_SPEC.md`](HOST_SERVER_SPEC.md)를 참조합니다. | 단계 | 구현 | 상태 | 근거 / 비고 | | :--- | :--- | :--- | :--- | | 화면 캡처 | DXGI 1.2 Desktop Duplication, 프레임을 GPU 텍스처로 유지 | 구현됨 | `capture.rs:102` (`DuplicateOutput`), `:246` (`CopyResource`). 커서 합성(`GetFramePointerShape`) 없음 → 로드맵 M3 | | 영상 인코딩 | Media Foundation H.264 하드웨어 MFT. `MFTEnumEx(MFT_ENUM_FLAG_HARDWARE)`로 열거된 **첫 번째** 인코더 사용 | 부분 | `encoder.rs:228-243`. GPU 벤더별 동작 미검증. 결함: OpenH264 SPS/PPS 이식(`:329-342`), 키프레임 플래그가 실제 IDR이 아닌 요청 여부(감사 §2.3), 인트라 리프레시·B프레임 설정 미적용 | | 소프트웨어 폴백 | OpenH264 | 부분 | `encoder.rs:605-620`, 폴백 분기 `:822`. 범위 초과 읽기 결함(감사 §2.3, `:740-745`) | | 직접 NVENC SDK 연동 | — | 미구현 | 호스트팀 검토 중. 현재 코드는 NVENC SDK를 호출하지 않음 | | HEVC / AV1 / 10-bit HDR | — | 로드맵 | M9 | | 오디오 | WASAPI 루프백, 48 kHz 목표 포맷 | 부분 | `audio.rs:28`, `:402-420`. 현재 평문 `serialize()` 송신(`audio.rs:300`) → SECURE 전환 진행 중 | | 패킷화 | 최대 데이터그램 1400 B, 슬라이스 헤더 16 B | 부분 | 페이싱 없음, 1초 강제 IDR(감사 §2.5) | | FEC | 그룹당 XOR 패리티 1개(그룹 4) | 로드맵 | `fec.rs:93-148` 라이브러리 존재, 라이브 경로 호출 0건(감사 §2.1). M5 | | 키프레임 요청 `0x36`, NACK, 혼잡 제어 | — | 로드맵 | M3(키프레임 요청), M5(NACK·혼잡 제어) | | 키보드·마우스 주입 | Win32 `SendInput` | 부분 | `input.rs:121` 이하. 와이어 결함(감사 §2.4) 수정 진행 중 | | 가상 컨트롤러 | ViGEmClient.dll 동적 로딩, 최대 4슬롯 | 부분 | `party.rs:14`, `:274-287`. 버튼 ID가 플랫폼마다 다름(감사 §4.3). MLP는 1인 컨트롤러(ROADMAP §2) | | 클립보드 | `CF_UNICODETEXT`, 옵코드 **`0x50`** | 부분 | `protocol.rs:44`, `clipboard.rs`. 현재 무인증 → SECURE 내부 전용으로 전환 진행 중, 기본 OFF(ROADMAP M0) | | 모니터 전환 | `SET_MONITOR_REQ 0x42` / `RESP 0x43` | 부분 | `main.rs:494`, `monitor.rs` | ## 4. 전송·보안 계층 (프로토콜 v2) | 구성 요소 | 상태 | 비고 | | :--- | :--- | :--- | | 단일 UDP 포트 47998, 47999 폐지 | 진행 중 (2026-11-03) | 현재 배포 코드는 47998·47999 두 포트를 `0.0.0.0`에 바인딩(`main.rs:311-315`) | | CLIENT_HELLO 160 B 패딩 + 무상태 반환 경로 쿠키 | 진행 중 (2026-11-03) | PROTOCOL §3.2–3.3 | | SPAKE2 (Ed25519, symmetric) — 모드 0 PIN / 모드 1 페어링된 기기 | 진행 중 (2026-11-03) | PROTOCOL §3.1. 현 `auth.rs:311` `verify_pin`은 호출처 없음(감사 §2.1) | | HKDF 기반 방향별 키 `k_c2h` / `k_h2c` | 진행 중 (2026-11-03) | PROTOCOL §3.6 | | SECURE 봉투 `0xE0`: ChaCha20-Poly1305, 64비트 카운터 논스, 1024 재전송 윈도우 | 진행 중 (2026-11-03) | PROTOCOL §4. 기존 `PacketCipher`(`protocol.rs:1169` 이하)는 라이브 경로 미사용·논스 재사용 위험으로 재작성 대상 | | 실패 잠금(IP별 5회/5분, 전역 20회 시 PIN 교체) | 진행 중 (2026-11-03) | PROTOCOL §3.7 | | PAIRING_GRANT `0x64` + 클라이언트 보안 저장소 | 진행 중 (2026-11-03) | PROTOCOL §5.9 | | 세션 5초 타임아웃, DISCONNECT `0x6F` | 진행 중 (2026-11-03) | PROTOCOL §5.10 | **현재 배포본(v0.1.0)의 실제 상태**: 라이브 경로 전체가 평문·무인증입니다(감사 §2.2). 위 표의 항목이 완료되어 ROADMAP M2 완료 기준(pcap 평문 부재, 재전송·주입·잘못된 PIN 거부 자동 테스트)을 통과하기 전까지 "암호화 적용"으로 표기하지 않습니다. ## 5. 공용 코어 `aether-core` (`core/`) | 항목 | 상태 | 비고 | | :--- | :--- | :--- | | `core/` Rust 크레이트 (sans-IO: 바이트 입력 → 이벤트 출력) | 진행 중 (2026-11-03) | 2026-11-03 작업 트리에 `core/` 크레이트 골격(`Cargo.toml`, `src/wire.rs`)이 생성되어 작업 중이며 미커밋·미검증 | | 패킷 프레이밍, 정규 입력 인코딩, 슬라이스 재조립, 세션 상태 머신 | 진행 중 (2026-11-03) | | | C ABI (`extern "C"`) + C# 바인딩 | 진행 중 (2026-11-03) | ROADMAP M2 | | Kotlin/Swift 바인딩 | 로드맵 | M6(Android), M8(iOS) | | 데스크톱 중복 구현 삭제 (`FrameReassembler.cs`, `RoomCodeCodec.cs`, 패킷 빌더) | 진행 중 (2026-11-03) | | ## 6. 연결 수립 (LAN / WAN) | 항목 | 상태 | 비고 | | :--- | :--- | :--- | | LAN 직접 연결 (호스트 IP:47998) | 부분 | 현재 평문 v1 경로로 동작 코드 존재. v2 핸드셰이크 전환 진행 중 | | 6자리 레거시 룸코드 (LAN IP:포트 가역 인코딩) | 부분 | 비밀이 아님(`nat.rs:373-470`, 감사 §2.2). 오프라인 LAN 용도로만 유지 | | Cloudflare 랑데부 Worker + 8자리 룸코드 `XXXX-XXXX` | 배포됨, 실환경 미검증 (2026-11-03) | [`RENDEZVOUS.md`](RENDEZVOUS.md). Worker `cloudflare/rendezvous/` 배포(`aether-rendezvous.zelodoc08.workers.dev`, CI 자동 배포). 호스트·Windows 클라이언트·Android는 기본값으로 이 서비스를 사용(`AETHER_RENDEZVOUS_URL=off`로 끔). 실제 NAT 너머 연결은 미검증. 코드만으로는 아무 권한도 얻지 못하며 PIN/페어링 핸드셰이크가 여전히 필요 | | 스트림 소켓(47998)에서 STUN `stun.cloudflare.com:3478` | 진행 중 (2026-11-03) | 현재 코드는 임시 소켓에서 STUN 수행 가능(`nat.rs:235-241`) → NAT 포트 변환 시 결과 오류 | | TURN 릴레이 / 실제 WAN 원격 접속 | 로드맵 | M7. 그 전까지 메쉬 VPN을 문서화된 임시 경로로 안내([`USER_GUIDE.md`](USER_GUIDE.md)) | ## 7. 클라이언트 파이프라인 ### 7.1 Windows 데스크톱 (`desktop/`, C# WPF .NET 9) | 단계 | 상태 | 비고 | | :--- | :--- | :--- | | UDP 수신·재조립 | 부분 | 현재 자체 C# 구현 → aether-core로 교체 진행 중 | | H.264 디코딩 (Media Foundation MFT) | 부분 | 소프트웨어 디코딩(`SET_D3D_MANAGER` 미사용, 감사 §3.1). 테스트 하네스에서 1080p 5프레임 디코딩 1회 실행 확인 | | 수신·디코딩 스레드 분리 (UI 스레드에서 제거) | 진행 중 (2026-11-03) | | | D3D11VA 하드웨어 디코딩, GPU 색변환 | 로드맵 | M3. 현재 CPU NV12→BGR32 변환(1080p 1프레임 6.644 ms, 1회 내부 측정 — 공개 수치 아님) | | 오디오 재생 | 로드맵 | M3 | | 가짜 UI 표기·200 ms 가짜 Connected 제거 | 진행 중 (2026-11-03) | 감사 §3.2 | 상세: [`DESKTOP_CLIENT_SPEC.md`](DESKTOP_CLIENT_SPEC.md) ### 7.2 Android (`client-android/`) MediaCodec 디코더 경로 코드 존재(상태: 부분). 본 감사에서 빌드 미검증. aether-core 연동은 로드맵 M6. 상세: [`MOBILE_CLIENT_SPEC.md`](MOBILE_CLIENT_SPEC.md) ### 7.3 iOS (`client-ios/`) VideoToolbox/Metal 경로 코드 존재(상태: 부분). Xcode 앱 타깃이 없어 빌드·배포 불가. 로드맵 M8. 상세: [`MOBILE_CLIENT_SPEC.md`](MOBILE_CLIENT_SPEC.md) ## 8. 보안 경계 위협·완화·잔여 리스크는 [`THREAT_MODEL.md`](THREAT_MODEL.md)에 정의합니다. ## 9. 측정 현재 제품 지연 수치는 실측 근거가 없습니다(감사 §1.4). Glass-to-Glass 측정 하네스와 단계별 타임스탬프는 로드맵 M1입니다. 모든 공개 수치는 [`CLAIMS.md`](CLAIMS.md)에 측정 파일 링크와 함께 등록된 후에만 게시합니다.