프로젝트 소개
Wappie는 밀봉된 아카이브를 갖춘 멀티테넌트 WhatsApp API 서버이며, 동일한 API를 사용하는 웹 클라이언트를 제공합니다. 이 시스템은 HTTP 및 WebSocket을 통해 비즈니스 시스템을 WhatsApp에 연결하며, 공유 작업 공간, 번호별 권한 및 메시징 클라이언트를 지원합니다. 서버, CLI, 웹 클라이언트 및 기본 관리 기능은 Apache-2.0 오픈 소스로 제공됩니다. 관리형 호스팅과 상업적 과금은 별도로 유지되며, 호스팅 파일럿은 초대 전용 무료 서비스로 제공되고 결제는 시뮬레이션 방식으로 처리됩니다.
기능적으로 서버는 터미널에서 8자리 코드(연결된 기기 항목에 입력) 또는 터미널에 렌더링된 QR 코드를 사용하여 WhatsApp 기기를 페어링한 후, 유입되는 해당 기기의 트래픽을 밀봉합니다. 히스토리 동기화, 프로젝트 수정, 취소 및 반응을 수집하고, 수신 확인을 기록하며, 각 읽기 사용자가 화면에서 어떤 리비전을 보았는지 추적합니다. 수신 미디어는 Meta CDN이 제공한 그대로 저장되며, 송신 미디어 업로드 및 전송과 '한 번만 보기' 기능이 지원됩니다. 구조화된 콘텐츠(위치, 투표, 연락처, 이벤트), 연락처, 이름 및 프로필 사진, 온디맨드 백필, 그리고 읽지 않은 메시지 수, 체크 표시, 상태, 그룹 및 투표가 포함된 대화 레이어가 모두 구현되어 있습니다. 액세스는 CLI 도구(`wsctl`)와 HTTP/WebSocket API를 통해 이루어지며, 호스팅 엔드포인트와 WebSocket용 `/v1/ws` 경로가 제공됩니다.
아카이브 보호 모델은 이 프로젝트의 핵심입니다. WhatsApp 미디어는 이미 32바이트 미디어 키를 통해 encrypt-then-MAC HMAC 방식의 AES-256-CBC로 암호화되어 도착합니다. 암호문은 그대로 저장되며 미디어 키만 기기 공개 키로 밀봉됩니다. 메시지 본문은 비용 효율성을 위해 배치 단위의 콘텐츠 키를 사용하는 HPKE(RFC 9180, X25519 + HKDF-SHA256 + AES-256-GCM)로 밀봉됩니다. 서버는 공개 키만 보유하므로 밀봉은 가능하지만 열 수는 없습니다. 각 기기는 페어링한 클라이언트가 생성한 아카이브 키 쌍을 가지며, 개인 키 부분은 해당 기기를 읽을 수 있는 모든 계정의 공개 키로 밀봉(키 부여)된 후 삭제됩니다. 계정 개인 키는 가입 시 브라우저에서 생성되어 계정 주소에 바인딩된 Argon2id 유도 키로 래핑되며 절대 전송되지 않습니다. 복구 코드가 동일한 키를 두 번 래핑합니다. 브라우저는 페이지 내부에서 HPKE, 콘텐츠 키 처리, 메시지 본문, 연락처 이름, 프로필 사진 및 첨부 파일 복호화를 수행합니다.
권한은 계층 구조로 되어 있습니다. API 키는 `read`, `send` 또는 `full` 범위를 가지며, 멤버는 자신에게 부여된 기기에만 접근할 수 있습니다. 소유자 또는 관리자는 모든 기기의 엔벨로프에 접근하여 페어링, 권한 부여, 키 발행 및 기기의 discreet/loud 모드 전환이 가능합니다. 어떤 범위의 권한으로도 테넌트 설정에는 접근할 수 없습니다. 제3자에게는 비밀번호가 없는 키 쌍인 서비스 계정이 부여되며, 사람과 동일하게 기기 권한을 부여받고 이를 대행하는 API 키를 통해 접근합니다. 데이터 보존은 기본적으로 꺼져 있으며, 테넌트는 메시지, 수신 확인, 그룹 이벤트 및 첨부 파일에 적용될 시간 창을 설정할 수 있고 채팅과 연락처는 유지됩니다. 특정 사용자를 모든 기기의 아카이브에서 삭제할 수 있으며, 기기를 삭제하거나 아카이브를 초기화하면 저장소에서 해당 첨부 파일 객체가 제거됩니다. 로그는 모든 수준에서 식별자를 마스킹합니다.
로컬 실행을 위해서는 `uuidv7()`를 지원하는 PostgreSQL 18 이상이 필요하며, 슈퍼유저는 행 수준 보안(RLS)을 우회하므로 비슈퍼유저 역할이 필요합니다. 객체 저장소는 선택 사항이며, 설정되지 않은 경우 첨부 파일은 저장소가 나타날 때까지 데이터베이스에 큐로 저장됩니다. `make dev-up` 타겟을 통해 Docker에서 Postgres와 MinIO를 실행할 수 있습니다. 테스트 타겟은 포맷팅, 검증, 레이아웃, 레이스 조건 테스트, 브라우저 클라이언트의 타입 체크 및 빌드, 커버리지 및 퍼징을 포함하며, 각 테스트는 자체 Postgres 스키마에서 실행됩니다. 웹 클라이언트는 Vite를 통해 `web/dist`로 빌드되어 `WS_WEB_DIR`에 의해 제공됩니다. Go 바이너리에 내장된 것은 없으며, 빌드가 없는 경우 서버는 API만 제공합니다. 개발 중 Vite 서버는 WebSocket 핸들러가 동일 출처 연결만 허용하므로 `/v1`을 Go 포트로 프록시합니다.
README는 제공되는 기능 이상의 것을 암시하지 않고 한계를 명확히 밝히고 있습니다. 라이브 서버에서 코드를 실행하는 공격자는 Signal 복호화, 밀봉 및 저장 사이의 전송 중인 평문을 볼 수 있으므로, 저장 시 밀봉(at-rest sealing)은 탈취된 디스크, 유출된 백업 또는 데이터베이스 덤프를 보호하는 것이지 침해된 프로세스를 보호하는 것이 아닙니다. whatsmeow 세션 저장소는 프로세스가 읽을 수 있어야 하며, 이를 탈취한 사람은 기기를 사칭하여 새 메시지를 읽을 수 있지만 아카이브는 읽을 수 없습니다. 송신 텍스트와 미디어는 WhatsApp 업로드가 평문만 허용하므로 평문으로 전달되며, 수신 미디어는 서버 측에서 절대 복호화되지 않습니다. 첨부 파일 메타데이터(유형, 크기, 치수, 기간, 해시)와 수신 확인을 포함한 라우팅 메타데이터는 읽을 수 있으므로, 데이터베이스 덤프를 통해 소셜 그래프와 누가 언제 무엇을 읽었는지는 알 수 있지만 내용은 알 수 없습니다. 권한 부여를 취소하면 키를 다시 얻는 것은 막을 수 있지만, 키가 브라우저에 저장되었으므로 이미 잠금 해제된 복사본을 회수할 수는 없습니다. 모든 액세스 경로를 잃으면 모든 사용자가 아카이브를 영구적으로 잃게 됩니다. 브라우저 세션 자료는 추출 불가능한 WebCrypto 키 아래 암호문으로 IndexedDB에 저장되며, 클라이언트는 콘텐츠 보안 정책(CSP)을 적용하고 제3자 JavaScript를 로드하지 않습니다.
상태 표에는 스켈레톤, 마이그레이션, 미디어 암호화부터 페어링, 수집, 수정/취소/반응 프로젝션, 미디어 수신 및 송신, 히스토리 동기화, 미디어 재시도, 연락처, 온디맨드 백필, 웹 클라이언트, 기기별 키 및 계정, 대화 레이어, 인코그니토 및 쿼터, 보안 감사 패스까지 완료된 단계가 나열되어 있으며, 관리 콘솔이 다음 단계로 예정되어 있습니다. 교차 구현 테스트 벡터는 Go에서 생성되어 두 구현체 모두에서 열리며, 블롭이 다른 행으로 이동하거나 다른 종류로 제시되는 부정적인 케이스를 포함하여 브라우저 클라이언트가 스스로와만 일치하는 오류를 방지합니다. `seeddemo` 도구는 실제 수집 파이프라인을 통해 작은 가짜 대화를 작성하여 휴대폰 페어링 없이도 클라이언트를 개발할 수 있게 합니다.
Comments
0 Rating appears after 10 ratings
Sign in to join the discussion.