본문으로 건너뛰기
5분 AI.
TRENDSHIFT DAILY #16 딥다이브 · 2026-10-23 분석

ds-harness-remote 딥다이브
— 내 코딩 에이전트까지 이어지는 암호화 통로

플러그인·클라이언트

빠른 판단

무엇
DeepSeek Harness의 세션·파일·터미널을 P2P 우선 암호화 연결로 여러 기기에 제공하는 원격 접속 플러그인
이럴 때
작업 PC의 에이전트를 유지한 채 휴대전화나 다른 컴퓨터에서 프롬프트를 보내고 결과를 확인할 때
집이나 회사의 컴퓨터에서 실행 중인 DeepSeek Harness를 휴대전화와 다른 컴퓨터에서 이어 쓰게 하는 원격 접속 프로젝트입니다. 새 AI 모델을 만드는 대신, 기존 작업 환경에 안전하게 도달하는 길을 만듭니다. TypeScript·React Native·WebSocket·WebRTC·Noise가 하나의 제품 안에서 어떻게 역할을 나누는지 소스 수준으로 살펴봅니다. 저장소: liguobao/ds-harness-remote · 2026-10-23 GitHub 기준 스타 699 · 포크 70 · 최신 릴리스 v0.5.18 · 코드 기준 커밋 fac5e60.
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

실행 중인 코딩 환경은 그대로 두고, 접속하는 창구를 늘린다

ds-harness-remote는 DeepSeek Harness의 플러그인으로 동작하는 Host와 여러 Client를 연결합니다. 사용자가 Android에서 프롬프트를 보내면 실제 작업은 Host의 Harness 또는 연결된 에이전트 백엔드가 수행합니다. 작업 환경을 휴대전화로 옮기는 대신, 원래 컴퓨터의 작업 환경에 암호화된 요청을 보내는 구조입니다.

용어
Host와 Client
Host는 실제 프로젝트와 도구를 가진 작업 컴퓨터 쪽입니다. Client는 그 컴퓨터에 요청을 보내고 결과를 보여 주는 접속 기기입니다. 한 컴퓨터가 Host이면서 다른 Host에 접속하는 Client일 수도 있어, 역할과 물리적 기기를 구분해야 합니다.
한 문장 비유

작업실은 그대로 두고, 열쇠가 있는 사람에게만 원격 조종석을 내어 줍니다

Host는 공구와 작업물이 있는 작업실, Client는 조종석입니다. Server는 어느 작업실이 온라인인지 알려 주고 연결을 중개하는 안내 데스크입니다. 대화 내용은 봉인된 운송 상자에 들어가므로, 중계 역할만 하는 Server는 상자 속 업무 내용을 해독할 키를 갖지 않습니다.

다만 문을 통과했다고 모든 공구를 쓸 수 있는 것은 아닙니다. Host는 요청마다 허용된 API인지 다시 검사합니다. 연결의 신원 확인과 작업의 권한 확인이 별도로 필요합니다.

1-1. 한 번의 요청이 지나가는 길

  1. Host와 Client가 같은 Server에 각자의 기기 자격 증명으로 연결합니다.
  2. Client가 Host를 선택하고, Host는 계정 소속과 고정된 상대 공개키를 확인합니다.
  3. 두 기기가 Noise IK 핸드셰이크를 마쳐 연결 전용 암호화 상태를 만듭니다.
  4. Client가 RPC 요청을 전송하고, Host가 허용된 Harness·Codex·ACP 기능으로 전달합니다.
  5. 결과와 실시간 이벤트가 다시 암호화되어 Client 화면에 나타납니다.

Desktop에서는 Harness의 기존 UI를 재사용하고, Android는 React Native 앱을 제공합니다. VS Code 확장과 Chrome/Edge 확장도 포함되지만 역할이 다릅니다. apps/browser는 온라인 Host를 골라 Remote Web을 여는 입구이며, 자체 대화 UI나 원격 전송 엔진을 구현한 브라우저 앱은 아닙니다.

읽기 범위
공유 라이브러리부터 제품의 경계를 배운다

처음에는 233줄 분량인 packages/client-core/src/index.ts부터 읽어 보세요. 요청 ID, 타이머, 취소, 연결 종료만으로도 네트워크 프로그램의 핵심 실패 상황을 배울 수 있습니다. 그다음 packages/webrtc, packages/crypto, Host bridge 순서로 넓히면 됩니다.

2왜 주목받는가

코딩 에이전트를 오래 켜 두고 여러 기기에서 이어 쓰는 문제

2-1. 선정 사실과 관심의 이유를 구분하기

이 글의 선정 근거는 TrendShift Daily #16입니다. 2026-10-23 GitHub 기준 스타 699, 포크 70이며, 생성일은 2026-08-14입니다. 이 숫자만으로 사용자 증가 속도나 인기의 원인을 확정할 수는 없습니다. 아래의 주목 포인트는 README와 구현을 바탕으로 한 기술적 해석입니다.

코딩 에이전트는 프로젝트 파일, 터미널, 인증 상태, 대화 이력에 의존합니다. 이동할 때마다 다른 기기에 환경을 복제하면 같은 작업을 이어 가기 어렵습니다. 이 프로젝트는 오래 실행되는 Host에 여러 화면을 연결해 그 마찰을 줄이려 합니다.

주목할 지점은 원격 접속 자체보다, 코딩 에이전트의 세션·승인·파일·터미널을 연결 단위로 다루는 설계입니다. ConnectionController.acceptOne()은 인증된 연결마다 별도의 router를 만들고, HarnessRemoteBridge는 스트림과 큰 응답의 전송 상태를 관리합니다. 채팅 화면만 붙이는 일과는 구현 범위가 다릅니다.

2-2. 다른 접근과 비교하면 보이는 위치

아래 표는 특정 상용 제품의 최신 기능표가 아니라 접근 방식의 비교입니다. 우열은 원격으로 무엇을 하려는지에 따라 달라집니다.

접근 방식주로 보내는 것이 레포의 상대적 장점과 경계
SSH + 터미널 세션명령과 터미널 출력에이전트의 작업공간·대화·승인을 앱 UI로 표현합니다. 일반 셸 관리의 범용성은 별도 문제입니다.
원격 데스크톱화면과 입력 이벤트업무 API와 이벤트를 전달하므로 휴대전화용 UI를 따로 구성할 수 있습니다. 전체 데스크톱 조작은 제공하지 않습니다.
VPN + 기존 웹 UI사설망 접근 경로기기 신뢰, 연결별 RPC와 업무 권한 검사가 프로젝트 안에 통합됩니다. VPN의 일반 네트워크 접근과 목적이 다릅니다.
범용 터널포트로 흐르는 바이트고정 API 허용 목록과 세션 범위를 적용합니다. 임의 TCP 전달을 위한 도구로 해석하면 안 됩니다.
ds-harness-remote암호화한 RPC·이벤트Harness UI 재사용과 다중 접속에 초점을 둡니다. Harness 버전과 별도 Server 기능에 영향을 받습니다.

2-3. P2P 우선이라는 설명의 정확한 범위

공유 전송 구현인 AdaptiveTransport의 기본 선호 순서는 ['lan', 'p2p', 'turn', 'relay']입니다. 하지만 실제 경로는 양쪽 capability와 Server 지원을 협상해 정합니다. 직접 통신을 시도한다고 항상 직접 연결되는 것은 아닙니다.

범위 확인
이 저장소를 배포하는 것과 전체 서비스를 복제하는 것은 다릅니다

apps/server/src/gateway.ts는 webrtcEnabled: false와 ['transport.relay']를 응답합니다. 자체 호스팅 버전은 단일 계정·단일 프로세스의 Relay 서버입니다. 전체 Remote Web 대화 화면, 다중 계정 관리, WebRTC/TURN 서비스는 이 서버에 포함되지 않습니다.

README의 Web 지원은 제품 전체 설명입니다. 이 저장소에 모든 Web 서비스의 구현이 들어 있다는 뜻은 아닙니다.

2-4. 최신 릴리스에서 읽을 수 있는 실제 문제

.github/release-notes.md의 v0.5.18 기록은 Android 로그인 방식 자동 조회, 서버 변경 후 늦게 도착한 로그인 결과의 격리, 도구 응답 파싱 경계, 대기 메시지 표시 수정 등을 설명합니다. 이는 새로운 네트워크 기술만큼 비동기 상태의 일관성이 제품 품질에 중요함을 보여 줍니다.

해당 릴리스 기록에 따르면 전체 794개 테스트가 통과했습니다. v0.5.18에서 추가된 실기기·기기 간 검증은 없다고 명시되어 있으므로, 자동화 테스트 결과와 실환경 검증 범위를 구분해야 합니다.

3기술 스택 전체 지도

선언된 버전 범위와 잠금 파일의 확정 버전을 구분한다

루트 package.json과 각 하위 패키지는 버전 0.5.18로 맞춰져 있습니다. pnpm-workspace.yaml은 apps/*, packages/*, examples/*를 묶습니다. 여러 앱이 같은 프로토콜을 공유하되, 각 실행 환경의 의존성과 버전은 따로 관리합니다.

3-1. 백엔드와 Host 플러그인

영역의존성 파일의 실제 표기구현에서 맡는 일
자체 호스팅 서버Node.js 22 · ws ^8.21.3 · zod ^3.24.1Node HTTP 서버, WebSocket 제어·중계, 요청·기기 정보 검증
Host 확장 기반@deepseek-ai/cordis 4.0.5 · @deepseek-ai/dsh-api-gateway 0.2.1-rc.1개발 의존성 기준 플러그인 구성과 공식 Gateway 접속
Host WebRTCwerift 0.24.4 · 선택 의존성 @roamhq/wrtc 0.10.0Node 환경의 WebRTC 구현 선택지
암호화@lukeburns/clatterjs 1.0.0 · @noble/ciphers ^1.1.3Noise 핸드셰이크와 인증 암호화 원시 기능
키와 해시@noble/curves ^1.7.0 · @noble/hashes ^1.6.1X25519 키, HKDF, SHA-256
Host 터미널@xterm/headless 6.0.0 · @xterm/addon-serialize 0.14.0화면 상태 유지와 재접속 스냅샷

근거 파일은 apps/server/package.json, packages/plugin/package.json, packages/crypto/package.json입니다. 자체 호스팅 서버는 Express·NestJS 없이 node:http의 createServer()를 사용합니다. apps/server/src/store.ts는 데이터베이스 대신 JSON 파일과 메모리 상태를 조합합니다.

3-2. 프론트엔드와 기기별 접속면

접속면선언 버전읽어 볼 구현
Androidexpo ~57.0.13, react 19.2.3, react-native 0.86.2apps/android/src/state와 apps/android/src/screens
Android 전송·상태react-native-webrtc ^124.0.8, zustand ^5.0.15네이티브 WebRTC와 상태 저장소
Android 보관·렌더링expo-secure-store ~57.0.1, react-native-webview 13.16.1, pdfjs-dist 6.3.289자격 증명 보관과 로컬 번들 기반 문서·터미널 표현
서버 상태 Web UIreact ^18.3.1, antd ^5.22.7, vite ^6.0.3apps/server/web/src/main.tsx의 로그인·기기 화면
Desktop 플러그인 UIReact peer 범위 >=18.2.0 <19packages/plugin/src/client/에서 Harness UI 확장
VS Code엔진 ^1.96.0, @vscode/vsce ^3.2.1apps/vscode의 확장과 SecretStorage 연결
브라우저 확장Manifest V3 · lucide ^0.469.0apps/browser/manifest.json의 popup·service worker
용어
버전 범위와 잠금 파일
^3.24.1은 package.json이 허용하는 범위이고, 실제로 고정한 버전은 잠금 파일에서 확인합니다. 루트 패키지의 Zod는 pnpm-lock.yaml에서 3.25.76으로 고정되어 있습니다. 선언 버전과 설치 결과를 같은 숫자로 취급하면 재현성 설명이 틀어집니다.

3-3. 인프라·빌드·배포

루트 package.json에는 의도적으로 scripts가 없습니다. AGENTS.md의 설명에 따르면 Harness가 GitHub에서 플러그인을 설치할 때 루트 패키지를 사용하기 때문입니다. 커밋된 packages/plugin/dist/index.js와 packages/plugin/dist/client.github.js는 배포 진입점이며, CI가 다시 만든 번들과 차이가 없는지 검사합니다. 일반적인 “모든 dist는 무시한다” 규칙을 그대로 적용할 수 없는 사례입니다.

4아키텍처 심화 분석

신호를 조정하는 길과 업무 데이터를 보내는 길을 분리한다
[Android / Desktop / VS Code Client] UI / 상태 저장소 | RemoteClientCore 요청 ID · 타임아웃 · 이벤트 | 보안 전송 래퍼 분할 + Noise 암호화 | AdaptiveTransport |-------------------- WebSocket -------------------+ | | | [Remote Server] | | 계정 · 기기 · 연결 | | 신호 · 암호문 중계 | | | +==== LAN / P2P / TURN의 DataChannel ==========+ | +==== WebSocket Relay의 암호문 ================|=====+ | [Host Remote Plugin] Noise 복호화 ConnectionController 연결별 RpcRouter | +--------------------------+-------------------+ | | | HarnessRemoteBridge codex/ 도메인 acp/ 도메인 공식 Typert Gateway App Server 백엔드 adapter | | | 기존 Harness 환경 Codex 프로세스 ACP 계열 프로세스 [apps/server] 계정 1개 · Relay 전용 · 로그인/기기 상태 UI [별도 전체 Server] Remote Web 대화 UI · WebRTC/TURN 연동 등 [브라우저 확장] 같은 출처의 Remote Web을 여는 입구

위 도식의 보안 전송 래퍼는 역할을 나타냅니다. 실제 구현은 Client마다 접속 코드가 있으며, Desktop 쪽 대표 구현이 ClientSecureTransport입니다. Server와 오가는 제어 연결과 기기끼리 보내는 업무 데이터는 개념적으로 분리되지만, Relay를 선택하면 둘 다 같은 WebSocket을 이용할 수 있습니다.

4-1. 설계 패턴 ① 인터페이스로 경로를 교체한다

packages/webrtc/src/transport.ts의 RemoteTransport는 바이트를 보내고 받는 최소 약속을 정의합니다. RPC 계층은 현재 경로가 WebRTC인지 Relay인지 몰라도 이 약속만 사용하면 됩니다.

// 원본 RemoteTransport에서 핵심 메서드만 발췌
export interface RemoteTransport {
  connect(): Promise<void>
  send(data: Uint8Array): Promise<void>
  onMessage(cb: (data: Uint8Array) => void): () => void
  onClose?(cb: () => void): () => void
  close(): Promise<void>
  getStats(): TransportStats
}

AdaptiveTransport.negotiate()는 capability로 걸러진 선호 경로를 이용합니다. WebRTC를 쓸 수 있으면 tryWebRtc()를 호출하고, 협상 실패 시 허용된 Relay로 내려갑니다. forceRelay: true는 선호 경로를 Relay 하나로 제한합니다.

업무 요청은 같은 형태를 유지하고, 연결 경로와 암호화는 그 아래 계층에서 책임집니다. 이런 분리는 새 전송 수단을 추가할 때 UI나 모든 RPC 호출부를 바꾸지 않게 해 줍니다.

소스의 세부 동작
협상 실패와 사용 중인 경로의 장애는 처리 방식이 다릅니다

negotiate()의 실패는 Relay 선택으로 이어질 수 있습니다. 반면 이미 사용 중인 WebRTC가 닫히면 rtc.onClose()가 failConnection()을 호출합니다. README도 새 전송과 새 Noise 채널로 다시 연결한다고 설명합니다. 기존 암호화 상태를 그대로 유지한 무중단 경로 전환으로 표현하면 부정확합니다.

4-2. 설계 패턴 ② 전송 위에 인증 암호화 래퍼를 씌운다

packages/plugin/src/client-secure-transport.ts의 ClientSecureTransport는 내부 SecureHandshakeTransport를 감싸면서 자신도 RemoteTransport를 구현합니다. 기존 인터페이스를 유지한 채 보안 책임을 추가하는 데코레이터 패턴으로 읽을 수 있습니다.

  1. connect()가 내부 전송을 연결하고, 반환된 기기 ID가 예상한 Host·Client와 같은지 확인합니다.
  2. NoiseIkSession을 만들고 createNoisePrologue()에 연결 ID와 두 기기의 ID를 묶습니다.
  3. waitForResponder()가 두 번째 핸드셰이크 메시지를 기다립니다. 기본 제한 시간은 10초입니다.
  4. send()는 메시지를 분할한 뒤 각 조각을 암호화합니다. 수신은 복호화 후 재조립합니다.
  5. 인증 실패나 분할 형식 오류가 발생하면 연결을 닫아, 불확실한 상태에서 업무 요청을 계속 처리하지 않습니다.

packages/crypto/src/noise.ts는 암호 모음을 Noise_IK_25519_ChaChaPoly_SHA256으로 고정합니다. verifyRemoteStatic()은 상대의 정적 공개키가 로컬에서 신뢰한 키와 같은지 확인합니다. 계정 로그인 성공만으로 상대 기기의 정체까지 보장되지는 않는다는 뜻입니다.

// createNoisePrologue()가 만드는 문맥의 형태
DSH-REMOTE\0v=1\0connection=...\0host=...\0client=...

// 메시지의 계층별 변화
RPC JSON 바이트
  -> SecureMessageCodec.encode()
  -> NoiseIkSession.encrypt()
  -> DataChannel 또는 Relay
  -> NoiseIkSession.decrypt()
  -> SecureMessageCodec.decode()
  -> RPC 요청 처리

Noise 공식 명세는 메시지 상한을 65,535바이트로 정합니다. 그래서 이 프로젝트의 SecureMessageCodec은 48 KiB보다 큰 메시지를 먼저 분할합니다. 완성 메시지 상한은 4 MiB, 동시에 조립할 수 있는 메시지는 8개입니다.

분할 헤더에는 메시지 ID·조각 순번·전체 조각 수·전체 길이가 들어갑니다. decode()는 순번과 길이를 검증하므로, 조각을 모으는 배열 하나로 끝나는 구현이 아닙니다.

4-3. 설계 패턴 ③ 요청 ID와 수명으로 비동기를 관리한다

packages/client-core/src/index.ts의 RemoteClientCore.rpc()는 pending: Map<string, PendingCall>에 요청별 완료 함수와 타이머를 넣습니다. 응답은 requestId로 원래 요청을 찾아 완료시킵니다. 응답 순서가 바뀌어도 요청을 구별할 수 있습니다.

// 원본 제어 흐름을 줄여 표현한 학습용 의사 코드
요청 A 등록 -> pending[A] = 완료 함수 + 타이머 + 취소 해제 함수
요청 B 등록 -> pending[B] = 완료 함수 + 타이머 + 취소 해제 함수
응답 B 도착 -> takePending(B) -> B만 완료
A 취소      -> takePending(A) -> A만 거절
늦은 응답 A -> 등록된 요청 없음 -> 무시

takePending()은 Map에서 제거하고 타이머와 취소 리스너를 함께 정리합니다. 기본 RPC 제한 시간은 30초이며, 전송 자체를 await한 뒤 기다리지 않습니다. send()가 끝나지 않는 상황에서도 타임아웃·취소·연결 종료가 호출자를 풀어 줄 수 있게 한 선택입니다.

여기서의 취소는 우선 Client의 기다림을 끝내는 동작입니다. 이것만 보고 Host의 실행 중인 작업도 반드시 취소됐다고 말하면 안 됩니다. 작업 중단은 session/cancel 등 해당 업무 API의 의미를 따로 확인해야 합니다.

4-4. 설계 패턴 ④ 연결마다 권한과 상태를 격리한다

packages/plugin/src/connection-controller.ts의 acceptOne()은 Noise 프로토콜, 비어 있지 않은 연결·membership 문맥, IdentityStore.isTrusted()를 확인합니다. 통과한 연결에는 createRouter({ connectionId, peerDeviceId }, ...)로 router를 만듭니다.

이후 packages/plugin/src/harness-remote-bridge.ts의 HarnessRemoteBridge.call()이 HARNESS_REMOTE_ALLOWLIST와 입력 스키마를 검사합니다. 예를 들어 일반 설정 API가 허용 목록에 있어도 Remote 자신의 설정을 바꾸는 요청은 별도로 거절합니다.

// HarnessRemoteBridge.call()의 원본 일부
if (['settings/update', 'settings/replace', 'settings/mutate'].includes(params.endpoint)) {
  const args = requestArgs(params.payload)
  if (args.ns === 'ds-harness-remote' || args.ns === 'dsh-remote') {
    throw new RpcError('PERMISSION_DENIED',
      'Remote access settings can only be changed locally on the Host.')
  }
}

이 검사는 원격 접속자가 터미널·포트 접근 정책을 원격에서 다시 넓히는 것을 막는 경계입니다. ACP도 packages/plugin/src/acp/method-policy.ts의 parseAcpCall()에서 메서드와 매개변수를 제한합니다. 암호화와 허용 목록은 서로 다른 문제를 해결합니다.

비유

연결별 router는 호텔 객실별 프런트 담당과 같습니다. 같은 계정의 기기라도 어느 연결의 승인·구독·터미널 입력권인지 구분해야 합니다. 방 번호를 구분하지 않으면 A가 요청한 승인에 B가 응답하는 식의 혼선이 생깁니다.

5디렉토리 구조 해부

앱의 입구, 공유 약속, 실제 작업 실행을 나눠 읽는다
ds-harness-remote/ ├── package.json GitHub 플러그인 설치 경계 · 버전 ├── pnpm-workspace.yaml 앱·공유 패키지·예제의 묶음 ├── pnpm-lock.yaml 의존성 확정 버전 ├── index.js Host 배포 입구 ├── apps/ │ ├── android/ │ │ ├── src/screens/ 기기·작업공간·대화 화면 │ │ ├── src/state/ 상태와 계정·연결 동작 │ │ ├── src/services/ 로그인·전송·보관·파일 미리보기 │ │ └── app.json 네이티브 앱과 Android 설정 │ ├── vscode/ IDE 확장 │ ├── browser/ MV3 접속 입구 │ └── server/ │ ├── src/server.ts HTTP 인증·기기 API │ ├── src/gateway.ts 제어 연결·핸드셰이크·Relay │ ├── src/store.ts JSON 기기·토큰 상태 │ ├── web/ 로그인·기기 상태 화면 │ └── Dockerfile 자체 호스팅 이미지 ├── packages/ │ ├── protocol/src/ 메시지·스키마·분할 규칙 │ ├── crypto/src/ 키 생성·Noise 세션 │ ├── webrtc/src/ 전송 추상화·협상·역압력 │ ├── client-core/src/ RPC·이벤트·업무별 Client │ └── plugin/ │ ├── src/client/ Harness UI 확장 │ ├── src/codex/ Codex App Server 연결 │ ├── src/acp/ ACP Gateway·백엔드 adapter │ ├── src/*bridge.ts 허용된 업무 API의 경계 │ ├── tests/ Host 핵심 계약 검증 │ └── dist/ 커밋된 GitHub 배포 번들 ├── examples/mock-host/ 외부 Server 연동용 예제 ├── fixtures/ 프로토콜·암호화 기준 데이터 ├── docs/ 통신 계약·설계·제약 └── .github/workflows/ CI·릴리스·Android APK

5-1. 폴더 이름보다 의존성 방향을 본다

protocol은 통신 양쪽이 동의해야 할 데이터 형태를 제공합니다. crypto는 기기 키와 암호화 상태를 담당하고, webrtc는 전송 경로를 담당합니다. client-core는 그 위에서 요청·응답과 업무 이벤트를 연결합니다.

앱은 이 공유 코드를 조합하되 로그인 UI, 네이티브 보관소, 네트워크 생명주기 같은 플랫폼별 문제를 직접 처리합니다. Host 플러그인은 공식 Harness API와 외부 에이전트 프로세스에 접속하므로 가장 많은 업무 코드가 모입니다. 폴더를 분리했다는 사실보다 어느 계층이 어느 사실을 알아야 하는가가 중요합니다.

5-2. 커밋 기준 코드 규모

집계 대상은 커밋 fac5e60의 추적 파일이며, 확장자별 물리적 줄 수를 기준으로 합니다. 빈 줄·주석·테스트를 포함하며, packages/plugin/dist/ 아래 배포 번들은 별도 항목입니다. JSON·YAML·Markdown·이미지는 아래 언어별 합계에 포함하지 않았습니다.

언어·형식파일 수물리적 줄 수해석
TypeScript (.ts)36490,647프로토콜·Host·클라이언트 서비스와 테스트 포함
TSX (.tsx)4211,001React·React Native 화면
JavaScript 계열181,317.js·.mjs·.cjs 빌드 보조 등
CSS32,615서버 Web UI 등의 스타일
Kotlin4364Android 네이티브 연동
Shell / HTML1 / 2251 / 15설치 스크립트와 HTML 입구
제외한 배포 번들248,155packages/plugin/dist/의 JavaScript, 원본 규모와 중복

TypeScript와 TSX를 합치면 406개 파일, 101,648줄입니다. 그 안의 TypeScript 테스트는 tests·__tests__ 경로나 .test. 파일명 기준 117개 파일, 31,276줄입니다. 줄 수는 구현의 복잡도나 품질 점수가 아니라, 읽기 범위를 가늠하기 위한 대략적인 규모입니다.

같은 기준으로 Plugin의 TypeScript·TSX는 테스트 포함 51,555줄, Android는 32,552줄입니다. 처음부터 화면 전체를 읽기보다, 요청 하나를 여러 계층에 걸쳐 추적하는 편이 낫습니다.

6학습 포인트 (기술별)

라이브러리 이름을 외우는 대신, 실제 실패 상황에서 설계 의도를 찾는다

이 레포에서 가장 좋은 공부 방법은 정상 요청 하나와 실패 요청 하나를 같은 경로로 따라가는 것입니다. 예를 들어 “세션 목록이 보인다”와 “세션 목록 응답 전에 연결이 끊긴다”를 함께 그려 보세요. 오류 처리가 어느 클래스의 책임인지 선명해집니다.

6-1. TypeScript와 Zod: 컴파일 뒤에도 입력은 검증해야 한다

TypeScript의 타입은 개발 중 실수를 줄여 주지만 네트워크에서 도착한 JSON이 그 타입이라고 보장하지 않습니다. packages/protocol/src/index.ts의 parseControlFrame()은 바깥 메시지 형태를 검사한 뒤, controlFramePayloadSchemas에서 종류별 본문 스키마를 선택합니다.

// 원본 parseControlFrame()의 핵심
const frame = controlFrameSchema.parse(input)
const payloadSchema = controlFramePayloadSchemas[frame.type]
if (payloadSchema) {
  return { ...frame, payload: payloadSchema.parse(frame.payload) }
}
return frame

실습 아이디어: relayPayloadSchema를 읽고 counter가 음수·소수·문자열인 입력을 각각 종이에 써 보세요. 어떤 조건이 거절하는지 표시하면 “타입이 숫자”와 “프로토콜에서 유효한 숫자”의 차이를 이해할 수 있습니다.

6-2. Promise와 AbortSignal: 완료 경로를 빠짐없이 닫는다

RemoteClientCore는 성공, RPC 오류, 타임아웃, 취소, 전송 실패, 연결 종료를 처리합니다. 등록된 요청의 완료·실패 경로가 takePending()를 거쳐 정리되도록 만든 점을 보세요. “응답이 오면 resolve한다”만 구현하면 나머지 경로의 타이머·리스너·Map 항목이 남을 수 있습니다.

실습 아이디어: packages/client-core/tests/client-core.test.ts에서 LoopbackTransport를 읽고, 실제 네트워크 없이도 연결 종료를 표현하는 drop()을 찾아보세요. 이는 테스트가 외부 서비스 대신 작은 인터페이스를 대체하는 방법입니다.

용어
수명 관리
요청·연결·화면·프로세스가 언제 시작되고 언제 끝나는지 정하고, 끝날 때 관련 타이머·구독·자원을 정리하는 일입니다. 화면이 닫혔어도 응답은 도착할 수 있으므로, 응답을 받을 자격도 수명과 함께 확인해야 합니다.

6-3. WebSocket와 역압력: 보내기만 빨라서는 안 된다

packages/webrtc/src/websocket-backpressure.ts의 waitForRelayCapacity()는 bufferedAmount가 512 KiB를 초과하면 기다립니다. 10초를 넘기거나 소켓이 닫히면 실패합니다. 소비자가 느릴 때 생산자의 속도를 조절하는 장치입니다.

SerialSend.run()은 Promise 체인으로 송신을 순서대로 실행하고, 대기량을 최대 8 MiB·256건으로 제한합니다. 암호화와 송신 순서를 함께 지켜야 nonce 상태와 실제 전송 순서가 어긋나지 않습니다.

// SerialSend.run()의 원본 핵심
if (this.bytes + bytes > 8 * 1024 * 1024 || this.count >= 256) {
  throw new Error('Encrypted send queue exceeds its limit')
}
this.bytes += bytes; this.count += 1
const task = this.tail.then(send)
this.tail = task.catch(() => undefined)
try { await task } finally { this.bytes -= bytes; this.count -= 1 }

실습 아이디어: 첫 작업이 대기 중일 때 두 번째 작업이 시작되는지, 첫 작업이 실패하면 세 번째 작업은 계속 가능한지 추적해 보세요. packages/webrtc/tests/serial-send.test.ts가 순서와 대기량을 검증하는 이유도 설명할 수 있어야 합니다.

6-4. WebRTC: 후보를 모으는 것과 암호화는 다른 책임이다

packages/webrtc/src/rtc-adapter.ts는 실행 환경의 WebRTC 구현을 추상화하고, packages/webrtc/src/rtc-data-channel.ts는 실제 DataChannel과 ICE 협상을 다룹니다. packages/webrtc/src/adaptive-transport.ts는 그 결과를 업무 전송 경로로 선택합니다.

ICE는 연결 가능한 후보를 찾는 절차이고, STUN은 외부에서 보이는 주소를 찾는 데 도움을 주며, TURN은 직접 통신이 어려울 때 중계합니다. 이 기본 구분은 MDN의 WebRTC 프로토콜 설명에서 확인할 수 있습니다.

실습 아이디어: tryWebRtc()에서 ICE 서버 조회가 실패했을 때의 코드를 읽어 보세요. 빈 ICE 서버 목록으로도 직접 연결 후보를 시도하는 흐름과, 최종 협상 실패 후 Relay를 선택하는 흐름을 두 단계로 나눠 그립니다.

6-5. 암호화: 원시 함수와 실제 보안 채널을 구별한다

packages/crypto/src/index.ts에는 deriveSharedKey()와 encryptFrame()도 있습니다. 하지만 현재 Desktop 원격 연결 경로의 핵심은 packages/crypto/src/noise.ts의 NoiseIkSession입니다. 폴더에서 함수 하나를 찾았다는 이유로 그 함수가 현재 통신 전체를 구현한다고 판단하면 안 됩니다.

실습 아이디어: packages/crypto/tests/noise.test.ts와 fixtures/crypto/v1/noise-ik.json을 비교해 보세요. 고정 입력과 기대 출력이 왜 다른 구현끼리의 호환성을 확인하는 데 유용한지 적습니다. 학습용 고정 난수와 실제 실행용 난수의 목적도 구분해야 합니다.

6-6. React Native와 상태 저장소: 늦은 결과를 거절한다

apps/android/src/state/account-actions.ts의 createAccountActions()는 accountGeneration을 증가시키고, captureAccount()로 현재 계정·설정·identity가 여전히 같은지 확인합니다. exclusive()는 자격 증명 관련 쓰기를 Promise 체인으로 순서화합니다.

사용자가 A 서버 로그인 도중 B 서버로 전환한 뒤 A의 응답이 도착할 수 있습니다. 이때 단순히 “가장 늦게 도착한 값을 저장”하면 새 서버의 상태를 덮어씁니다. 요청을 시작한 세대와 현재 세대가 같은지 확인하는 방식으로 그런 결과를 걸러낼 수 있습니다.

실습 아이디어: A 로그인 시작 → B 서버 선택 → B 로그인 완료 → A 응답 도착의 시간표를 만드세요. 각 단계의 세대 번호를 쓰고, 어떤 결과가 보관소와 화면 상태를 바꿀 수 있는지 표시합니다.

6-7. 파일 보관과 CI: 운영 가능한 작은 시스템을 읽는다

apps/server/src/store.ts의 Store.save()는 임시 파일을 쓰고 renameSync()로 교체합니다. 폴더 생성은 0o700, 파일 생성은 0o600을 지정합니다. 장기 보관되는 토큰은 원문 대신 SHA-256 다이제스트로 색인합니다.

파일 교체 방식이 곧 다중 프로세스 데이터베이스의 동시성 보장을 뜻하지는 않습니다. 이 구현은 단일 프로세스를 전제로 하며, 동기 파일 쓰기와 상태 크기도 운영 특성에 영향을 줍니다. 실습 아이디어: 두 프로세스가 같은 파일을 따로 읽고 저장한다면 어떤 갱신이 사라질 수 있는지 그립니다.

CI의 전체 빌드에는 --workspace-concurrency=1이 지정되어 있습니다. 워크플로 주석은 Android 사전 빌드가 공유 패키지를 다시 컴파일하므로, Plugin 번들이 작성 중인 dist를 읽지 않도록 직렬화한다고 설명합니다. 빌드 도구의 빠른 병렬 실행도 산출물 간 의존성을 무시할 수 없다는 사례입니다.

7하드웨어 / 시스템 요구사항

Host, 중계 서버, 접속 기기, 개발용 PC의 비용을 나눠 본다

원격 접속 프로그램의 자원 요구량과, 그 뒤에서 실행되는 AI 모델의 자원 요구량은 별개입니다. 이 저장소 자체가 모델 가중치를 제공하거나 GPU 추론 서버를 구현하는 것은 아닙니다. Host에서 사용하는 모델 제공자·도구·프로젝트에 따라 추가 자원이 필요합니다.

역할확인된 요구·설정출처와 해석
Harness HostRemote 플러그인이 연결되는 Harness 환경README의 현재 기준은 dsh-v0.2.1-rc.1이며 0.2.0 rc.2/rc.1 호환을 설명합니다.
Node 개발 환경Node.js 22 · pnpm 9.15.4서버 README·CI·Dockerfile 기준. 주 릴리스 작업만 Node 24를 사용합니다.
자체 호스팅 서버기본 포트 8080 · JSON 상태 파일DB·Redis 서비스는 Compose에 없습니다. 영속 볼륨 server-data가 구성되어 있습니다.
Android 기기minSdkVersion: 26apps/android/app.json의 네이티브 빌드 설정입니다. Android 8.0 이상에 해당합니다.
Android 개발Android SDK·네이티브 빌드 도구 · CI의 Java 17WebRTC 네이티브 모듈 때문에 Expo Go로 실행할 수 없다고 문서에 명시합니다.
VS Codeengines.vscode: ^1.96.0확장 package.json의 엔진 조건입니다. 모든 기능의 실기기 검증을 의미하지 않습니다.
브라우저 확장Chrome/Edge의 Manifest V3현재 manifest의 Host 권한은 지정된 서비스 출처로 한정됩니다.
GPU·최소 RAM정량 최소값 미명시서버·Android README는 CPU·RAM의 정량 최소 사양을 제시하지 않습니다.

7-1. 네트워크는 Host와 Server를 구별한다

README의 “공개 수신 포트가 필요 없다”는 설명은 Harness Host에 대한 것입니다. Client와 Host가 외부로 연결하더라도 중계 Server 자체에는 접근 가능한 주소가 있어야 합니다. 자체 호스팅 시에는 Server의 공개 경로와 Host의 내부 작업 환경을 나눠 설계해야 합니다.

apps/server/compose.yaml의 기본 호스트 바인딩은 127.0.0.1이고, 컨테이너 안의 DSH_SERVER_HOST는 0.0.0.0입니다. 이 둘은 모순이 아닙니다. 앞의 값은 호스트에서 공개할 인터페이스, 뒤의 값은 컨테이너 내부에서 수신할 인터페이스입니다.

서버 README는 공개 서비스 앞에 HTTPS 역방향 프록시를 두고, /ws/v1/connect의 WebSocket Upgrade와 75초보다 긴 유휴 시간을 지원하도록 안내합니다. 실제 Gateway의 ping 주기는 25초, 응답이 없을 때의 종료 기준은 75초입니다.

7-2. 코드 상한은 성능 보장 수치가 아니다

제한코드 값찾을 곳
제어 프레임 / Relay 프레임64 KiB / 1 MiBpackages/protocol/src/index.ts
보안 메시지 재조립메시지당 4 MiB · 동시 8개SecureMessageCodec
전송 대기열8 MiB · 256건SerialSend.run()
Harness 원격 스트림연결당 16개 상한MAX_ALPHA_STREAMS_PER_CONNECTION
자체 호스팅 연결 수WebSocket·논리 링크 각각 256 상한Gateway의 수락·연결 생성 검사
CodeX / ACP 터미널세션당 8개 · 컨트롤러의 전체 상한 256개packages/plugin/src/remote-terminal-controller.ts
터미널 화면 복원스크롤백 1,000줄 · 스냅샷 256 KiB같은 파일의 SCROLLBACK·MAX_SNAPSHOT_BYTES

256개를 허용하는 검사문이 있다고 256개 접속에서 일정한 지연 시간을 보장하는 것은 아닙니다. 현재의 하드 상한, 안정적으로 처리 가능한 부하, 실제 운영 용량은 서로 다른 수치입니다. 특히 큰 대화 이력과 파일 전송은 짧은 텍스트 프롬프트보다 메모리·중계 대역폭을 더 사용합니다.

시스템 권한
암호화된 원격 터미널도 Host 사용자 권한으로 실행됩니다

packages/plugin/src/config.ts에서 terminal.enabled는 기본 활성화이고, loopback.ports는 기본 빈 목록입니다. README는 Linux 설치기가 systemd 시스템 서비스를 구성하며 root 권한으로 실행할 수 있음을 명시합니다. 연결 암호화는 잘못 부여한 운영체제 권한을 줄여 주지 않습니다.

실제 도입 전에는 Host 실행 계정, 터미널 허용 여부와 포트 목록을 먼저 검토해야 합니다.

8직접 해볼 수 있는 실습 과제

설치나 외부 접속 없이, 소스와 종이만으로 해보는 다섯 과제

아래 과제는 읽기와 계산으로 완성할 수 있습니다. 저장소 코드나 설치기를 실행할 필요가 없습니다. 각 과제의 목표는 기능 이름을 설명하는 데서 끝나지 않고, 실패할 때 어느 상태가 바뀌는지 보여 주는 것입니다.

과제 1 · 입문

계층별로 요청 한 장을 배달하기

읽을 파일: packages/client-core/src/index.ts, packages/webrtc/src/transport.ts, packages/plugin/src/client-secure-transport.ts. 모두 공유 라이브러리와 플러그인을 담은 packages/ 아래입니다.

할 일: “세션 목록 요청” 카드에 요청 ID를 적고, RemoteClientCore → 보안 래퍼 → 전송 → Host 순서로 놓습니다. 각 단계에서 다루는 것이 업무 객체인지, JSON 바이트인지, 암호문인지 표시합니다.

완료 조건: Server의 Relay가 왜 세션 목록의 내부 내용을 알 필요가 없는지 설명하고, 응답을 원래 Promise에 연결하는 ID는 어느 계층이 쓰는지 구별합니다.

과제 2 · 입문

같은 버전이라는 말을 세 가지로 나누기

읽을 파일: 루트 package.json, apps/android/package.json, apps/server/package.json, pnpm-lock.yaml.

할 일: 프로젝트 릴리스 버전, 의존성 허용 범위, 잠금 버전을 각각 다른 열로 적습니다. Zod의 ^3.24.1과 루트 잠금 버전 3.25.76, Android React 19.2.3과 서버 Web React ^18.3.1을 예로 사용합니다.

완료 조건: “같은 저장소니까 React 버전이 모두 같다”가 왜 틀린지, --frozen-lockfile을 CI에서 쓰는 목적이 무엇인지 자기 말로 적습니다.

과제 3 · 중급

한 번만 끝나는 RPC 시간표 만들기

읽을 파일: packages/client-core/src/index.ts와 packages/client-core/tests/client-core.test.ts.

할 일: 요청 A·B를 시작하고 B 응답, A 취소, A의 늦은 응답, 연결 종료 순서로 진행합니다. 단계마다 pending의 키, 살아 있는 타이머, 호출자가 받은 결과를 표로 기록합니다.

완료 조건: A의 늦은 응답이 이미 취소된 Promise를 바꾸지 않는 이유를 takePending()의 반환값으로 설명합니다. 전송 Promise가 끝나지 않아도 제한 시간이 작동하는 이유까지 설명하면 충분합니다.

과제 4 · 중급

메시지 분할과 대기열의 경계 계산하기

읽을 파일: packages/protocol/src/index.ts의 SecureMessageCodec와 packages/webrtc/src/serial-send.ts.

할 일: 120 KiB 메시지를 48 KiB 기준으로 나눕니다. 각 조각의 본문 크기, 17바이트 헤더를 더한 크기, 마지막 조각의 크기를 계산합니다. 이어 3 MiB 메시지 두 건이 송신 중이거나 대기 중일 때 3 MiB 메시지를 추가하면 어떤 검사가 실패하는지 찾습니다.

완료 조건: 본문은 48·48·24 KiB의 세 조각입니다. 조각 1보다 2가 먼저 도착했을 때 왜 거절하는지, 4 MiB 재조립 상한과 8 MiB 송신 대기열 상한이 왜 서로 다른지도 적습니다.

과제 5 · 고급

경로 협상과 권한 검사를 합친 실패 지도 만들기

읽을 파일: packages/webrtc/src/adaptive-transport.ts, apps/server/src/gateway.ts, packages/plugin/src/connection-controller.ts, packages/plugin/src/harness-remote-bridge.ts.

할 일: “Server가 Relay만 지원”, “WebRTC 협상 실패”, “활성 WebRTC 종료”, “상대 공개키 불일치”, “Remote 자신의 설정 수정”의 다섯 상황을 그립니다. 각 상황을 경로 선택, 재연결, 신원 거절, 업무 권한 거절 중 어디에서 처리하는지 표시합니다.

완료 조건: 같은 계정이라도 키가 다르면 연결이 거절되고, 올바른 키라도 금지된 API는 호출할 수 없음을 함수명으로 설명합니다. 자체 호스팅 Relay만으로 P2P 실험 결과를 주장할 수 없는 이유도 포함합니다.

자기 점검
실습 결과는 정답 문장보다 근거 경로로 평가합니다

“연결이 안전하다” 대신 “어느 함수가 어느 입력을 검사하고, 실패하면 어떤 자원을 정리하는가”를 적으세요. 한 장의 상태 전이 표와 정확한 파일·함수 이름이면, 긴 기능 목록보다 훨씬 좋은 학습 결과가 됩니다.

9관련 기술 심화 학습 로드맵

작은 요청 처리기에서 다중 기기 시스템까지 여섯 주에 걸쳐 넓힌다

주차는 권장 순서이며, 정해진 학습 시간이나 숙련 속도를 보장하는 계획은 아닙니다. JavaScript의 함수·배열·객체를 읽을 수 있다면 1주차부터, Promise와 타입을 이미 이해한다면 2주차부터 시작할 수 있습니다.

주차집중할 개념읽을 소스남길 학습 결과
1주차TypeScript 인터페이스·유니언·제네릭, Promise·Map·Setpackages/webrtc/src/transport.ts, packages/client-core/src/index.ts요청 A·B와 응답의 연결 표, 생소한 문법 10개의 설명
2주차JSON 계약·런타임 검증·버전 협상packages/protocol/src/index.ts의 Control 스키마·인코더잘못된 입력 5개와 각각 실패하는 스키마 조건
3주차키 고정·Noise IK·인증 암호화·문맥 결합packages/crypto/src/noise.ts, packages/plugin/src/client-secure-transport.ts공개키·개인키·연결 키를 구별한 두 메시지 핸드셰이크 그림
4주차제어면·데이터면·ICE·Relay·역압력packages/webrtc/src/adaptive-transport.ts, packages/webrtc/src/serial-send.ts, 서버 apps/server/src/gateway.ts협상 실패와 활성 경로 장애를 나눈 상태 전이도
5주차모바일 비동기 상태·보관소·로그인 세대apps/android/src/state/account-actions.ts, apps/android/src/services/login/, apps/android/src/services/storage.ts서버 전환 중 늦은 응답의 처리 시간표
6주차Host 권한·프로세스·자체 호스팅·배포 계약packages/plugin/src/harness-remote-bridge.ts, packages/plugin/src/acp/method-policy.ts, apps/server/Dockerfile·.github/workflows/ci.yml허용 API와 거절 API 표, 소스·설계·검증 기록의 차이 목록

학습 순서는 화면의 화려함보다, 데이터의 형태와 요청의 수명을 먼저 이해하는 쪽으로 잡는 것이 좋습니다. 화면부터 읽으면 접속·승인·재연결 상태가 한꺼번에 나타납니다. 바닥의 인터페이스와 정리 규칙을 알고 돌아오면 같은 UI 코드가 훨씬 단순해집니다.

9-1. 다음 단계로 넘어가는 기준

암호 알고리즘을 새로 설계하거나 운영 서비스를 재현하는 일은 이 로드맵의 선행 조건이 아닙니다. 먼저 이미 있는 코드에서 실패 경계와 자원 상한을 읽을 수 있는 수준을 목표로 삼습니다.

10핵심 키워드 사전

네트워크·암호화·에이전트 용어를 이 저장소의 맥락으로 정리한다
용어쉬운 설명
Harness모델과 도구, 대화, 작업공간을 연결하는 실행 환경입니다. 여기서는 DeepSeek Harness 제품을 가리킵니다.
Host / Client실제 작업이 있는 기기 쪽과, 그 작업에 접속하는 기기 쪽입니다.
플러그인기존 프로그램의 확장 지점에 붙는 코드입니다. 이 프로젝트의 Host는 Harness를 통째로 대체하지 않고 확장합니다.
RPC다른 프로세스·기기에 함수 호출처럼 요청을 보내고 결과를 받는 방식입니다. 네트워크 실패를 일반 함수보다 더 신경 써야 합니다.
이벤트 / 스트림응답 하나로 끝나지 않고 상태 변화나 생성 결과가 계속 도착하는 흐름입니다.
요청 상관관계응답의 requestId로 어떤 요청에 대한 답인지 찾는 일입니다. 응답 순서에 의존하지 않게 합니다.
Control plane로그인, 기기 선택, 연결 ID와 경로 협상처럼 통신을 준비하고 관리하는 제어면입니다.
Data plane프롬프트·응답·파일 등의 실제 업무 데이터를 운반하는 데이터면입니다.
WebSocket서버와 연결을 유지하며 양방향 메시지를 주고받는 통신 방식입니다. 이 프로젝트의 제어와 Relay에 쓰입니다.
WebRTC DataChannelWebRTC로 바이트 데이터를 보내는 통로입니다. 영상 통화만을 위한 기능은 아닙니다.
P2P / LAN기기 간 통신 / 같은 로컬 네트워크를 뜻합니다. 이 프로젝트는 LAN과 인터넷 직접 경로를 구별해 표시합니다.
NAT / ICE공유기의 주소 변환 / 실제로 연결 가능한 후보를 찾아 시험하는 절차입니다.
STUN / TURN외부에서 보이는 주소를 찾는 데 도움을 주는 서버 / 직접 연결 대신 데이터를 중계하는 서버입니다.
Relay중간 서버가 데이터를 전달하는 방식입니다. 이 레포의 WebSocket Relay와 WebRTC의 TURN 경로는 구별됩니다.
E2EE양 끝 기기에서 업무 내용을 암호화·복호화합니다. 중간 경로가 내용을 읽지 못해도 연결 시간과 크기는 보일 수 있습니다.
Noise IK미리 알고 있는 상대 정적 공개키를 사용하는 Noise 핸드셰이크 패턴입니다. 이 구현은 고정된 암호 모음만 허용합니다.
X25519상대와 공유 비밀을 만드는 데 사용하는 키 교환 방식입니다. 공개키와 개인키를 구분해야 합니다.
ChaCha20-Poly1305내용을 숨기는 암호화와 변조 검사를 함께 제공하는 인증 암호화 방식입니다.
HKDF / SHA-256비밀에서 용도별 키를 유도하는 방법 / 데이터를 정해진 길이의 해시로 바꾸는 함수입니다.
Identity pinning신뢰한 기기의 공개키를 기억해, 같은 기기 ID가 다른 키로 바뀌면 거절하는 방식입니다.
Prologue핸드셰이크에 묶는 공통 문맥입니다. 여기서는 연결 ID와 두 기기의 ID를 포함합니다.
Nonce / 재전송 공격암호화에서 반복 사용을 막아야 하는 값 / 이전 메시지를 다시 보내 동작을 유도하려는 시도입니다.
Capability상대가 제공한다고 알리는 기능 표지입니다. 구현 지원과 실제 백엔드 준비 상태까지 항상 같은 뜻은 아닙니다.
Allowlist허용한 API만 통과시키는 목록입니다. 로그인한 상대도 이 목록 밖의 기능은 쓸 수 없습니다.
Fail closed신원·형식·순서가 불확실하면 계속 허용하지 않고 거절하거나 연결을 닫는 원칙입니다.
Backpressure받는 쪽이 느릴 때 보내는 쪽도 기다리게 하여 대기열이 무한히 커지지 않게 하는 역압력입니다.
AbortSignal진행 중인 비동기 처리를 그만 기다리거나 취소하도록 알리는 신호입니다. 실제 작업 취소 여부는 구현에 달렸습니다.
ACP / MCP에이전트와 클라이언트의 통신 / 모델·에이전트에 도구와 문맥을 연결하는 프로토콜 계열입니다. 이 레포의 ACP 데이터면을 MCP 서버로 부르면 부정확합니다.
PTY셸이 실제 터미널에 연결된 것처럼 동작하도록 하는 가상 터미널 장치입니다.
Golden vector같은 입력에 같은 결과가 나오는지 비교할 수 있도록 고정한 기준 데이터입니다.
모노레포 / workspace여러 앱과 라이브러리를 한 저장소에서 관리하고 서로 참조하게 하는 구성입니다.
Shallow clone일부 최신 이력만 가져온 복제본입니다. 전체 커밋 수나 과거 기여도 분석에는 충분하지 않습니다.

11참고 링크

제품 설명, 통신 계약, 실제 구현을 함께 대조한다

11-1. 공식 저장소와 커밋 기준 문서

11-2. 본문의 근거가 되는 핵심 소스

11-3. 관련 기술의 공식 학습 자료

11-4. 현재 상태 (2026-10-23 확인)

저장소 메타데이터는 2026-10-23 GitHub 기준입니다. 코드·설정·줄 수의 기준은 커밋 fac5e60입니다. 릴리스 검증 결과의 출처는 아래에 명시한 릴리스 기록입니다.

항목확인한 상태
저장소 / 선정liguobao/ds-harness-remote · TrendShift Daily #16
스타 / 포크699 / 70 — 2026-10-23 GitHub 기준
생성일 / 최근 push2026-08-14 / 2026-10-23 — 2026-10-23 GitHub 기준
주 언어TypeScript — GitHub 메타와 실제 코드 구성 일치
최신 릴리스v0.5.18 · 2026-10-23 — 2026-10-23 GitHub 기준. 패키지 버전과 변경 기록도 0.5.18
최근 커밋fac5e60 · 2026-10-23 13:43:47 +08:00 · chore(release): prepare v0.5.18
코드 규모TypeScript 90,647줄 + TSX 11,001줄. 테스트 포함, 배포 번들 48,155줄 제외. 추적 파일은 전체 644개.
라이선스 메타없음 — 2026-10-23 GitHub 기준. 루트 LICENSE 파일 없음.
실제 라이선스 표기루트·Plugin package.json에 MIT 표기. packages/plugin/LICENSE와 apps/vscode/LICENSE에 MIT 본문 존재.
자체 호스팅 범위단일 계정·JSON 보관·Relay·로그인/기기 상태 Web UI. 전체 Remote Web·WebRTC/TURN은 이 서버에 없음.
릴리스 검증 기록.github/release-notes.md 기준 전체 794개 테스트와 빌드 통과. v0.5.18의 신규 실기기·기기 간 검증은 없다고 명시.
수치의 의미코드 규모는 추적 파일의 정적 통계입니다. 테스트 수와 코드 상한은 운영 성능이나 기기 간 호환성의 보장 수치가 아닙니다.
사용 전에 알아둘 한계
지원 표, 구현 존재, 실제 검증은 서로 다른 증거입니다

라이선스: GitHub 메타의 “없음”과 일부 패키지의 MIT 파일이 공존합니다. 저장소 전체의 배포·재사용 조건을 이 글만으로 단정하지 말고, 사용할 경로와 배포물의 실제 조건을 확인해야 합니다.

보안: E2EE는 중계 서버에서 업무 내용을 숨기지만, 연결 시간·기기·전송 크기는 숨기지 않습니다. 암호화 문서는 장기 연결 rekey와 독립 보안 검토를 남은 과제로 적습니다. Host나 Client가 침해되거나 전달받은 Web 코드가 바뀌는 문제까지 암호화만으로 해결되지는 않습니다.

지원 범위: README는 VS Code를 실험 단계, iOS를 공개 배포되지 않은 TestFlight 베타로 설명합니다. 설정에 특정 백엔드 명령이 있어도 설치·실행 준비가 완료됐다는 뜻은 아닙니다. 현재 packages/plugin/src/config.ts에는 Codex·Cursor·Antigravity·Pi·Claude·OpenCode가 등록되며, 오래된 설계 기록의 기본값보다 현재 소스와 capability 응답을 우선 확인해야 합니다.

문서와 라우팅: 자체 호스팅 README는 로그인 후 /app/remote 이동을 안내하고, Web 소스도 그 주소로 화면을 전환합니다. 그러나 apps/server/src/server.ts가 HTML을 반환하는 경로 목록에는 /app/remote가 빠져 있습니다. 따라서 이 서버에 해당 주소를 직접 요청하는 경우 404가 반환되는 코드 경로로 이어집니다.

이력과 성능: 특정 시점의 스타·포크 수만으로 전체 기여도나 성장 속도를 판단할 수는 없습니다. README의 “낮은 지연 시간”과 “안전”이라는 설명도 정량 벤치마크나 독립 보안 감사의 완료를 뜻하지는 않습니다. 지원 설명과 실제 구현의 제한을 함께 살펴봐야 합니다.