Skip to main content
Chat XDK는 X Chat의 키 관리, 암호화, 복호화, 서명을 처리합니다. X HTTP API를 직접 호출하지 않습니다Python 또는 TypeScript XDK나 사용자 액세스 토큰을 사용한 HTTPS와 함께 사용하세요. 앱 상세 안내: 시작하기. 샘플 봇: chat-xdk/examples.

설치

PyPI 패키지는 chatxdk이며 chat_xdk로 import합니다. Python 3.10+이 필요합니다.

빠른 시작

키를 로드하고, 아이덴티티를 한 번 설정하고, 백로그를 복호화하고, 하나의 실시간 이벤트를 복호화하고, 메시지를 암호화합니다. 시작하기와 같이 전송 본문을 POST /2/chat/conversations/{id}/messages에 연결하세요. 스니펫은 가장 짧은 호출 형태를 위해 두 개의 선택적 세션 저장소를 사용합니다: set_signing_keys는 다른 참여자의 공개 키를 보관하여(공개 키 엔드포인트에서 가져옴) 복호화 호출이 호출별 인수 없이 발신자를 검증할 수 있게 하고, set_cache_keys(true)는 SDK가 각 대화의 검증된 키를 기억하도록 하여 암호화 호출이 대화 ID와 텍스트만 필요하게 합니다. 둘 중 하나를 건너뛰고 동일한 값을 호출별로 전달하세요—두 스타일 모두 동일하게 검증합니다; Decrypt를 참고하세요.

라이프사이클과 키

SDK를 구성하고, 개인 키를 저장하고(패스코드로 보호된 보안 키 백업 또는 로컬 키 blob), Chat API에 공개 키를 등록한 다음, unlock 또는 import 후 **set_identity(user_id, signing_key_version)**을 호출하세요—모든 서명된 액션이 기본적으로 사용하는 발신자와 서명 키 버전을 설정하므로, encrypt와 prepare 메서드가 호출별 아이덴티티 인수 없이 작동합니다. 기기/앱 아이덴티티당 generate_keypairs를 한 번 호출하고, 등록 페이로드를 공개 키 엔드포인트에 게시하세요. 모든 바인딩에서 보안 키 백업에 대해 setup / unlock(및 관련 패스코드 헬퍼)을 사용하세요. export_keys / import_keys(봇과 서버를 위한 원시 키 blob 지속성)는 네이티브 바인딩 전용입니다—Python, Go, .NET, JVM, Rust. JS/WASM 바인딩은 원시 키 내보내기나 가져오기를 노출하지 않습니다: 브라우저에서는 인스턴스에 접근할 수 있는 어떤 스크립트든 아이덴티티를 유출할 수 있으므로 JS는 키를 보안 키 백업 내부에 유지합니다. 요청당 백업 realm 왕복을 피하려는 JS 서버는 요청 전체에 걸쳐 잠금 해제된 하나의 Chat 인스턴스를 재사용하거나, 키 blob이 지원되는 네이티브 바인딩을 실행해야 합니다. SDK는 또한 등록된 공개 키에 대해 X API가 보고하는 버전을 필요로 하므로, 다른 버전을 대상으로 하는 키 변경 항목은 건너뜁니다. set_identity는 이를 사용자 ID와 함께 기록합니다; import_keys는 이를 선택적 인수로 직접 받습니다(Rust와 Go는 import_keys_with_version / ImportKeysWithVersion을 사용).
보안 키 백업 구성은 세 가지 형태를 허용합니다: X API의 juicebox_config 객체(권장—그대로 전달), 전체 sdk_config 래퍼, 또는 단순 token_map. 선택 사항: 서명 검증은 기본적으로 켜져 있습니다(reject_unverified = true)—비활성화하려면 set_reject_unverified(false)를 호출하세요(권장하지 않음); 백업 realm 구성이 변경되면 update_config; UI 상태를 위해 is_unlocked / has_identity_key. 전체 필드 목록은 chat-xdk repo 스텁에 있습니다.

대화 키

세 개의 prepare 메서드는 각각 한 번의 호출로 키 변경에 필요한 모든 것을 수행합니다: 새 대화 키를 생성하고, 모든 참여자(전달한 공개 키에서)에 대해 암호화하고, 변경에 서명합니다. 발신자 아이덴티티와 서명 키 버전은 세션(set_identity)에서 옵니다. 오버라이드하려면 params에 sender_id / signing_key_version을 설정하세요. 모두 동일한 PreparedConversationChange 형태를 반환하여 POST 준비가 됩니다—conversation_participant_keys에서 SDK 필드 encrypted_key를 **encrypted_conversation_key**로 이름 변경하고, 액션 서명을 필수 action_signatures 본문 필드로 매핑하세요. encrypt_message와 미디어에 사용할 원시 키 바이트를 보관하세요; API의 암호화된 봉투를 절대 encrypt에 전달하지 마세요.
감싸기 전에 가져온 키를 검증하세요. prepare 메서드는 전달하는 모든 공개 키에 대해 새 대화 키를 암호화합니다. 전달하기 전에 각 가져온 레코드에 대해 verify_key_binding(identity, signing, signature)을 호출하세요—공개 키 API에서 얻은 public_key, signing_public_key, identity_public_key_signature 필드—대체된 아이덴티티 키가 대화 키를 받지 못하도록 합니다.
키 변경 이벤트 페이로드에 대해 extract_conversation_keys를 사용해 { keys, latest_version }을 재구성하세요. decrypt_conversation_key는 단일 ECIES blob을 언랩합니다.
그룹 생성과 멤버 추가에 대해서는 각 메서드가 필요로 하는 params를 전달하세요(prepare_group_create에는 멤버/관리자 ID 목록; prepare_group_members_change에는 신규 및 현재 명단)—샘플은 그룹을 참고하세요. 둘 다 두 개의 액션 서명을 반환합니다; POST에는 둘 모두 포함해야 합니다.

Decrypt

**decrypt_events**는 히스토리와 백로그용입니다: 스트림에서 대화 키를 가져오고, 복호화된 메시지를 반환하며, 전체 배치를 실패시키는 대신 이벤트별 오류를 수집합니다. **decrypt_event**는 단일 실시간 이벤트용입니다; 실패 시 예외를 발생/던집니다. SDK가 발신자를 검증할 수 있도록 서명 키를 전달하세요. API 공개 키 필드를 SigningKeyEntry에 매핑하세요: public_key_versionpublic_key_version(동일 이름), signing_public_keypublic_key, public_keyidentity_public_key, 그리고 identity_public_key_signatureuser_id. 두 개의 옵트인 세션 저장소를 사용하면 호출별 키 인수를 생략할 수 있습니다:
  • **set_signing_keys(entries)**는 참여자 서명 키를 저장합니다; 서명 키 인수를 생략(또는 빈 값을 전달)하는 복호화 호출은 저장소를 대신 사용합니다. 검증 자체는 변경되지 않습니다—키는 이 호출을 통해서만 저장소에 들어가며, 복호화 중인 이벤트에서는 결코 들어가지 않습니다. 각 호출은 이전 세트를 대체합니다.
  • **set_cache_keys(true)**는 대화 키 캐시를 활성화합니다(기본은 꺼짐). 활성화된 동안 decrypt_events는 대화별로 유효한 서명이 있는 키 변경의 최신 키를 캐시합니다; decrypt_event는 대화 키 인수가 생략되면 이에 폴백하고, encrypt 헬퍼는 생략된 대화 키를 이로부터 해석합니다. 비활성화하면 캐시가 지워집니다.
명시적으로 비어 있지 않은 인수는 항상 저장소보다 우선합니다. 명시적 호출별 인수는 여전히 일급이며—서버리스 또는 멀티 인스턴스 배포에 적합한 선택입니다. 여기서는 요청이 저장소가 비어 있는 새 인스턴스에 도달할 수 있습니다. 검증은 기본적으로 필수입니다: 서명 키를 생략해도 검증을 건너뛰지 않습니다. 아무것도 전달하지 않고 아무것도 저장하지 않으면, 서명된 이벤트는 실패합니다(decrypt_events의 경우 errors에 수집되고, decrypt_event의 경우 던져짐). 실제로 검증을 건너뛰려면 먼저 set_reject_unverified(false)를 호출해야 합니다(프로덕션에서는 권장하지 않음).

암호화 및 전송 헬퍼

**encrypt_message(conversation_id, text)**는 텍스트 메시지에 대한 서명된 암호문을 생성합니다; 선택 사항으로 entities, attachments(media_hash_key를 통해), should_notify, ttl_msec. 발신자 아이덴티티는 세션(set_identity)에서, 대화 키는 옵트인 키 캐시(set_cache_keys)에서 해석됩니다—또는 sender_id / signing_key_versionconversation_key + conversation_key_version을 명시적으로 전달하세요. SDK가 message_id(서명된 이벤트에 포함된 UUID)를 생성하고 페이로드에 반환합니다—절대 직접 만들지 마세요; 재시도에는 동일한 페이로드를 재사용하여 ID가 두 번 생성되지 않도록 하세요. 페이로드를 send-message 본문으로 매핑하세요: message_idmessage_id, encrypted_contentencoded_message_create_event, encoded_event_signatureencoded_message_event_signature. 답장은 이벤트 기반입니다. encrypt_reply(conversation_id, text, reply_to_event)는 답장할 base64 원시 이벤트를 받습니다. SDK가 이로부터 인용된 미리보기(sequence ID, 발신자, 텍스트, entities, attachments)를 도출하고 서명된 원본을 발신 메시지에 포함하여 수신자가 인용을 검증할 수 있게 합니다. 원본이 답장보다 이전 키 버전으로 암호화된 경우 원시 키 변경 이벤트를 reply_to_ckces로 전달하세요. 원본이 편집된 경우 원시 편집 이벤트를 reply_to_edit_event로 전달하세요: 그러면 미리보기가 메시지가 현재 말하는 것을 인용하며(텍스트와 entities는 편집에서 옴), 편집은 수신자가 확인할 수 있도록 원본과 함께 이동합니다. 명시적 reply_to_* 필드는 원시 이벤트를 더 이상 보유하지 않는 호출자에 대한 오버라이드로 유지됩니다. 리액션도 이벤트 기반입니다. encrypt_add_reaction(target_event, emoji)encrypt_remove_reaction(...)는 리액션 대상인 원시 이벤트에서 대화 ID와 대상 sequence ID를 도출합니다; 동일한 params로 리액션을 추가하고 나중에 제거할 수 있습니다. 원시 이벤트를 더 이상 보유하지 않을 때에만 conversation_idtarget_message_sequence_id를 명시적으로 설정하세요. 수신 측에서, 답장을 인용하는 복호화된 메시지는 reply_preview_validation("Valid" / "Invalid"; JS 바인딩은 'valid' / 'invalid' 사용)을 가집니다: SDK가 저장된 서명 키에 대해 포함된 원본의 서명을 검증하고—결코 이벤트에 담긴 키가 아닙니다—복호화한 다음 인용된 콘텐츠와 작성자를 이에 대조합니다. 미리보기가 편집 이벤트를 포함하는 경우, SDK는 편집을 동일한 방식으로 검증하고(동일한 대화, 원본과 동일한 작성자) 편집 이전 텍스트가 아닌 편집된 내용에 대해 인용된 텍스트를 확인합니다. 메시지에 미리보기가 없거나 미리보기에 원본이 포함되지 않은 경우 이 필드는 없습니다. Invalid 미리보기는 신뢰할 수 없는 것으로 취급하세요: 메시지 자체는 진짜지만, 인용된 자료는 그렇지 않습니다—인용은 검증된 원본에서만 렌더링하세요. **encrypt / decrypt**는 대화 키 하의 UTF-8 메타데이터용입니다(예: 암호화된 그룹 이름)—메시지 봉투용이 아닙니다. **encrypt_stream / decrypt_stream**은 첨부 파일 바이트를 암호화합니다; 미디어 참고. 저수준 **sign / verify / verify_key_binding**은 고급 흐름을 지원합니다; 대화 키 변경, 그룹 생성, 멤버 추가는 prepare 메서드에 의해 서명됩니다. encrypt_message / encrypt_reply에 전달되는 대화 ID는 당신이 보유한 어떤 형태든 될 수 있습니다—이벤트의 A:B, 목록이나 URL 경로의 A-B(순서 무관), 혹은 수신자의 사용자 ID—SDK가 서명 전에 정규화합니다. 그룹 ID(접두사 g)는 변경 없이 통과합니다.

미디어 스트림

텍스트에 사용된 것과 동일한 대화 키로 파일 바이트를 암호화하고, Chat 미디어 API를 통해 업로드하고, encrypt_message에 **media_hash_key**를 첨부하세요. 이는 Posts 미디어 모델(expansions=attachments.media_keys)이 아닙니다. 전체 업로드/다운로드 흐름: 미디어.

대용량 미디어를 위한 증분 스트리밍

대용량 파일의 경우 전체 페이로드를 메모리에 유지하지 않도록 하세요: stream_encryptor() / stream_decryptor()는 청크(약 1 MB 각각)로 push(chunk)를 공급하고 마지막에 finish()를 한 번 호출하는 StreamEncryptor / StreamDecryptor를 반환합니다. 복호화 시 finish()는 잘린 스트림을 감지합니다(마지막 프레임 전에 입력이 끝나면 실패). 따라서 성공할 때까지는 푸시된 평문을 완전한 것으로 취급하지 마세요.
JS/WASM 전용: finish()는 내부 WASM 객체를 소비하고 해제합니다—finish() 후에는 free()를 절대 호출하지 마세요(예외를 던집니다). free()는 finish 전에 스트림을 중단할 때만(예: 오류 경로에서) 호출하세요.

유틸리티

Base64/hex 헬퍼, MIME 스니핑, 이미지 치수는 모듈 수준 함수(Python/JS/Rust/Go) 또는 ChatXdkUtilities(C#/Java)로 제공됩니다—추가 라이브러리를 가져오지 않고 첨부 메타데이터를 구성할 때 유용합니다.

중요한 타입

이러한 개념적 타입은 언어 전반에 걸쳐 나타납니다(정확한 필드 이름은 다르며, JS는 종종 message와 같은 카멜케이스 이벤트 판별자를 사용합니다):
  • SendPayloadencrypt_message와 다른 encrypt 헬퍼의 반환 값: SDK가 생성한 message_id(서명된 이벤트에 포함된 UUID—메시지의 message_id로 전송하고 중복 제거를 위해 보관), encrypted_content, encoded_event_signature, 서명 메타데이터, conversation_key_version, should_notify. Chat API 전송 본문으로 매핑하세요.
  • PublicKeyRegistrationPayload — add-public-key API를 위한 generate_keypairs / 공개 키 getter의 출력.
  • SigningKeyEntry — 서명 검증을 위해 decrypt에 전달되거나 set_signing_keys로 저장되는 발신자 공개 자료.
  • PreparedConversationChange — 세 prepare 메서드의 출력: 도출되거나 전달된 conversation_id, 원시 conversation_key 바이트, conversation_key_version, participant_keys(user_id, encrypted_key, public_key_version), action_signatures(message_id, encoded_message_event_detail, signature, signature_version, public_key_version, 선택적 signature_payload—해당 페이로드가 평문 키를 포함하기 때문에 키 변경 서명에서는 생략됨).
  • DecryptEventsResult — messages, 선택적 errors, 그리고 추출된 conversation_keys. 답장을 인용하는 복호화된 메시지는 reply_preview_validation을 가집니다(암호화 및 전송 헬퍼 참고).
전체 필드 목록은 chat-xdk repo(docs/API.md, *.pyi, index.d.ts)의 언어 스텁을 사용하세요.

오류

Python은 일반적으로 서술적인 메시지와 함께 **ValueError**를 발생시킵니다(예: 잘못된 패스코드). TypeScript/JavaScript는 **Error**를 던집니다. Go는 (value, error)를 반환합니다. 하나의 잘못된 이벤트가 배치를 중단하지 않도록 히스토리에는 **decrypt_events**를 선호하세요; 부분 실패에 대해서는 errors 컬렉션을 검사하세요. 일부 검증 오류는 영구적입니다. 서명은 불변이며 이벤트 자체에서 서명된 페이로드를 재구성하여 검증되므로, signature missing or no matching signing key 또는 ECDSA 불일치로 실패하는 오래된 이벤트는 이후 모든 로드에서 실패합니다—어떤 재시도, 키 새로 고침, API 호출도 이를 치유할 수 없습니다. 이러한 오류는 일시적 오류가 아니라 묘비(tombstone)로 취급하세요. 대화 키를 순환하면 그 시점부터 깨끗하고 검증 가능한 히스토리가 시작됩니다.

다음 단계

시작하기

Chat XDK를 Chat API에 연결

미디어

스트림 암호화와 미디어 REST

실시간 이벤트

웹훅과 활동 전달

문제 해결

일반적인 실패