Skip to main content
X에서 종단 간 암호화된 다이렉트 메시지를 주고받으세요: 키를 설정하고, 대화를 초기화하고, 메시지를 보내고, 수신 트래픽을 복호화합니다. X Chat 앱은 두 가지 구성 요소를 함께 사용합니다:
사전 요구 사항
  • 개발자 계정 및 OAuth 2.0으로 구성된 앱
  • dm.read, dm.write, tweet.read, users.read가 포함된 사용자 액세스 토큰

1. 종속성 설치

PyPI 패키지는 chatxdk이며, chat_xdk로 임포트합니다. Python 3.10+ 필요.
사용자 OAuth 2.0 액세스 토큰으로 API 클라이언트를 생성합니다:

2. 기존 키로 Chat XDK 초기화

이 단계는 이미 보유한 키를 로드합니다—이 신원이 이전에 최초 설정을 완료한 경우 사용하세요:
  • 보안 키 백업: 공개 키 레코드의 juicebox_config로 SDK를 구성한 다음, 패스코드로 unlock하여 개인 키를 복구합니다 (예: 새 기기에서).
  • 키 blob: 이전에 export_keys로 내보낸 blob으로 import_keys를 호출하고, 등록된 키 버전을 함께 전달합니다 (Rust와 Go에서는 이 변형을 import_keys_with_version / ImportKeysWithVersion이라고 부릅니다).
그런 다음 사용자 ID와 레코드의 public_key_version을 함께 **set_identity(user_id, signing_key_version)**를 한 번 호출합니다. 이는 세션 신원을 저장하므로, 이후의 모든 encrypt 및 prepare 호출은 이 신원으로 서명합니다. 따라서 호출마다 발신자 ID나 서명 키 버전을 전달할 필요가 없습니다. 처음 설정하시나요? 동일한 방식으로 SDK를 구성하되 unlock/import_keys는 건너뛰고, 3단계로 계속 진행하여 키를 생성, 백업, 등록하세요.
서버 및 봇 샘플에서는 종종 키 blob(export_keys / import_keys)을 사용합니다. 클라이언트 앱에서는 종종 보안 키 백업(패스코드로 setup / unlock)을 사용합니다. 두 경로 모두에 대해 Chat XDK 참조를 확인하세요.
직접 만든 키를 가져오시나요? import_keys는 Chat XDK의 export_keys가 생성한 불투명 blob만 허용합니다—이는 전체 키 상태의 버전 관리된 비공개 직렬화이며, 원시 또는 PEM으로 인코딩된 P-256 키가 아닙니다. 이 blob은 직접 구성할 수 없습니다: generate_keypairs(3단계)로 키를 생성하고, blob을 한 번 내보내 base64로 인코딩하여 저장하세요. 수작업으로 만들거나 수정한 blob은 가져오기에 실패합니다.

3. 키 생성 및 등록 (최초 설정)

2단계에서 기존 키를 로드했다면 이 단계를 건너뛰세요. 그렇지 않은 경우, 새 신원에 대한 일회성 설정은 세 가지 작업을 수행합니다:
  1. 키페어 생성generate_keypairs가 신원 및 서명 키페어를 생성합니다.
  2. 개인 키 저장 — 패스코드로 setup하면 보안 키 백업에 기록됩니다(클라이언트). 또는 export_keys가 안전하게 저장할 키 blob을 반환합니다(서버 및 봇).
  3. 공개 키 등록 — 다른 사람이 사용자에게 암호화하고 서명을 검증할 수 있도록 등록 페이로드를 공개 키 추가 엔드포인트에 POST합니다.
마지막으로 등록의 키 버전으로 set_identity를 호출하여 이 세션이 새 신원으로 서명하도록 합니다.
모든 바인딩(Python, TypeScript, Go, Rust, C#, Java)에서 바로 실행할 수 있는 일회성 등록 스크립트는 chat-xdk/examples에 있습니다. 새 신원을 온보딩하기만 하면 되는 경우, 아래의 흐름을 직접 작성하는 대신 이 스크립트를 사용하세요.
보안 키 백업에 강력한 패스코드를 사용하세요. 패스코드나 보호되지 않은 키 blob을 분실하면 이전 메시지를 복호화할 수 없게 될 수 있습니다.

4. 대화 키 설정

모든 참가자의 신원 공개 키와 함께 **prepare_conversation_key_change**를 호출하세요. 발신자 신원은 2단계에서 설정한 세션에서 가져옵니다. 한 번의 호출로 새 대화 키가 생성되고, 각 참가자에 대해 암호화되며, 변경 사항이 서명됩니다. 결과를 대화 키 추가 엔드포인트(POST /2/chat/conversations/{id}/keys)에 POST하세요—본문에는 conversation_key_version, conversation_participant_keys(SDK encrypted_key → API encrypted_conversation_key), 그리고 action_signatures(필수; 없으면 API가 호출을 거부함)가 필요합니다. 전송에 사용할 원시 대화 키를 보관하세요. 응답은 표준 대화 ID(data.conversation_id—1:1의 경우 하이픈으로 연결된 쌍, 그룹의 경우 g- 접두사가 붙은 ID)와 키 변경의 data.sequence_id를 반환합니다. 클라이언트 측에서 재구성하는 대신 이후 요청에는 반환된 ID를 사용하세요. 같은 호출은 나중에 키를 교체하기도 합니다: 기존 대화 ID를 prepare_conversation_key_change에 전달하고 최신 키 버전으로 POST합니다. 대화 키가 노출된 것으로 의심될 때 교체하세요—교체는 향후 메시지만 보호합니다. 이전 키 버전으로 암호화된 메시지는 그 버전을 보유한 사람이라면 누구나 계속 읽을 수 있습니다.
래핑하기 전에 가져온 키를 검증하세요. prepare_conversation_key_change는 전달된 공개 키로 새 대화 키를 암호화합니다. 각 가져온 레코드를 먼저 verify_key_binding(identity, signing, signature)로 확인하세요—공개 키 API의 public_key, signing_public_key, identity_public_key_signature 필드를 전달하면 대체된 신원 키가 대화 키를 받는 것을 방지할 수 있습니다.

5. 메시지 보내기

4단계의 원시 대화 키로 암호화합니다. SDK가 메시지 ID(UUID)를 생성하여 서명된 이벤트에 삽입하고 페이로드에 반환합니다—직접 만들지 마세요. 전송 요청에서 다음과 같이 매핑합니다: API가 요구할 때 URL 경로에는 하이픈으로 연결된 대화 ID를 사용하세요(:-). SDK 자체는 유연합니다: encrypt_messageencrypt_reply는 보유하고 있는 어떤 형태의 ID든 허용합니다—이벤트의 A:B, 목록이나 URL 경로의 A-B(어떤 순서든), 또는 그저 수신자의 사용자 ID—그리고 서명 전에 정규화합니다. 그룹 ID(g 접두사)는 그대로 통과됩니다.
위 스니펫은 방금 4단계에서 만든 대화 키를 명시적으로 전달합니다. 키 캐시가 켜져 있고 decrypt_events 처리 과정에서 해당 대화의 키가 검증되면(6단계), encrypt_message(conversation_id, text)만으로도 충분합니다—SDK가 검증된 최신 키를 자동으로 채웁니다. 재시도 시에는 동일한 암호화 페이로드를 다시 보내야 ID가 두 번 생성되지 않습니다.

6. 수신 및 복호화

실시간 트래픽에는 웹훅 또는 활동 스트림을, 이력 조회에는 대화 이벤트의 페이지 처리를 사용하세요.
  • 실시간 페이로드 필드: encoded_event, 선택적 conversation_key_change_event
  • 이력: GET /2/chat/conversations/{id}/events — 모든 이벤트에 대해 **decrypt_events**를 실행하고 meta.conversation_key_events를 함께 사용하는 것이 좋습니다
  • 복호화하려면 발신자의 서명 키가 필요합니다. 그래야 SDK가 각 메시지의 작성자를 검증할 수 있습니다. 이는 다른 참가자들의 공개 키입니다—4단계에서 사용한 동일한 공개 키 엔드포인트에서 가져와 필드를 SigningKeyEntry로 매핑하세요(아래 스니펫에 매핑이 포함되어 있습니다)
  • 서명 키(및 decrypt_event의 경우 대화 키)를 매 호출마다 전달하거나, 또는 두 개의 선택적 세션 저장소를 한 번 설정하고 짧은 호출 형식을 사용할 수 있습니다. 아래 스니펫은 저장소를 사용합니다: set_signing_keys(entries)는 참가자의 키를 보관하고, set_cache_keys(true)(기본값 꺼짐)는 각 대화의 서명 검증된 최신 키를 유지하여 이후 호출에서 키 인수를 생략할 수 있게 합니다. 두 방식 모두 동일하게 검증합니다
  • JavaScript는 camelCase 이벤트 유형(message)을 사용합니다. 다른 언어는 "Message"와 JSON의 snake_case 필드를 사용합니다
서버리스 또는 다중 인스턴스인가요? 서명 키 저장소와 키 캐시는 SDK 인스턴스의 메모리에 존재합니다. 이러한 환경에 맞지 않는 경우—한 호출은 복호화하고 다른 호출은 전송하는 경우—키를 명시적으로 전달하세요: decrypt_events(events, signing_keys), decrypt_event(event_b64, conversation_keys, signing_keys), 그리고 encrypt 메서드의 conversation_key/conversation_key_version 재정의를 사용합니다. decrypt_events가 반환하는 conversation_keys를 직접 유지하고 다시 전달하세요.
모든 언어에 대한 전체 폴링-응답 봇 예제: chat-xdk/examples.

모범 사례

  • 서명 키 저장소를 최신 상태로 유지하세요: 발신자가 새 키 버전을 등록하면 전체 참가자 세트로 set_signing_keys를 다시 호출하고, 서명 검증 실패 시 갱신하세요
  • event_uuid로 실시간 전달을 중복 제거하세요