콘텐츠로 이동

원격 제어와 모바일

Control Center는 데스크톱 앱이지만, 그것이 추적하는 작업은 자리를 비울 때 멈추지 않습니다. 원격 제어는 remote.usectrl.dev에 있는 컴패니언 앱인 **Remote**를 사용해 휴대폰을 cc_server와 페어링하므로, 그곳에서 플릿을 따라가며 메시지와 티켓을 읽고, 에이전트에 답장하고, 뉴스피드를 분류할 수 있습니다. 서버에 있는 오디오, 전사, 소스 코드, 자격 증명은 결코 서버를 떠나지 않습니다.

데스크톱 앱을 곁에서 거드는, 읽기 위주의 컴패니언이라고 생각하면 됩니다. 데스크톱 클라이언트 전체를 대체하려는 것이 아닙니다.

휴대폰은 WebSocket을 통해 cc_server에 도달합니다. 직접 연결(루프백, LAN, tailnet, wss://)이거나, 봉인된 프레임을 중계하는 시그널링 브로커의 초대 전용 룸을 통하는 방식입니다. 피어 투 피어 데이터 채널은 없습니다.

페어링된 휴대폰이 닿는 서버 표면은 의도적으로 좁게 잘라낸 작은 일부입니다.

  • 읽기: 티켓, 에이전트, 스페이스와 그 메시지, 그리고 내 뉴스피드
  • 답장: 스페이스에 메시지 보내기, 티켓 갱신하거나 할당하기, 글을 읽음 또는 저장됨으로 표시하기
  • 플레이북 실행: 저장된 플랜 템플릿을 매개변수와 함께 인스턴스화합니다. 이것은 플랜을 _제안_할 뿐이며, 운영자가 풀 클라이언트의 Plan Studio에서 승인하기 전까지는 아무것도 실행되거나 비용이 지출되지 않습니다
  • 워크스페이스 전환: 휴대폰 자체의 피커에서

거의 이것이 전부입니다. 휴대폰은 풀 클라이언트보다 권한이 낮은 주체입니다. 프로세스를 띄우거나 LLM 예산을 지출하거나 GitHub에 푸시하거나 워크스페이스를 만들 수 없습니다. 이것이 단지 의도가 아니라 실제로 강제되는 방식은 접근 모델을 참고하세요.

연결은 브로커를 거치는 릴레이를 탑니다. 서버는 wss:// 브로커의 초대 전용 룸 하나에 소유자로 참여하고, 페어링된 모든 클라이언트(데스크톱, 웹, 휴대폰)는 시간 제한이 있는 입장 토큰으로 같은 룸에 참여합니다. 세 가지 요소가 맞물립니다.

요소 역할
Remote(휴대폰 앱) 기본적으로 remote.usectrl.dev에 호스팅되거나 자체 호스팅되는 컴패니언 웹 앱입니다. 브라우저에서 로드되며, 페어링 레코드를 IndexedDB에 보관합니다.
릴레이 브로커 N개가 참여하는 룸을 호스팅하는 무상태 wss:// 릴레이(cc_signaling_server)입니다. 불투명한 프레임만 나릅니다. 모든 페이로드는 휴대폰과 서버 사이에서 종단 간 봉인되므로, 브로커는 앱 데이터나 페어링 비밀을 결코 보지 못하고 전달하는 내용을 해석하지도 않습니다.
cc_server 룸의 소유자입니다. 다른 모든 클라이언트가 사용하는 것과 같은 디스패처를 통해 휴대폰의 호출에 응답합니다.

페어링 오퍼에는 서버로 가는 모든 경로(LAN, tailnet, 공용 wss://, 릴레이)와 신원 지문을 나열한 연결 디스크립터가 담깁니다. 휴대폰의 리졸버는 도달 가능한 최적의 경로를 고르고 첫 연결 시 지문을 고정합니다(TOFU). 릴레이는 직접 경로가 없을 때도 연결성을 보장하며, 트래픽을 읽을 수는 결코 없습니다.

페어링은 앱에서 발급되어 딥 링크로 전달됩니다. 절차는 기기 페어링을 참고하세요. 여기서 중요한 것은 그 구조입니다.

https://<pwa-host>/#<payload>

페이로드는 URL 프래그먼트, 즉 # 뒤에 오며 브라우저가 서버로 결코 보내지 않는 부분에 실리므로, PWA의 HTTPS 호스트는 이를 보지 못합니다. 페이로드에는 휴대폰이 서버에 도달하고 자신이 누구인지 증명하는 데 필요한 모든 것이 담깁니다. 연결 디스크립터(모든 경로와 신원 지문), 새로 발급된 기기 id, 32바이트 사전 공유 키, 만료 스탬프입니다. 휴대폰은 이를 디코딩해 페어링 레코드를 로컬에 저장하고 프래그먼트를 제거하므로, 비밀은 URL에서 사라집니다.

페이로드 형태는 두 가지이며 서로 호환되지 않습니다. 앱 내 페어링 패널에서 발급하는 휴대폰 QR은 위에서 본 v2 PairingPayload이며, Remote PWA 호스트를 가리킵니다. cc_server pair --client-url은 웹 클라이언트 전용으로 만들어진, 더 오래된 v1 링크({s, i, k} — 서버 URL, 기기 id, 키)를 내보냅니다.

두 형태는 수명에서도 다르며, 이는 쉽게 착각하는 부분입니다.

발급 주체 자격 증명 만료
앱 내 페어링 패널(pairing.mint) 항상 30일
명령줄의 cc_server pair 없음 — 자격 증명이 만료되지 않음

페어링된 기기는 인증되지만 신뢰되지 않습니다. 여러 개의 독립적인 게이트가 승인된(또는 유출된) 페어링이 서버의 완전한 제어권이 되는 것을 막습니다.

1. 경계는 페어링이 아니라 멤버십입니다

섹션 제목: “1. 경계는 페어링이 아니라 멤버십입니다”

유효한 페어링 키는 _어느 기기_인지를 증명할 뿐입니다. 무엇을 읽거나 바꿀 수 있는지는 워크스페이스 역할이 결정하며, 이는 호출마다 매번 다시 해석됩니다. 저장소 내용을 노출하는 모든 것에는 저장소별 권한 부여도 함께 적용됩니다. 완벽하게 유효한 키를 들고 있어도 지정한 워크스페이스의 멤버가 아닌 사용자에게 속한 기기는 “Not a member of this workspace”로 거부됩니다.

역할 하한은 연산이 스스로 선언하지 않는 한 연산 종류에서 도출됩니다. 읽기에는 guest, 변경에는 member, 파괴적 연산에는 admin이 필요합니다.

2. 휴대폰은 더 낮은 세션 등급입니다

섹션 제목: “2. 휴대폰은 더 낮은 세션 등급입니다”

역할 검사와는 별개로, 휴대폰 세션은 데스크톱이나 웹 세션보다 낮은 기능을 가집니다. 풀 클라이언트 전용으로 선언된 연산은 그 뒤에 있는 사용자가 아무리 높은 권한을 가졌더라도 핸들러가 실행되기 전에 휴대폰에서는 거부됩니다. 기기 페어링 발급, 목록 조회, 이름 변경, 해지, 터널 연결 상태 읽기와 설정, 서버 백업과 워크스페이스 내보내기/가져오기, 프로세스 감지와 종료, 어댑터 탐침, 서버 설정, SSO 관리, 플릿 제어, 실행 중인 에이전트 인수와 반환이 여기에 해당합니다.

특히 MCP 도구 표면(tools/call)에서는 기본 거부 허용 목록이 휴대폰이 호출할 수 있는 도구를 결정합니다. 목록에 없는 것은 실행되기 전에 거부됩니다. 목록은 읽기와 관찰 위주에, LLM 예산을 쓰지 않고 프로세스를 띄우지 않으며 외부 시스템을 건드리지 않는 소수의 로컬 전용 쓰기(메시지 하나, 티켓 갱신 하나, 글 플래그 하나)를 더한 형태입니다.

예를 들어 다음은 거부됩니다. 에이전트 상담, AI 리뷰 시작, 에이전트 종료, GitHub에 리뷰 게시, 에이전트 채용이나 해고, 워크스페이스 생성.

4. 호출별 워크스페이스 스코프 지정

섹션 제목: “4. 호출별 워크스페이스 스코프 지정”

서버는 “현재 워크스페이스”를 갖지 않습니다. 설계상 무상태입니다. 워크스페이스 범위의 모든 호출은 인자에 대상 workspace_id를 지정해야 하며, 그것이 없거나 서버에 등록되지 않은 워크스페이스를 가리키면 디스패처가 무언가 데이터베이스를 열기 전에 호출을 즉시 거부합니다. 따라서 휴대폰은 다른 클라이언트의 스코프를 물려받거나 그쪽으로 흘러 들어갈 수 없고, 남의 워크스페이스를 지정해도 소용이 없습니다. 게이트 1의 멤버십 검사가 여전히 적용되기 때문입니다. 이것은 제품의 나머지 부분이 강제하는 것과 같은 워크스페이스 격리 불변 조건입니다.

5. 사전 공유 키로 키잉되는 종단 간 봉인 프레임

섹션 제목: “5. 사전 공유 키로 키잉되는 종단 간 봉인 프레임”

휴대폰과 서버 사이의 모든 프레임은 페어링 키로 봉인됩니다. 입장 토큰은 클라이언트를 룸에 들여보낼 뿐이며, 아무것도 복호화하지 않습니다. 브로커는 암호문을 중계할 뿐 키를 결코 보지 못합니다. 해지된 기기는 자신의 자격 증명이 이미 사라진 것을 확인하므로, 다시 연결할 때 닫힌 상태로 실패합니다.

앱에서 발급한 자격 증명은 30일로 시간이 제한되며, 그 이후에는 연결 게이트가 이를 실패 처리하고 휴대폰은 다시 페어링해야 합니다. 해지는 실시간입니다. 서버는 기기 테이블을 지켜보다가 해지된 기기의 열린 세션을 다음 연결을 기다리지 않고 몇 초 안에 끊습니다.

슬라이딩 윈도 제한기는 원격 도구 트래픽을 분당 120회로 제한하며, 변경 계열 호출에는 분당 30회라는 더 빡빡한 하위 한도를 둡니다. 이 예산은 세션이 아니라 주체에 속합니다. 기기 세 대가 하나의 허용량에서 사용량을 가져가므로, 탈취된 클라이언트가 세션을 더 열어 여유를 사들일 수 없고 시끄러운 멤버 하나가 다른 멤버를 굶길 수도 없습니다.

7. 아무것도 알지 못하는 브로커

섹션 제목: “7. 아무것도 알지 못하는 브로커”

릴레이 브로커는 의도적으로 단순합니다. 룸을 호스팅하고 봉인된 프레임을 전달할 뿐이며, 계정도 앱 데이터도 키도 보유하지 않습니다. 룸은 초대 전용이고 일시적입니다. 앱 데이터, 오디오, 전사, 소스, 자격 증명은 모두 서버에 남습니다.

페어링하고 나면 휴대폰은 데스크톱의 알림을 그대로 비추는 실시간 업데이트를 알림 프레임으로 받습니다. 새 에이전트 메시지, 나에게 할당된 티켓, 상태 변경 등입니다. 서버는 그 프레임을 연결된 모든 세션에 브로드캐스트하고 워크스페이스 필터는 클라이언트 쪽에서 적용되므로, 보이는 내용은 현재 보고 있는 워크스페이스로 한정됩니다.

화면 표시 내용
설정 → 나 → 내 기기 (/workspaces/:workspaceId/settings/you/devices) 실시간 상태가 표시되는 페어링된 클라이언트, 그리고 새 클라이언트 페어링(웹 브라우저, 데스크톱 앱, 휴대폰)과 해지

같은 패널에서 휴대폰뿐 아니라 두 번째 브라우저나 데스크톱 앱의 페어링도 발급합니다. 흐름은 동일하고 딥 링크만 다릅니다.