서버가 보는 것
Hwip은 제가 운영하는 서버를 거쳐 컴퓨터와 휴대폰 사이에 메시지를 전달합니다. 이 페이지에는 그 서버가 읽을 수 있는 것과 없는 것, 그리고 보호가 어디서 멈추는지를 적었습니다.
요약
- 질문, 답, 알림, diff, 터미널 출력, 세션 이력의 내용은 보내기 전에 기기에서 암호화됩니다. 서버는 암호문을 저장하고 전달합니다.
- 서버는 누가 누구와 이야기하는지는 봅니다: 계정, 기기, 세션 이름, 크기와 시각.
- 두 종류의 메시지는 암호화되지 않습니다: 채널 글, 그리고 서버가 직접 쓰는 상태 알림("세션 완료", "세션 대기 중")입니다. 여기에는 프로젝트와 세션 이름이 들어갑니다.
- 암호화는 유출되거나 읽힌 서버로부터 당신을 보호합니다. 기기 목록을 적극적으로 속이는 서버로부터는 완전히 보호하지 못합니다. 자세한 내용은 아래에 있습니다.
암호화 방식
각 기기(휴대폰, 컴퓨터마다)는 처음 쓸 때 자기 ECDH P-256 키 쌍을 만듭니다. 개인 키는 기기를 떠나지 않고, 서버는 공개 키만 저장하며 개인 키 업로드는 거부합니다. 메시지는 AES-256-GCM으로 한 번 암호화되고, 그 키는 계정의 기기마다 따로 감싸집니다.
암호화는 모든 요금제에서 항상 켜져 있습니다. 끄는 설정도, 평문으로 대신 보내는 길도 없습니다: 메시지를 봉인할 수 없으면 CLI는 보내기를 거부하고 이유를 알려 줍니다.
봉인되는 것
| 항목 | 설명 |
|---|---|
| 알림 | 제목, 본문, URL이 암호문 안에 실려 갑니다 |
| 질문 | 제목, 본문, 각 버튼의 문구 |
| 답 | 휴대폰이 질문한 컴퓨터를 위해 봉인합니다 |
| 질문 맥락 | 질문에 함께 실리는 git diff 와 터미널의 마지막 60줄 |
| 실시간 세션 보기 | 터미널 프레임, 키 입력, 화면 스냅샷, 스크롤백 페이지 |
| diff 검토 | 어떤 파일을, 무엇과 비교해, 몇 번째 페이지를 요청했는지도 봉인됩니다 |
| 세션 이력 | 프롬프트, 에이전트의 답, 도구 호출마다 한 줄 |
| 승인 요청 | 세션이 기다리는 명령이나 diff, 번호 붙은 답 |
| 앱에서 보낸 파일 | 크기를 채워 저장하고 기록에는 file, application/octet-stream, 채운 크기만 적힙니다 |
| 컴퓨터 사이의 메시지 | hwip agent send |
서버가 보는 것
봉인은 내용을 덮습니다. 전달하려면 몇 가지 사실은 평문이어야 합니다.
| 항목 | 이유 |
|---|---|
| 계정과 기기 목록, 각 기기의 공개 키 | 어디로 전달할지 알기 위해 |
hwip-myapp 같은 세션 이름 | 메시지를 맞는 세션으로 보내기 위해. 이름은 프로젝트 디렉터리에서 오므로 디렉터리 이름이 보입니다 |
| 메시지가 어느 기기를 위해 봉인됐는지, 어느 기기가 답했는지 | 키 감싸기 표가 기기 id로 묶여 있습니다 |
| 질문의 경우: 각 버튼의 id(문구가 아니라), 제한 시간, 우선순위, 몇 가지 플래그 | 만료시키고 답을 기록하기 위해 |
| 크기와 시각 | 메시지를 언제 보냈는지, 대략 얼마나 큰지 |
| 오류 코드 | 실패한 요청에는 짧은 코드가 평문으로 돌아갑니다 |
| 세션 목록 | 세션마다: 이름, 프로젝트 디렉터리의 이름(경로가 아니라), 붙은 라벨과 제목, 에이전트 자체의 세션 id, 상태(작업 중, 대기 중, 완료), 마지막 활동 시각. 앱에서 세션에 붙인 별칭과 폴더도 저장됩니다 |
| 푸시 종류와 우선순위 | 푸시가 메모인지, 클립보드 항목인지, 파일인지, 명령인지 등 |
"크기"보다 더 들어가는 두 가지:
- 질문의 맥락 첨부는 내용이 없거나 꺼 두었을 때도 늘
file이라는 이름의 64 KiB로 보냅니다. 서버는 질문에 맥락이 있었는지, 얼마나 바뀌었는지 알 수 없습니다. - 앱에서 보낸 파일은 크기를 채우고, 실제 이름·종류·크기는 봉인 안에 들어갑니다. CLI나 MCP 서버가 보낸 파일은 기록에 이름이 남습니다.
서버는 디렉터리의 전체 경로는 받지 않고 디렉터리 이름만 받습니다.
암호화되지 않는 것
- 채널 글. 채널 구독자에게 보내는 푸시(
hwip notify --channel)는 평문입니다. 구독자가 봉인된 글을 열 수 없기 때문입니다. CLI는 보낼 때마다 이 사실을 알립니다. - 상태 알림. 세션이 끝나거나, 종료되거나, 당신을 기다리고 있으면 서버가 위의 세션 목록을 보고 짧은 알림을 직접 씁니다. 제목에는 프로젝트 이름이나 세션에 붙인 별칭이, 본문에는 tmux 세션 이름이 들어갑니다. 봉인되지 않고 쓰인 그대로 Google과 Apple의 푸시 서비스에 넘어갑니다. 에이전트가 한 말과 기다리는 내용은 들어 있지 않습니다.
- 컴퓨터가 휴대폰의 키를 보기 전에 입력한 메시지. 실시간 보기를 열지 않은 채 휴대폰에서 보낸 명령은, 휴대폰과 그 컴퓨터가 서로 한 번 봉인하기 전까지 평문 요청으로 갑니다. 그 첫 봉인 이후로 컴퓨터는 평문을 영영 거부합니다.
hwip sh셸 세션의 화면도 마찬가지입니다. - 옛 앱 버전. 기기별 키가 생기기 전의 앱 빌드에는 넘겨줄 키가 없어 실시간 보기가 평문입니다. 키가 있는 기기를 한 번이라도 본 컴퓨터는 그런 요청을 거부합니다.
봉인은 CLI, MCP 서버, 앱이 합니다. 서버는 평문을 보내기로 한 클라이언트의 메시지를 거부하지 않으므로, "항상 켜져 있다"는 약속은 서버의 성질이 아니라 직접 읽어 볼 수 있는 클라이언트의 성질입니다.
보호가 멈추는 곳
적극적으로 속이는 서버. 컴퓨터는 기기들의 공개 키를 서버에서 받는데 그 목록에는 서명이 없습니다. 자기 기기를 끼워 넣거나 당신이 아는 기기의 키를 바꿔치기한 서버는, 그 뒤의 메시지를 자기 앞으로도 봉인받아 읽을 수 있습니다. 당신의 휴대폰인 척 세션에 입력을 보낼 수도 있습니다. 휴대폰의 입력은 에이전트의 터미널로 들어가고 Claude Code는 ! 로 시작하는 줄을 셸 명령으로 실행하므로, 이는 당신의 컴퓨터에서 명령을 실행한다는 뜻입니다.
이를 막는 것: 모든 기기의 키는 처음 보았을 때 고정됩니다. 키가 바뀌면 한 번 알리고(터미널에, 세션을 시작할 때 Claude Code 플러그인에, 또는 MCP 도구 결과 끝에) ~/.hwip/key-changes.json 에 기록합니다. 알릴 뿐 막지는 않습니다. 직접 확인하려면 지문을 비교하면 됩니다: hwip trust 가 이 컴퓨터의 지문과 고정된 기기들을 보여 주고 앱은 각 기기 페이지에 같은 문자열을 보여 줍니다.
순방향 비밀성이 없습니다. 두 기기 사이의 키 합의는 고정되어 있습니다. 어떤 기기의 개인 키가 도난당하면, 그 쌍을 위해 봉인된 과거 메시지를 모두 열 수 있습니다.
비밀값 가리기는 최선을 다할 뿐입니다. 질문에 붙는 diff와 터미널 줄에서는 먼저 비밀값처럼 보이는 텍스트를 찾습니다. 검사가 모르는 모양의 비밀값은 그대로 남습니다. 첨부는 여전히 봉인되므로, 이는 서버가 아니라 휴대폰에 무엇이 닿느냐의 문제입니다. 프로젝트마다 hwip ask-context off 로 맥락을 끌 수 있습니다.
API 키는 세션의 열쇠입니다. ~/.hwip/config.json 의 API 키를 가진 사람은 누구나 당신의 계정으로 푸시를 보낼 수 있습니다. 이 파일을 남이 보지 못하게 하세요. CLI는 ~/.hwip 를 0700 권한으로 두고 리디렉트로 답하는 호스트에는 키를 보내지 않습니다.
한 컴퓨터의 OS 사용자 둘은 기기 id를 함께 씁니다. 각자 자기 키가 있어 기기 목록에서 서로의 키를 덮어쓰고 앱은 그때마다 키가 바뀌었다고 경고합니다.
컴퓨터에 남는 것
- 개인 키:
~/.hwip/device-keys.json. - 도구 인자, 도구 출력, 파일 내용. 세션 이력과 실시간 타임라인은 도구 이름, 도구가 다룬 대상, 줄 수를 보내고 도구가 읽거나 쓴 내용은 보내지 않습니다.
- 프로젝트의 질문 맥락을 껐는지 여부. 그 스위치는 로컬 파일입니다.
세션 이력 업로드는 hwip listener --no-turn-history 나 ~/.hwip/config.json 의 "turnHistory": false 로 끌 수 있습니다.
보관 기간
| 항목 | 보관 기간 |
|---|---|
| 푸시, 질문, 채널 글 | 30일 |
| 파일과 첨부 | 30일 |
| 세션 이력(봉인됨) | 365일 |
| 서버 로그 | 1개월 |
| 기기 목록 | 30일 동안 보이지 않은 기기는 다음에 기기를 등록할 때 지워집니다 |
| 실시간 세션 보기 | 저장하지 않습니다. 보고 있는 동안 전달만 합니다 |
삭제는 데이터베이스의 만료 기능에 맡기는데 이 기능은 몇 시간 늦게 돌 수 있습니다. 운영 데이터베이스에는 특정 시점 복구도 켜져 있어서 지운 행도 복구 기간 동안은 제가 되살릴 수 있습니다.
계정을 지우면 파일, 세션, 이력, 기기, 당신이 소유한 채널, 로그인 기록이 지워집니다. 다른 구독자에게 이미 전달된 채널 글의 사본은 30일이 다 될 때까지 남고 로그와 데이터베이스 복구 데이터는 각자의 일정에 따라 사라집니다.
그 밖에 관여하는 곳
- Amazon Web Services 가 모든 것을 서울 리전(
ap-northeast-2)에서 호스팅합니다: 데이터베이스, 파일 저장소, 중계, 로그인. - Google(Firebase Cloud Messaging)과 Apple(APNs) 이 알림을 전달합니다. iPhone 알림은 Google을 먼저 거쳐 Apple로 갑니다. 봉인된 푸시라면 두 서비스는 고정 제목(
Hwip), 고정 본문(New encrypted message), 푸시 id, 그 종류, 계정을 가리키는 해시를 받고 실제 내용은 앱이 기기에서 가져와 엽니다. 위의 암호화되지 않는 두 종류는 텍스트가 그대로 넘어갑니다. - Google과 Apple 로그인. 둘 중 하나로 로그인하며 서버는 거기서 이름과 이메일 주소를 받습니다. Apple에서는 이메일을 숨길 수 있습니다.
앱에는 분석 SDK도, 크래시 리포터도, 광고 SDK도 없습니다.
직접 확인하기
CLI, Claude Code 플러그인, MCP 서버는 오픈 소스(Apache-2.0)이고 봉인은 모두 거기와 앱에서 일어납니다. 서버와 앱은 소스가 공개되어 있지 않으므로, 이 페이지의 서버 쪽 서술은 직접 감사할 수 있는 것이 아니라 제 말입니다.
질문이나 제보: support@hwip.app