
[Blueprint W5] PonsWarp Desktop — WarpInfra, 브라우저를 떠난 QUIC 메시
브라우저를 떠난 QUIC 메시. Tauri2 warp-core, warp-agentd, mDNS, rarest-first로 인트라넷 릴리스 그리드를 만든 인프라.

# [Blueprint W5] PonsWarp Desktop — WarpInfra, 브라우저를 떠난 QUIC 메시
> Series: `blueprint-ponswarp` (5/5) · Category: `P2P/분산기술` + `개발 회고` · Slug: `blueprint-ponswarp-05-desktop-warpinfra`
> Source: `blueprints/specs/P01_ponswarp_family.md` §05 + `blueprints/05-ponswarp-desktop.png` + `ponswarp-desktop/src-tauri/warp-core`
—
## 0. TL;DR
`ponswarp-desktop`은 `Tauri2` 껍데기 안에 `warp-core(Rust)` 엔진을 넣고, `warp-agentd(QUIC 시더 데몬)`와 `GUI(React/Zustand)`가 같은 메시를 공유한다. `mDNS(_warpinfra._udp.local)`로 피어를 찾고, `QUIC + TLS CA pinning`으로 연결하며, `rarest-first bitfield`로 조각을 스케줄링한다. `grid`가 `TS`로 엔진을 뽑았다면, `desktop`은 `Rust`로 엔진에 `인프라`를 붙였다. 인트라넷 릴리스 그리드가 목표.

—
## 1. 문제 정의 — 왜 데스크톱인가
브라우저 P2P는 탭을 닫으면 끝이다. 그러나 사내 배포·연구소 데이터·현장 영상은 밤새 시딩되어야 한다. `ponswarp`는 `DataChannel`로 보내고 끝, `grid`는 `CLI`로 시딩하지만 `데몬`이 없다. `desktop`은 `warp-agentd`를 `systemd`로 띄워, GUI를 꺼도 시딩이 유지되게 했다. 그리고 `mDNS`로 같은 공유기 안 피어를 서버 없이 찾게 했다.
—
## 2. 설계도 한눈에 — 3개 바이너리가 한 메시
외곽 `Tauri2 앱` 테두리 안에 3노드.
* **warp CLI(warp-core):** `Tauri-free` 크레이트. `cargo run -p warp-core — send/receive`로도 돈다. `GUI` 없이 `CLI`로만 릴리스를 발행.
* **warp-agentd(QUIC 시더 데몬):** 트레이에 상주. `QUIC`로 피어와 `rarest-first`로 조각을 교환. `TLS CA pinning`으로 사내 CA만 신뢰.
* **GUI(React/Vite/Zustand):** `Tauri IPC`로 `warp-core`를 호출. 진행률·피어 목록을 보여준다.
세 노드는 같은 `mesh`에 참여: `seeder A → receiver B` 굵은 `QUIC` 화살표. `mDNS _warpinfra._udp.local` 점선으로 피어 탐색. `.warp.json 매니페스트` 게이트에서 `finalHashVerified SHA-256`으로 검증. `Control(SQLite control.db) 등록/로그인`, `heartbeat(JSON/Prometheus)` 점선, `systemd` 표기. `AI 에이전트 친화 –json` 라벨.
—
## 3. 아키텍처 해부 — warp-core가 중심
“`
src-tauri/
├── warp-core/ # Tauri-free, 순수 Rust 엔진
│ ├── lib.rs
│ ├── mesh.rs
│ └── manifest.rs
├── src/ # Tauri IPC 래퍼
└── warp-agentd/ # 데몬 (QUIC + mDNS)
“`
* **warp-core:** `Tauri`에 의존하지 않는 크레이트. `GUI`와 `CLI`가 같은 코드를 쓴다. `manifest.rs`가 `.warp.json`을 발행·검증.
* **warp-agentd:** `quinn` crate로 `QUIC` 메시. `mDNS`로 ` _warpinfra._udp.local` 브로드캐스트. `bitfield`로 `rarest-first` 스케줄링.
* **Control:** `SQLite control.db`에 `노드 등록/로그인`, `heartbeat`를 `JSON/Prometheus`로 노출. `systemd`로 부팅 시 자동 시작.
—
## 4. 로직 딥다이브 — 2개만 깊게
### 4.1 rarest-first bitfield 스케줄링
* 각 피어가 `bitfield`로 “내가 가진 조각”을 브로드캐스트.
* `warp-agentd`는 가장 희귀한 조각을 먼저 요청. 희귀 조각이 빨리 퍼져야 전체 완료 시간이 단축되기 때문. `BitTorrent`와 동일한 전략.
### 4.2 QUIC + TLS CA pinning
* `QUIC`는 `TCP+TLS`를 한 번에. 핸드셰이크가 빠르고, 연결 마이그레이션(와이파이→유선)에도 살아남는다.
* 사내 CA의 `pin`을 `warp-core`에 하드코딩. 외부 CA가 발급한 인증서로는 메시 참여 불가. 인트라넷 보안을 위한 선택.
—
## 5. 왜 이 선택인가
| 선택 | 채택 | 버린 대안 | 버린 이유 |
|—|—|—|—|
| 엔진 | Rust warp-core (Tauri-free) | TS grid 재사용 | `QUIC`·`mDNS`·`bitfield`를 TS로 구현하면 성능·안정성 저하 |
| 전송 | QUIC mesh | WebRTC DataChannel | `DataChannel`은 브라우저 종속, `QUIC`은 `CLI/daemon`에서도 동일 |
| 탐색 | mDNS | 중앙 coordinator | 인트라넷은 서버 없이 동작해야 함 |
—
## 6. 실패와 복구
* **실패:** 처음엔 `warp-core`를 `Tauri`에 직접 묶었다. 그러자 `CLI`에서 `warp-core`를 쓸 때마다 `Tauri` 런타임이 필요해 테스트가 무거워짐. `warp-core`를 `Tauri-free`로 분리해 해결.
* **복구:** `mDNS`가 `Docker` 브리지에서 동작 안 하는 문제. `host` 네트워크 모드로 전환하고, `warp-agentd`를 `systemd`에서 `NetworkManager-wait-online` 뒤에 시작.
—
## 7. 운영 증거
* `warp-core` — `cargo test` + `clippy`, `Tauri-free`로 `CLI`·`GUI` 공용
* `warp-agentd` — `systemd` 유닛, `heartbeat` `JSON/Prometheus`, `control.db` `SQLite`
* `mDNS` — `avahi` 없이 `Rust mDNS` 크레이트로 구현, `_warpinfra._udp.local` 탐색
—
## 8. 시리즈 마무리 — PonsWarp 5부작을 닫으며
`ponswarp`(원본) → `vnext`(헥사고날) → `lan-10mbps`(증거) → `grid`(TS 엔진) → `desktop`(Rust 인프라). 같은 문제(브라우저 P2P)를 5번 다르게 풀었다. 다음은 `PonsLink` 3부작: `pons_p2p`(룸), `control-tower`(SNS), `포트폴리오`(정적). `blueprint-ponslink-06`에서 이어간다.
*부록: `blueprints/05-ponswarp-desktop.png` 프롬프트 `prompts.part1.json#05`, 코드 진입점 `ponswarp-desktop/src-tauri/warp-core/src/lib.rs`*

댓글