본문 바로가기
ColliePWA

13/Documentation

보안 — 실행 전 필독 사항

Collie가 노출하는 대상, 방어 체계, 기기 페어링, 모든 요청에 사용하는 자격 증명

Collie는 구조상 머신에 대한 원격 셸 접근을 제공합니다. 단 한 번의 Collie API 호출로 실행 중인 터미널 창에 임의의 키 입력을 직접 보낼 수 있습니다. 페어링된 모든 기기는 모든 창(소스 코드, 시크릿, 환경 변수, 에이전트 출력)을 읽고 사용자 권한으로 명령을 실행할 수 있습니다. 1.18.0부터는 읽기를 포함한 모든 API 요청에 페어링된 기기의 토큰이 필요하므로, 관건은 페어링 자체입니다. URL에 접근할 수 있고 유효한 페어링 코드를 가진 사람은 누구나 동일한 접근 권한을 얻습니다. 1.18.0 이전에는 URL에 접근할 수 있는 누구나 모든 창을 읽을 수 있었습니다.

명령 필터링은 이 도구의 목적에 맞지 않으므로 샌드박스나 명령 허용 목록(allow-list)은 존재하지 않습니다. 페어링된 모든 기기를 루트 로그인으로 취급하고, 페어링 코드를 비밀번호로 취급하십시오. 분실한 기기는 collie devices revoke <label> 명령으로 즉시 철회하십시오. 페어링을 통해 위험을 줄일 수는 있지만 완전히 없앨 수는 없으므로, URL을 공용 인터넷에 노출하지 말고 tailnet과 같은 사설 네트워크에 유지하십시오.

디바이스 페어링: 쓰기 자격 증명

1.18.0부터 이는 쓰기 요청뿐만 아니라 읽기를 포함한 모든 요청에 필요한 자격 증명입니다.

# on the host — prints an 8-character code and a QR code, good for 10 minutes
bin/collie pair

페어링은 항상 켜져 있으며 쓰기뿐만 아니라 읽기에도 페어링 토큰이 필요합니다. 기기가 페어링될 때까지 Collie는 모든 스마트폰과 브라우저에 403 device not paired을 반환합니다. 스마트폰에서 Collie를 열기 전에 호스트에서 collie pair을 먼저 실행하십시오.

휴대폰에서 Collie를 열고 Settings → Paired devices로 이동한 다음 기기 라벨과 함께 코드를 입력하거나, 명령어로 출력된 QR 코드를 스캔하여 코드가 이미 입력된 해당 화면을 바로 엽니다. 휴대폰에 반환된 토큰이 저장됩니다. Collie는 해시만 보관하며 토큰은 한 번만 표시됩니다. 프로세스를 다시 시작할 필요는 없습니다. 실행 중인 데몬이 다음 요청 시 페어링 및 철회를 바로 적용합니다.

두 가지 디바이스 게이트는 서로 다른 질문을 다루며, 하나만 사용하거나 둘 다 사용하거나 둘 다 사용하지 않을 수 있습니다.

질문 내용신뢰 대상취소 방법
COLLIE_DEVICE_HEADER이 디바이스가 운영자 목록에 있습니까?정제된 이름을 삽입하는 프록시COLLIE_DEVICE_ALLOWLIST 편집 후 재시작
페어링이 디바이스가 내가 발급한 자격 증명을 보유하고 있습니까?네트워크상의 그 무엇도 신뢰하지 않음collie devices revoke <label> — 즉시 적용

페어링에는 추가 인프라가 필요하지 않습니다. 헤더를 삽입할 프록시가 없는 직접 tailscale serve 설정에 적합합니다.

페어링은 읽기를 포함하여 모든 /api 경로를 제어합니다. 두 경로만 열려 있습니다. 모니터나 로드 밸런서가 요청하는 /api/health 경로와 장치가 토큰을 보유하기 전에 필요한 /api/pair 경로입니다. /api/health 경로는 빌드, 크루 모드, 지난 7일간의 세 가지 카운트(입력 상자가 비어 있는 것으로 인식된 횟수, 인식할 수 없었던 횟수, Collie가 읽을 수 없는 화면을 무시하고 사용자가 직접 전송한 횟수)를 응답합니다. 카운트에는 창, 텍스트, 사용자가 명시되지 않습니다.

디바이스 헤더는 쓰기 작업과 두 가지 읽기 작업을 제어합니다. 워크스페이스 폴더 아래의 모든 파일을 표시할 수 있는 파일 보기와 홈(ADR 0086) 아래의 폴더 이름을 나열하는 New 페이지의 Browse입니다.

bin/collie devices list             # what holds a credential, and when each was last seen
bin/collie devices revoke old-phone # effective immediately, no restart

새로 설치한 상태에서는 페어링된 기기가 없으므로, collie pair을 실행할 때까지 Collie는 어떤 전화기에도 응답하지 않습니다. 마지막 기기의 권한을 취소해도 Collie가 다시 열리지는 않습니다. 이후 모든 기기는 device not paired을 받습니다. 복구하려면 호스트에서 collie pair을 실행하고 전화기를 다시 페어링하십시오. 페어링된 기기가 없는 동안에는 collie doctor 경고가 표시됩니다.

참고. crew 링크와 standby 도어는 자체 자격 증명을 유지합니다. lead는 고정된 상호 TLS와 crew secret을 통해 읽기 요청을 member로 전달하므로, member는 페어링된 자체 기기가 필요하지 않습니다(crew).

코드를 5회 잘못 입력하면 해당 코드는 무효화되며, collie pair을 다시 실행해야 합니다. 또한 bridge는 출발지 주소당 1분에 10회를 초과하는 페어링 시도를 429 및 Retry-After: 60와 함께 거부합니다. 동일한 머신의 front 도어 뒤에 있는 경우, 출발지 주소는 front 도어가 추가한 마지막 X-Forwarded-For 항목입니다. 다른 피어로부터 온 해당 헤더는 무시됩니다. 카운터는 메모리에 유지되며 bridge가 다시 시작되면 초기화됩니다.

CLI 자체 읽기

bridge는 시작할 때마다 상태 폴더의 local-secret에 새로운 무작위 secret을 씁니다. collie doctor 및 collie crew update는 같은 머신에 있는 자체 bridge를 읽기 위해 이 값을 보냅니다. 이 파일은 소유자 전용이므로 사용자 계정만 읽을 수 있으며, 정상 종료 시 삭제됩니다. bridge는 이 secret을 읽기 작업에만 허용하며, 쓰기 작업이나 Files 뷰에는 절대 허용하지 않고, 다른 머신으로부터도 절대 허용하지 않습니다. 루프백에서 오는 secret만 허용하며, X-Forwarded-For 같은 프록시 헤더가 포함된 요청에서는 절대 허용하지 않습니다. COLLIE_HOST을 tailnet 주소에 바인딩하는 crew member는 루프백에 아무것도 없으므로, 이 경우 bridge는 이 머신 자체의 주소에서도 secret을 허용합니다. socat 또는 ssh -L처럼 동일한 머신에 있는 일반 TCP 릴레이는 계속 secret을 전달할 수 있습니다. secret을 안전하게 유지하는 것은 이 파일이므로, 상태 폴더를 소유자 전용으로 유지하십시오.

기기에 만료 시간 설정

페어링된 기기는 권한을 취소할 때까지 토큰을 유지합니다. 이를 제한하려면 페어링할 때 유효 기간을 전달하십시오.

bin/collie pair --expires 30d             # also 12h, 2w; at most 3650d
bin/collie devices set-expiry pixel 90d   # a new lifetime, counted from now
bin/collie devices clear-expiry pixel     # no expiry again

유효 기간은 전화기가 코드를 등록한 시점부터 계산됩니다. --expires을 지정하지 않으면 토큰은 이전과 똑같이 만료되지 않으며, 기존 토큰도 변경되지 않습니다. 만료 후에는 bridge가 device not paired 대신 device expired과 함께 토큰을 거부합니다. 그러면 전화기는 페어링으로 남은 항목 정리 상태가 되고 설정에 다시 페어링을 표시하므로, 새 코드를 받으려면 collie pair을 실행하십시오. collie devices list 및 설정 화면에 각 기기의 만료 시간이 표시됩니다. 만료된 기기는 권한을 취소하거나 set-expiry을 통해 새 만료 시간을 지정할 때까지 목록에 남아 있습니다. 다시 페어링할 때 동일한 이름을 사용할 수 있습니다. 새 페어링은 만료된 항목을 대체하고 이전 토큰의 권한을 취소하며, 감사 로그에 권한 취소가 기록됩니다. 기기에서 아직 사용 중이고 만료되지 않은 토큰이 있는 이름은 거부됩니다. crew deputy의 standby 도어 역시 만료된 토큰을 거부합니다.

여러 인스턴스를 실행하는 호스트에서는 명령어 앞에 COLLIE_INSTANCE=<name>을 붙이고 휴대폰에서 해당 인스턴스 URL을 여십시오 (단일 호스트에서의 다중 Collie 인스턴스).

페어링 해제 시 전화기에서 삭제되는 항목

페어링이 종료되면 전화기는 Collie 아래에 저장했던 항목을 삭제합니다. 설정에서 전화기의 페어링을 해제할 때와 bridge가 device not paired 또는 device expired과 함께 토큰을 거부할 때 이 작업이 수행됩니다.

휴대전화는 토큰, 전송되지 않은 모든 초안, 저장된 창 텍스트 및 herd, 푸시 구독, "프롬프트 없음" 확인 내역, 앱 셸을 제외한 Collie의 캐시를 삭제합니다. 설정은 유지됩니다. 테마, 언어, 고정 항목 및 기타 환경설정에는 세션 텍스트가 포함되지 않습니다. 설정 화면은 기기를 철회하기 전에 확인을 요청합니다.

전화기 자체 정리만 수행됩니다. bridge는 Clear-Site-Data 헤더를 보내지 않습니다. 이 헤더는 사용자의 설정과 오프라인 앱까지 삭제하며, 공유 호스트 이름에서는 그곳의 다른 앱에도 영향을 미치기 때문입니다.

bridge가 페어링된 기기 목록(예: 작성이 완료되지 않은 파일)을 읽을 수 없는 경우, 대신 503 pairing unavailable으로 응답합니다. 그러면 전화기는 토큰과 저장된 모든 항목을 유지하고 다시 시도합니다.

전화기가 유지하는 항목

전화기는 마지막으로 확인한 내용의 작고 제한된 복사본을 유지하므로, bridge에 연결할 수 없을 때 콜드 오픈 상태에서도 이를 표시할 수 있습니다. 아래의 각 항목에는 크기 제한과 유효 기간이 있습니다.

항목위치유효 기간페어링 해제 시
페어링 토큰localStorage페어링 해제, 권한 취소 또는 만료 시까지삭제됨
설정: 테마, 언어, 핀localStorage변경할 때까지유지됨
전송되지 않은 초안localStorage48시간삭제됨
푸시 엔드포인트localStorage변경될 때까지삭제됨
App shellCache Storage다음 빌드까지유지됨
글꼴 및 푸시 제목Cache Storage대체될 때까지삭제됨
Whistle 음성 모델 및 엔진 파일(복사하도록 선택한 경우에만 해당)Cache Storage직접 제거할 때까지삭제됨
마지막으로 확인된 Herd 및 창 텍스트IndexedDB collie-store24시간삭제됨
각 창의 최신 Chat 턴IndexedDB collie-store1일 또는 "이 휴대폰에 대화 보관" 설정삭제됨

마지막 두 행은 기기 내 저장소입니다. 브릿지가 이미 마스킹됨한 텍스트만 보관하며, 창당 최대 256 KiB, 전체 최대 10 MiB까지 저장하고 가장 오래된 항목부터 삭제합니다. 창 텍스트와 Chat 턴은 창에 할당된 256 KiB를 절반씩 나누어 사용하므로 서로를 밀어내지 않습니다. Chat 턴은 Chat에 표시된 그대로 온전하게 보관되며 가공되지 않은 터미널 화면 형태로 보관되지 않습니다. "이 휴대폰에 대화 보관"을 끔으로 설정하면 아무것도 보관하지 않고 기존에 보관된 항목을 삭제합니다. 창에서 비밀번호를 요청하면 휴대폰은 해당 창의 항목을 삭제합니다. 저장소에 있는 데이터는 키 입력이나 응답을 보낼 수 없습니다. 페어링 해제 시 삭제되는 항목은 페어링 해제 시 전화기에서 삭제되는 항목에 나와 있습니다.

휴대폰의 앱 전환기에도 창이 표시될 수 있습니다. Collie를 벗어나면 휴대폰은 최근 앱 목록을 위해 화면 사진을 보관하며, 해당 사진에 창 텍스트가 표시될 수 있습니다. 웹 앱은 이를 막을 수 없습니다. 페이지가 백그라운드로 전환되었다는 신호를 받기 전에 Android가 화면을 캡처했으며(2026-10-08 Pixel에서 테스트됨), iOS 역시 이를 보장하지 않습니다. 이것이 문제가 된다면 다른 사람에게 휴대폰을 건네기 전에 휴대폰을 잠그거나 Collie를 닫으십시오.

위험 모델

주요 보안 경계 및 위험 요인:

  • 사용자 권한으로 실행됩니다. Collie는 ~/.ssh, git push --force, rm -rf, sudo을 포함한 사용자의 모든 접근 권한을 그대로 상속받습니다.
  • 인증은 사람이 아닌 기기를 식별합니다. Tailscale은 기기를 쥐고 있는 사용자가 아니라 하드웨어 엔드포인트를 확인합니다. 비밀번호나 사용자 세션이 없으므로 잠금 해제되었거나 도난당한 휴대폰은 열린 셸을 제공하게 됩니다. 페어링은 기기의 자격 증명(위)에 접근 권한을 연결하므로, 분실한 휴대폰은 즉시 권한을 취소하십시오. 내장된 유휴 잠금은 방치된 화면을 가릴 뿐 실제 보안 경계를 제공하지 않습니다(ADR 0007).
  • 모든 로컬 시스템 사용자가 해당 포트에 접근할 수 있습니다. 표준 터미널 멀티플렉서 소켓(tmux, zellij, herdr)은 파일 시스템 권한을 사용하여 다른 로컬 사용자의 접근을 제한합니다. Collie는 로컬 TCP 포트에서 수신 대기하므로 모든 로컬 UID에 노출됩니다. 읽기에는 페어링 토큰이 필요하므로 토큰이 없는 다른 로컬 사용자는 /api/health 외에는 아무것도 읽을 수 없습니다. 토큰이 경계 역할을 합니다. 브라우저 프로필을 읽을 수 있는 로컬 사용자는 토큰도 읽을 수 있습니다(ARCHITECTURE.md §6).
  • 보류된 권한은 사용자의 탭을 기다립니다. collie hooks install claude --hold-permissions 환경에서 Claude의 권한 hook은 소켓 파일을 통해 bridge에 요청합니다. 사용자 본인만 열 수 있습니다(0700 폴더 내 mode 0600). hook은 연결하기 전에 소켓 소유자, 폴더, 상위 상태 디렉터리를 확인합니다. bridge는 창에 배치된 프로세스에 대해서만 응답합니다. 단독으로 응답하지 않습니다. 탭 동작은 카드에 표시된 내용의 해시를 전달해야 합니다. 화면 카드는 대화 상자가 화면에 없는 요청(휴대전화에 보류된 권한)에는 응답하지 않습니다.
  • Files 뷰는 디스크에서 파일을 읽습니다. .git 폴더, Collie 자체 상태 및 설정 폴더, Collie 상태 시크릿과 유사한 이름의 파일을 제외하고 작업 공간 폴더 아래의 모든 파일을 표시합니다. 따라서 쓰기와 마찬가지로 승인된 기기를 요청합니다(ADR 0083).
  • New 페이지의 Browse는 홈 아래의 폴더 이름을 나열합니다. 이름만 표시하며 Files 뷰의 제한 사항이 적용됩니다. 직접 입력하지 않는 한 점으로 시작하는 폴더 제외, .git 제외, Collie 상태 또는 설정 폴더 제외, 홈 외부로 연결되는 링크 제외, 최대 200행. 인증된 디바이스도 필요합니다(즐겨찾기 및 최근 폴더).
  • 페어링된 모든 기기는 작업 공간 폴더 아래의 파일을 읽을 수 있습니다. 여기에는 .env 파일이 포함됩니다. 셸 자체를 맡길 수 있는 신뢰하는 기기만 페어링하십시오.
  • 작업 공간 폴더 아래의 자격 증명 파일은 읽을 수 있습니다. ~/.claude, ~/.codex, ~/.config/gh 또는 ~/.ssh에서 연 작업 공간은 해당 위치에 있는 내용을 표시합니다. 설정 폴더가 작업 공간 아래에 있는 두 번째 Collie의 .env도 마찬가지입니다. 폴더 외부 파일을 가리키는 폴더 내부의 하드 링크 역시 감지되지 않습니다.
  • 페어링된 기기는 실행기를 추가할 수 있습니다. 기본 [phone] adds = true 설정을 사용하면, 휴대전화는 레시피를 기반으로 구성된 행을 추가할 수 있습니다. 레시피는 에이전트와 옵션 칩으로 이루어지며, 옵션 칩의 플래그는 브리지가 자체 테이블에서 선택합니다. 직접 입력한 줄에는 free_text = true 설정이 필요하며, 이는 기본적으로 꺼져 있습니다. 레시피는 여전히 권한 프롬프트를 건너뛸 수 있습니다. 이러한 행에는 "프롬프트 없음" 배지가 표시되며, 각 기기에서 한 번씩 확인합니다. 기기를 철회하면 해당 기기가 추가한 행이 제거됩니다. 휴대전화에서 추가한 모든 행을 끄려면 adds = false을(를) 설정하십시오(사용자 정의 런처, ADR 0094).
  • 페어링된 기기는 일회성 명령을 실행할 수 있습니다. 기본 [phone] run = true 설정을 사용하면, 휴대전화가 작성한 한 줄을 머신의 새 셸에 입력할 수 있습니다. 페어링된 휴대전화는 이미 셸을 열고 내용을 입력할 수 있으므로, 이 기능이 새로운 권한을 추가하지는 않습니다. 입력 줄은 자유 줄 문자 규칙을 따르며, 페어링된 기기가 없으면 브리지가 이를 거부합니다. 시작된 각 명령은 상태 폴더 내의 commands-recent.json에 최대 12개까지 보관됩니다(모드 0600). 비밀 정보가 포함된 것으로 보이는 줄(TOKEN=…과 같은 할당, 비밀번호가 포함된 URL, 또는 token나 password와 같은 단어)은 실행되지만 보관되지는 않습니다. 이 검사는 추정에 기반하므로 비밀 정보가 목록에 유입될 수 있습니다. 감사 로그에는 해당 줄의 첫 번째 단어와 길이만 기록됩니다. 다시 한 줄을 실행하려면 페어링된 기기가 필요하므로, 기기의 페어링을 해제하거나 철회해도 목록은 그대로 유지됩니다. 바로가기를 제거하려면 run = false을(를) 설정하십시오(일회성 명령, ADR 0095).
  • 각 머신은 사용자가 Collie를 통해 보낸 쉘 줄을 보관합니다. Sent는 쉘 창으로 보낸 한 줄과 각 일회성 명령을 상태 폴더의 sent.jsonl에 보관합니다(모드 0600). 페어링된 디바이스는 창을 읽을 수 있는 것처럼 이 파일도 읽을 수 있습니다. 일부 비밀은 여전히 유지됩니다(Sent: 보낸 쉘 줄).
  • 단일 인스턴스가 모든 Herdr 세션을 노출합니다. 기본적으로 단일 Collie 프로세스는 샌드박스 세션(다중 세션)을 포함하여 Herdr의 설정 루트 아래에서 감지된 모든 Herdr 세션의 전면에서 동작합니다.
  • 쓰기 작업은 <state-dir>/audit.log에 기록됩니다. 경로입니다. COLLIE_STATE_DIR 변수로 변경하지 않는 한 ~/.local/state/collie/audit.log 경로를 사용합니다. 서버는 수신되는 모든 키 입력, 응답, 파일 업로드, 창/탭 수명 주기 이벤트를 기록합니다. 감사 로그는 접근 제어가 아닌 사후 가시성을 제공한다는 점에 유의하십시오. Type 모드에서 전송된 문자는 로그에 절대 기록되지 않습니다. COLLIE_AUDIT_CONTENT=none이 각 문자를 마스킹하며, 기본 미리보기에는 • 표시의 개수만 기록됩니다. Enter 및 Ctrl+C와 같은 특수 키는 식별 가능한 상태로 유지됩니다(ARCHITECTURE.md §6). durable agent의 응답, 전송, 중지, 버튼 탭, 시작, 재개방, 이름 변경도 기록됩니다(로그에 기록되는 내용).
  • 응답 헤더. 모든 응답에는 X-Content-Type-Options: nosniff, Referrer-Policy: no-referrer, Permissions-Policy: camera=(), geolocation=(), microphone=(self), payment=(), usb=()이(가) 포함됩니다. 핸즈프리 음성 설정을 위해 마이크는 계속 허용 상태로 유지됩니다. App shell의 Content-Security-Policy에는 object-src 'none', form-action 'self', frame-ancestors 'none'이(가) 포함됩니다. Strict-Transport-Security은(는) TLS 수신 대기 장치 또는 프록시의 X-Forwarded-Proto: https을(를) 통해 HTTPS로 요청이 도달한 경우에만 전송됩니다. Collie는 다른 서비스와 상위 도메인을 공유하는 경우가 많으므로 하위 도메인이 아닌 응답한 호스트 이름만 포함합니다. 브릿지 자체는 tailscale serve 뒤에서 일반 HTTP를 사용하며 브라우저는 여기서 해당 헤더를 무시합니다. /api/blobs/에서 제공되는 이미지는 세션 콘텐츠이므로 Cache-Control: private, no-store을(를) 포함하며 휴대폰 자체 캐시를 포함해 어떤 캐시에도 저장되지 않습니다.
  • 기본 방어 제어. Collie는 엄격하게 루프백 인터페이스에만 바인딩되며, 트래픽을 tailscale serve 또는 이에 상응하는 역방향 프록시를 통해서만 라우팅하고, 엄격한 CSP 규칙, 동일 출처 검사 및 호스트 헤더 유효성 검사를 적용합니다. 창 출력은 innerHTML 대신 React 텍스트 노드로 렌더링됩니다. tailscale funnel을(를) 사용하거나 원시 포트를 외부에 노출하지 마십시오. 특정 하드웨어를 승인하려면 페어링을(를) 직접 사용하거나, 프록시가 기기 ID를 삽입하는 경우 아래 두 개의 COLLIE_DEVICE_* 변수를 사용하십시오.
변수동작 방식
COLLIE_ALLOW_NON_LOOPBACK_BIND=1루프백 전용 바인딩 설정을 해제합니다. 설정하지 않으면 브리지가 0.0.0.0에 바인딩되지 않습니다.
COLLIE_ALLOW_ANY_HOST=1호스트 헤더 유효성 검사를 비활성화합니다. 이 기능은 기본적으로 켜져 있으며 실패 시 차단됩니다.
COLLIE_TRUSTED_USERTailscale-User-Login 헤더가 누락되었거나 일치하지 않는 요청을 거부합니다.
COLLIE_TRUSTED_USER_OPTIONAL=1누락된 Tailscale-User-Login 헤더를 허용합니다(태그된 노드는 헤더를 보내지 않습니다).
COLLIE_ACCESS_TEAM + COLLIE_ACCESS_AUDCloudflare Tunnel 전용입니다. Cf-Access-Jwt-Assertion이(가) 이 Access 앱에 대해 검증되지 않는 한 모든 원격 요청을 거부합니다. 루프백의 로컬 프로세스, /api/health 및 crew 링크만 이를 건너뛰며, 페어링은 여전히 적용됩니다. tailscale serve와(과) 같은 다른 진입점도 거부됩니다(Cloudflare Tunnel).
COLLIE_DEVICE_HEADER프록시가 기기 ID와 함께 삽입하는 헤더의 이름입니다.
COLLIE_DEVICE_ALLOWLIST쓰기가 허용된 기기 ID의 쉼표 구분 목록입니다. 다른 모든 기기는 읽기 전용으로 유지됩니다(docs/deployment.md).
COLLIE_REDACT=off창 텍스트, diff 및 파일 텍스트의 시크릿 마스킹을 끕니다(아래). 기본값은 켜짐입니다.
COLLIE_SENT=off보낸 쉘 줄의 기록을 보관하지 않습니다(아래). 기본값은 켜짐입니다.
🚫 Collie와 함께 tailscale funnel을 절대 사용하지 마십시오. Funnel은 트래픽을 공용 인터넷으로 라우팅하는 반면, tailscale serve은 비공개 tailnet으로 접근을 제한합니다. Collie를 Funnel을 통해 실행하는 사용 사례는 지원되지 않습니다.

Tailscale ACL 및 COLLIE_TRUSTED_USER을(를) 사용하여 접근을 추가로 제한하십시오. 어떠한 보증도 없이 있는 그대로 제공됩니다.

Durable agent

연결하는 durable agent는 직접 실행하거나 배포하는 코드이며, Collie는 해당 텍스트를 신뢰할 수 없는 것으로 취급합니다. 비공개 소켓은 다른 사용자를 차단하지만 사용자 본인의 프로세스는 차단하지 못하며, https 환경에서는 토큰이 유일한 보호 장치입니다. 전송 방식별 신뢰 규칙, Collie가 durable agent의 텍스트에 수행하는 작업, 감사 로그가 보관하는 내용은 Durable agent의 보안에 있습니다.

음성 엔진(Whistle)

Whistle은 Cactus Compute의 WebAssembly 엔진이라는 타사 코드를 실행합니다. 이 코드는 휴대폰에서 실행되거나, 호스트에서 bridge 사용자의 권한으로 실행됩니다. Collie는 해당 코드가 접근할 수 있는 범위를 제한합니다. 접근 범위를 완전히 없앨 수는 없습니다.

  • 벤더링 및 고정 완료. 엔진은 needle.js 및 needle.wasm 두 개의 파일로 구성되며 업스트림 라이선스 및 출처 메모(web/public/stt/needle-3-93e88ede/)와 함께 업스트림에서 배포한 바이트 그대로 유지됩니다. 테스트를 통해 두 파일의 sha256과 wasm이 호스트에 요청할 수 있는 16개 함수의 목록을 고정합니다. 새로운 임포트가 발견되면 작업을 중단해야 합니다. 사람이 직접 diff를 먼저 확인한 후에만 고정값을 업데이트합니다. 모델에는 자체 고정 해시가 있으며, Collie는 해시가 일치할 때만 다운로드된 복사본을 유지합니다.
  • 휴대폰 환경: 자체 정책을 가진 worker. wasm은 전용 worker에서 실행됩니다. bridge는 다음 정책을 적용하여 stt/ 하위의 모든 스크립트를 제공하며, 다른 어떤 페이지에도 이 정책이 적용되지 않습니다.
    Content-Security-Policy: default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; base-uri 'none'

    worker에는 네트워크, DOM, 외부 코드를 로드하는 수단이 없습니다. localStorage에 있는 페어링 토큰을 읽을 수 없으며, worker에는 localStorage이 없습니다. 다른 작업을 수행하기 전에 먼저 작은 wasm 모듈을 컴파일합니다. worker에 자체 정책을 부여하지 않는 브라우저는 이를 거부하며, 해당 환경에서는 이 휴대폰이(가) 사용할 수 없는 상태로 표시됩니다.

  • 페이지 정책은 변경되지 않습니다. document 정책에는 'wasm-unsafe-eval'이 부여되지 않으며, connect-src 'self' 정책을 유지합니다.
  • worker가 계속 접근할 수 있는 항목. 어떤 정책 지시문으로도 차단되지 않는 이 오리진의 IndexedDB 및 Cache Storage를 읽고 쓸 수 있습니다. collie-store에는 최근 창 및 Chat 텍스트(전화기가 유지하는 항목)가 보관됩니다. 이 텍스트는 이미 마스킹되었지만 읽을 수 있는 상태입니다. 이 접근 범위는 허용되며 ADR에 명시되어 있습니다. 이 점이 문제가 되기 시작하면 다음 단계는 worker 주변에 샌드박스 처리된 프레임을 구성하는 것입니다.
  • 호스트 환경: Collie 자체 바이너리의 자식 프로세스. 받아쓰기 1회마다 수명이 짧은 단일 자식 프로세스가 Collie가 작성한 고정된 인수 목록으로 _whistle-transcribe을 실행합니다. 컴파일을 수행하기 전에 엔진, wasm, 모델을 각각의 고정값과 대조하여 확인합니다. 60초의 기한이 있으며 SIGKILL로 종료되고, 256KiB의 출력 제한이 적용되며 한 번에 최대 2개까지만 실행됩니다. bridge의 COLLIE_* 변수는 전혀 전달받지 않습니다. bridge는 직접 확인한 경로를 통해 상태 폴더와 설치 폴더 두 가지만 설정합니다. 녹음 데이터를 제외하고는 요청의 어떤 항목도 인수 목록에 전달되지 않습니다.
  • 사용자 탭을 통한 두 번째 아웃바운드 호출. 업데이트 확인이 첫 번째입니다. 모델 다운로드는 두 번째입니다. 고정된 단일 Hugging Face 주소에서 가져온 단일 파일이며, 해시가 일치한 후에만 기록됩니다. 휴대폰은 Hugging Face를 직접 호출하지 않습니다. 페어링 토큰을 사용하여 호스트에서 모델을 복사합니다. 해당 경로는 쓰기 권한이 있는 기기가 필요하고, 리드에서만 실행되며, 한 번에 하나의 다운로드만 허용하고 감사가 이루어집니다.
  • Settings를 통한 설정은 "설정은 CLI 작업이다"라는 원칙의 예외입니다. 자격 증명을 저장하거나 명령어 이름을 지정하는 공급자는 터미널 내에 유지됩니다. Whistle은 이 중 어느 것도 수행하지 않습니다. 탈취된 쓰기 토큰은 이미 터미널에 입력을 수행하고 업데이트를 시작할 수 있으므로, 설정 탭을 누른다고 해서 새로운 권한이 부여되지는 않습니다.
  • Settings에서 Codex로 전환하는 것은 또 다른 예외입니다. 쓰기 권한이 있는 기기는 호스트를 Whistle, Codex, Off 간에만 전환할 수 있으며 그 외의 작업은 불가능합니다. Codex는 전체 동의, 즉 collie stt setup이 출력하는 동일한 문구와 동의합니다 탭이 필요합니다. 그런 다음 bridge가 CLI 자체 probe를 실행합니다. 탈취된 쓰기 토큰은 호스트의 음성 대상을 작업자의 로그인된 ChatGPT 계정으로 향하게 할 수 있습니다. 키나 명령어를 추가하거나 대체할 수는 없습니다. 토큰은 이미 터미널에 codex을 입력할 수 있습니다. ADR 0097에서 이를 검토합니다. Settings에서 전환하면 페어링 시 지정된 페어링 기기 이름과 시간이 stt.json에 기록되므로 호스트 음성에서 마지막으로 변경한 주체를 확인할 수 있습니다. 토큰이나 주소는 기록되지 않습니다. collie stt setup을 실행하면 해당 줄이 삭제됩니다.
  • 페어링된 기기는 호스트 음성 설정에 지정된 항목을 이름으로만 읽을 수 있습니다. 이는 명령어 이름, 모델 파일 이름, 음성 서비스의 호스트 이름이며, 경로, 인수, 키, URL 경로는 포함되지 않습니다.

이렇게 구축된 이유: ADR 0096. 설정 단계는 음성 입력 및 Web Push에 있습니다.

Walky talky

Walky talky는 에이전트 세션 간에 메시지를 전달하므로 이 섹션에서는 메시지가 무엇을 증명하고 무엇을 증명하지 않는지 설명합니다. 명령어는 Walky talky에 있습니다.

# in .env, then run `collie restart`
COLLIE_WALKY=off

해당 줄은 이 머신이 자신의 창으로 들어오는 모든 메시지를 거부하도록 만듭니다. 머신의 CLI는 여전히 보낼 수 있습니다.

  • crew 키는 머신 간 메시지에 서명합니다. 멤버는 자신의 crew 키로 메시지에 서명합니다. lead는 해당 멤버를 위해 고정한 키와 대조하여 서명을 확인한 다음, 이를 수신할 머신을 위해 자체 서명을 추가합니다. 단독 설치에는 crew 키가 없습니다. 아무것도 서명하지 않으며 머신 외부로 아무것도 보내지 않습니다.
  • 수신자는 lead의 보증을 통해 보낸 사람을 확인합니다. 멤버는 자신의 lead만 고정합니다. 따라서 lead는 메시지와 함께 보낸 사람의 인증서를 보내고, 이 인증서가 해당 크루의 멤버에 속한다는 짧은 진술에 서명합니다. 수신 머신은 해당 진술을 확인한 다음, 인증서와 대조하여 보낸 사람의 서명을 확인합니다. 확인이 하나라도 실패하면 메시지를 거부합니다. 크루에 누가 속해 있는지에 대해서는 여전히 lead를 신뢰합니다.
  • lead만 결과를 다시 쓸 수 있습니다. lead는 멤버에게 해당 멤버 자신의 메시지 결과를 전송합니다. 멤버는 고정된 lead로부터만 결과를 수신하며, 자신이 보냈고 여전히 로그에 기록 중인 메시지에 대해서만 결과를 받습니다. 결과 및 lead가 받은 기록은 메모리에 유지되며 10분 후에 사라집니다.
  • 전화기의 카드에는 로그가 필요합니다. 전화기는 브리지 자체의 로그에 해당 메시지 ID가 있는 경우에만 메시지 카드를 표시합니다. 에이전트가 화면에 출력만 한 헤더는 "no log record on this machine" 표시와 함께 일반 텍스트로 유지됩니다.
  • 창 토큰은 비밀이 아닙니다. #k3f9과 같은 주소의 토큰은 닫힌 창의 ID를 차지한 새 세션에 메시지가 도달하지 않도록 방지합니다. collie walky who는 이를 모든 에이전트에 출력하며, <stateDir>/walky/instances.json은 사용자가 읽을 수 있습니다. 이 토큰은 누가 보냈는지는 전혀 증명하지 않습니다.
  • 메시지가 거쳐 가는 모든 머신이 이를 보관합니다. 보낸 사람의 머신, lead 및 받는 머신은 각각 메시지를 <stateDir>/walky/에 기록합니다. 로그는 7일 동안 유지되며 최대 2,000행까지 보관됩니다. 메시지에 절대 비밀을 넣지 마십시오.
  • 에이전트의 메시지는 피어의 말입니다. 창에는 보낸 사람의 헤더가 포함된 한 줄이 전달되며 본문은 포함되지 않습니다. 에이전트는 본문을 collie walky inbox <id>의 출력으로 읽으며, 하네스는 이를 사용자의 지시가 아닌 데이터로 취급합니다. 설치된 스킬은 에이전트에게 이를 신중히 검토하고 동료가 요청했다는 이유로 파괴적인 명령을 실행하지 않도록 지시합니다.
참고. 에이전트는 사용자의 권한으로 실행되므로 crew 키를 읽고 로컬 소켓을 열 수 있습니다. 에이전트가 메시지에 서명하거나 자신이 소유하지 않은 창을 차지할 수도 있습니다. 서명은 머신 간 이동 중인 메시지를 보호하고 수신자에게 어느 머신이 보냈는지 알려줍니다. 동일한 머신에서 자신에 대해 거짓말을 하는 프로그램으로부터 사용자를 보호하지는 않습니다.

Walky talky는 외부 호출을 추가하지 않습니다. 다른 머신으로 보내는 메시지는 이미 존재하는 crew 링크를 타며, 어떤 모델, 클라우드 서비스 또는 서드파티도 이를 보지 못합니다.

머신 외부로 나가는 항목은 마스킹됩니다

Collie는 창 텍스트와 파일 콘텐츠에 포함된 알려진 시크릿 패턴을 휴대폰에 도달하기 전에 마스킹합니다.

# in your .env, only to turn the mask off; it is on by default
COLLIE_REDACT=off

마스크는 브릿지에서 터미널 미러, Chat 및 History 뷰, 모든 푸시 알림, Changes 뷰의 diff, Files 뷰의 파일 텍스트 등 5개 경로를 통해 실행됩니다. 숨겨진 내용은 너비가 동일한 • 표시로 바뀌므로 열과 줄 수가 유지됩니다. 벤더 접두사는 읽을 수 있는 상태로 유지되므로 sk-o••••을(를) 통해 숨겨진 내용이 무엇인지 계속 확인할 수 있습니다.

신뢰도가 높은 형태만 일치합니다:

형태마스킹 대상 예시
접두사가 있는 API 키sk-…, sk-ant-…, sk-or-v1-…, ghp_…, github_pat_…, xoxb-…, AKIA…, AIza…, glpat-…, npm_…
JWT세 개의 base64url 부분, 첫 부분은 eyJ로 시작
PEM 개인 키BEGIN … PRIVATE KEY와 해당 END 줄 사이의 모든 줄
베어러 토큰Bearer 뒤의 토큰, 20자 이상
이름이 지정된 값password=, secret:, token=, api_key: 및 이와 유사한 항목 뒤의 값, 8자 이상
주의. 이는 완화책일 뿐이며 보증이 아닙니다. 단독으로 있는 일반 비밀번호, 순수 16진수 또는 base64 값, 목록에 없는 접두사를 가진 키는 의도적으로 제외합니다. 이러한 항목까지 포함하는 패턴은 모든 화면의 일반 텍스트와 코드까지 마스킹하게 됩니다.

일반 텍스트 일부도 마스킹됩니다. 예를 들어 token: something 형식의 산문이나 YAML 줄이 그렇습니다. 직접 입력하여 전송하는 내용은 마스킹되지 않으며, 감사 추적은 자체 규칙을 유지합니다(COLLIE_AUDIT_CONTENT).

이 스위치는 브리지에만 COLLIE_REDACT 형태로 존재합니다. Settings → System에서는 "Secret masking"이라는 읽기 전용 항목으로 표시됩니다. 휴대전화에는 의도적으로 스위치를 두지 않았습니다. 휴대전화에 스위치가 있으면 페어링된 휴대전화나 도난당한 휴대전화에서 마스크를 끌 수 있기 때문입니다. 마스킹된 값을 확인하려면 머신에서 직접 확인하십시오. 창에 마스킹된 텍스트가 처음 표시되면 휴대전화에 안내 문구가 기기당 한 번 표시됩니다. 마스킹된 텍스트를 답변에 붙여넣으면 비밀 값 대신 점 기호가 창으로 전달된다는 주의 문구가 입력창 아래에 표시됩니다.

crew 환경에서는 각 멤버가 자체 텍스트를 마스킹하고, 휴대전화로 전송되기 전에 리드가 이를 다시 마스킹합니다. 리드는 멤버의 미러, Chat, History, diff, 파일 텍스트 및 창 제목을 자체 설정과 동일한 마스크로 마스킹하므로, 여전히 1.17.x 버전을 실행 중인 멤버가 휴대전화로 키를 평문 전송할 수 없습니다. 이미 마스킹된 텍스트는 다시 마스킹되어도 그대로 유지됩니다. 텍스트가 마스킹되지 않은 상태로 휴대전화에 도달하려면 리드와 멤버 양쪽에 COLLIE_REDACT=off 설정이 되어 있어야 합니다. 한 가지 특이점: 1.17.x 버전의 멤버는 자체 마스킹을 수행하지 않으므로, 답변 유효성을 확인하는 줄에 비밀 값이 포함되어 있으면 휴대전화에서 "The screen changed before that could be sent" 오류와 함께 해당 답변을 거부할 수 있습니다. 이 경우 해당 멤버가 1.18.0 버전을 실행할 때까지 답변이 대기합니다.

리드가 멤버의 응답을 마스킹하기 위해 읽을 수 없는 경우, 해당 응답을 전달하지 않습니다. 그러면 휴대전화에는 연결할 수 없는 멤버와 마찬가지로 해당 읽기 작업이 실패한 것으로 표시됩니다.

푸시 알림 역시 사용자가 직접 지정한 창 이름만 사용합니다. 창 레이블, Claude의 /rename 이름 또는 단일 창 탭의 이름이 이에 해당합니다. 창 내부 프로그램이 설정한 제목은 절대 사용하지 않습니다. 모든 프로그램이 해당 제목을 설정할 수 있고, 제목에 경로, 명령어, 비밀 값이 포함될 수 있기 때문입니다. 이러한 이름이 없는 창은 harness 이름(예: claude)으로 표시됩니다.

Sent: 보낸 쉘 줄

각 머신은 사용자가 Collie를 통해 보낸 셸 라인의 기록을 유지하므로, 이를 찾아 다시 보낼 수 있습니다.

# in your .env, to keep no record on this machine
COLLIE_SENT=off
질문답변
보관되는 항목셸 창으로 보낸 라인, 그리고 New 페이지의 각 일회성 명령입니다. 각 라인별 보관 항목: 텍스트, 해당 폴더, 시간, 그리고 둘 중 어느 쪽이었는지 여부.
절대 보관되지 않는 항목에이전트에게 보낸 프롬프트, 프로그램용 텍스트, Keys 보드의 키, Type 모드.
위치상태 폴더 내 sent.jsonl, COLLIE_STATE_DIR가 이동하지 않은 경우 ~/.local/state/collie/. 파일 권한은 0600이며, 0700 권한의 폴더에 있습니다.
유효 기간각 머신당 최대 5000개 라인이며, 365일보다 오래된 것은 없습니다.
읽을 수 있는 대상페어링된 기기입니다. 해당 라인이 전송된 창을 읽을 수 있기 때문입니다.
crew 환경에서각 머신은 자체 파일을 유지합니다. lead는 사용자의 전화기가 요청할 때 구성원에게 요청하며, 사본을 보관하지 않습니다.

Collie는 사용자가 탭한 모든 라인을 전송한 다음, 보관할지 결정합니다. 다음 조건에 해당하는 라인은 아무것도 보관하지 않습니다:

  • 공백이나 탭으로 시작하는 라인,
  • TOKEN=abc deploy 또는 export AWS_SECRET_ACCESS_KEY=…과 같이 변수를 설정하는 라인,
  • https://user:pass@host과 같이 비밀번호가 포함된 URL을 담고 있는 라인,
  • password, secret, token, bearer 또는 authorization와 같은 단어를 담고 있는 라인,
  • 시크릿 마스크가 인식하는 형태(ghp_… 또는 sk-… 키 등)를 담고 있는 라인,
  • 4096자를 초과하는 라인.

이러한 라인은 텍스트가 포함된 행, 개수, 로그 라인을 남기지 않습니다.

특정 라인을 제외하려면 공백으로 시작하십시오. 셸은 평소대로 실행하고 Collie는 아무것도 보관하지 않습니다.

주의. 이 검사는 추정치입니다. mysql -pPASS과 같이 일반 인수로 전달하는 비밀번호나, 인식하지 못하는 형태의 모든 시크릿은 보관됩니다. 이러한 라인은 공백으로 시작하십시오.

한 라인을 삭제하려면 Sent 시트의 해당 행에서 Forget을 사용하십시오. 머신 전체를 삭제하려면 Clear Sent를 사용하십시오. 둘 다 COLLIE_SENT=off가 설정된 상태에서도 작동합니다. 감사 로그는 삭제된 각 항목을 횟수로만 기록하며, 라인 자체는 절대 기록하지 않습니다. 모든 라인은 머신을 떠나기 전에 시크릿 마스크를 거칩니다.

이러한 방식으로 구축된 이유: ADR 0107.

Windows에서의 보안 파일

Windows(실험적 기능)에서 Collie는 보안 비밀 파일을 본인 계정, SYSTEM 및 Administrators에만 비공개로 유지합니다. "본인 계정"은 Collie를 실행하는 Windows 계정입니다.

NTFS에는 0600 모드가 없으므로 Collie는 상태 폴더와 설정 폴더의 접근 제어 목록(ACL)을 사용합니다. Collie가 해당 위치에 작성하는 각 파일은 동일한 목록을 받습니다. 사용자 프로필의 기본 폴더는 이미 다른 표준 사용자에게 닫혀 있습니다. 이 검사는 COLLIE_STATE_DIR 또는 다른 설정을 사용하여 폴더를 이동할 때 가장 중요합니다.

시작할 때 브리지는 두 폴더와 보안 비밀 파일을 검사합니다. 다른 계정이 파일을 읽을 수 있는 경우 브리지가 이를 수정하지만, Collie 자체 폴더에서만 수정합니다. 먼저 이전 목록을 상태 폴더의 acl-backups에 저장하고 이를 복원하는 icacls /restore 명령을 출력합니다. 관리자 권한으로 실행된 터미널에서 해당 명령을 실행하십시오. 다른 파일도 포함된 폴더는 검사만 수행되고 변경되지 않습니다. Collie가 사용자가 실행할 icacls 명령을 출력합니다. collie version 등의 다른 명령은 검사하고 경고만 표시합니다.

collie doctor은(는) 결과를 secrets-private(으)로 표시합니다. 다른 계정이 읽을 수 있는 폴더는 오류로 처리되며 해결 방법이 함께 제공됩니다. Collie가 확인할 수 없는 폴더는 경고로 처리됩니다: cannot confirm.

경우결과
폴더를 복사, 복원 또는 동기화함(zip, /SEC 없는 robocopy, OneDrive, 파일 히스토리, USB 드라이브)복사본은 목록을 잃을 수 있습니다. Collie는 다음 시작 시 다시 검사합니다.
바이러스 백신 또는 제어된 폴더 액세스가 변경을 차단함Collie가 폴더를 비공개로 설정할 수 없다고 알리고 해결 방법을 제공합니다.
폴더가 FAT, exFAT 또는 네트워크 공유에 있음Collie가 사용할 수 있는 목록이 없습니다. Collie가 cannot confirm을(를) 표시합니다. 상태 폴더를 이 PC의 NTFS 드라이브로 이동하십시오.
COLLIE_NO_ACL_REPAIR=1Collie는 목록을 변경하지 않습니다. 계속 검사하고 경고를 표시하며, doctor은(는) 계속 보고합니다.
참고. 이는 관리자로부터의 보호책이 아닙니다. 관리자는 여전히 파일을 읽을 수 있습니다. 또한 본인 계정으로 실행되는 다른 프로그램, 폴더 내부의 하드 링크, ~/.claude의 에이전트 백업, 또는 OneDrive나 문서 폴더 내부의 상태 폴더에는 적용되지 않습니다.

머신 외부로 전송되는 데이터

기본적으로 정책상 아무것도 전송되지 않습니다. Collie는 설치 이벤트, 사용 통계, 충돌 보고서, 분석 데이터를 전송하지 않습니다. 이를 활성화하는 플래그도 없습니다.

유일하게 요청 없이 발생하는 아웃바운드 호출은 업데이트 확인입니다. 사용자의 버전과 최신 태그를 비교하는 GitHub 공개 태그 API(bridge/update.ts)로의 익명 HTTPS GET 호출입니다. 사용자나 머신에 대한 데이터는 전송되지 않으며, 정적 사용자 에이전트인 collie-update-check만 포함됩니다.

Whistle 모델 다운로드는 또 다른 아웃바운드 호출입니다. 사용자가 호스트로 다운로드을 탭하거나 collie stt whistle을 실행할 때만 발생하며, 사용자에 관한 데이터는 전혀 전송하지 않습니다(음성 엔진(Whistle)).

Walky talky는 외부 호출을 추가하지 않습니다. 다른 머신으로 보내는 메시지는 crew 링크를 타며 다른 곳으로는 가지 않습니다(Walky talky).

문제 신고는 외부 호출을 발생시키지 않습니다. 전송이 실패한 후 스마트폰은 브라우저에서 바로 열 수 있는 링크 형태로 내용이 미리 채워진 GitHub의 새 이슈 등록 양식을 열 수 있습니다. 먼저 전체 텍스트를 확인하고 본인의 계정으로 직접 제출하십시오. Collie는 GitHub 토큰을 보유하지 않으며 스마트폰은 절대 GitHub를 호출하지 않습니다(ADR 0123).

COLLIE_ACCESS_TEAM을(를) 설정하면 Collie는 시작 시 및 한 시간에 한 번 해당 Cloudflare Access 팀의 공개 키도 가져옵니다. 해당 호출은 사용자가 직접 활성화하는 것이며, 사용자에 관한 정보도 일절 전송하지 않습니다.

URL로 durable agent를 연결하면 Collie는 모든 폴링마다 Authorization 헤더에 durable agent의 bearer 토큰을 포함하여 해당 URL을 호출합니다. 이 호출 역시 사용자가 직접 활성화해야 하며, 연결하기 전에는 아무것도 전송되지 않습니다(Durable agent의 보안).

데이터 수집이 추가되더라도 명시적인 옵트인이 최대 한도입니다. 기본적으로 꺼져 있고, 눈에 띄는 질문으로 확인하며, 플래그나 기본값으로 처리되지 않습니다. 이 약속을 제거하는 것은 주요 변경 사항(ADR 0034)에 해당합니다.

GitHub에서 이 페이지 수정하기

The rest of the documentation