Skip to content

Commit 3c9119a

Browse files
authored
Merge pull request #268 from xdevplatform/xchat-docs-xdk-0.3
docs(xchat): update Chat XDK pages to the v0.4.0 API
2 parents 7f4678c + 715b6f3 commit 3c9119a

7 files changed

Lines changed: 623 additions & 489 deletions

File tree

xchat/cryptography-primer.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ If anything in the signed material changes, verification fails. Only someone wit
226226

227227
The Chat XDK signs when you encrypt outbound messages and verifies when you decrypt inbound ones against the sender’s public key material (from the public-key APIs). Verification is **mandatory by default**: the SDK rejects unverified signed events unless you explicitly disable the check (not recommended). Details are in the [Chat XDK](/xchat/xchat-xdk) reference.
228228

229+
Signatures also cover quoted content. A reply embeds the raw **signed** original message it quotes; when the Chat XDK decrypts the reply, it verifies that embedded original and compares the quote against it, reporting the outcome as `reply_preview_validation` (`Valid` / `Invalid`). An `Invalid` outcome means the quote does not match the signed original—treat the quoted material as untrusted, even though the reply itself is verified separately—so no participant can attribute fabricated words to another.
230+
229231
### Signed state changes (action signatures)
230232

231233
Messages are not the only signed material. Every call that changes conversation state—adding or rotating conversation keys, creating a group, adding members—must carry one or more **action signatures**: the sender signs a payload describing exactly what the change does (for a key change, that payload includes the new conversation key itself), and the API rejects the request if the signatures are missing or malformed.

xchat/getting-started.mdx

Lines changed: 203 additions & 169 deletions
Large diffs are not rendered by default.

xchat/groups.mdx

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Crypto is still: **Chat XDK** for keys and payloads; **X API** to create the gro
2929

3030
1. Mint the group id with `POST /2/chat/conversations/group/initialize` — the response's `data.conversation_id` is the g-prefixed id you use everywhere below.
3131
2. Load each member's identity public key and `public_key_version` (`GET` public-key routes under **Encryption keys**; [`GET /2/users/public_keys`](/x-api/users/get-public-keys-for-multiple-users) fetches several users in one request). Verify each record with `verify_key_binding` before using it (see the warning in [Getting Started](/xchat/getting-started#4-set-up-conversation-keys)).
32-
3. Run **`prepare_group_create`** once, with **all** members (including yourself), the g-prefixed id, and the member/admin id lists. One call generates the conversation key, wraps it for every member, and signs the create — it returns **two** action signatures (the conversation-key change and the group create).
32+
3. Run **`prepare_group_create`** once, with **all** members (including yourself), the g-prefixed id, and the member/admin id lists. One call generates the conversation key, wraps it for every member, and signs the create with the session identity from `set_identity` — it returns **two** action signatures (the conversation-key change and the group create).
3333
4. `POST /2/chat/conversations/group` with the group members/admins, `conversation_key_version`, `conversation_participant_keys` (SDK **`encrypted_key`** → API **`encrypted_conversation_key`**), and **both** `action_signatures`. Validation failures come back as stable, human-readable messages, for example `"Too many members: adding these members would exceed the allowed group size."` or `"Cannot add all members: one or more of the requested members cannot be added to this conversation."`.
3434
5. Keep the **raw** conversation key and **version** for encrypt/decrypt.
3535

@@ -38,8 +38,9 @@ Crypto is still: **Chat XDK** for keys and payloads; **X API** to create the gro
3838
<Tabs>
3939
<Tab title="Python">
4040
```python
41+
# chat has keys loaded and set_identity called (see Getting Started)
4142
prepared = chat.prepare_group_create(
42-
"YOUR_USER_ID", signing_key_version, member_public_keys,
43+
member_public_keys,
4344
group_id, # g-prefixed id from POST /2/chat/conversations/group/initialize
4445
member_ids, admin_ids, title="Project team",
4546
)
@@ -50,8 +51,9 @@ Crypto is still: **Chat XDK** for keys and payloads; **X API** to create the gro
5051
</Tab>
5152
<Tab title="TypeScript">
5253
```typescript
54+
// chat has keys loaded and setIdentity called (see Getting Started)
5355
const prepared = chat.prepareGroupCreate({
54-
senderId: myUserId, signingKeyVersion, publicKeys: memberPublicKeys,
56+
publicKeys: memberPublicKeys,
5557
conversationId: groupId, // g-prefixed id from POST /2/chat/conversations/group/initialize
5658
memberIds, adminIds, title: 'Project team',
5759
});
@@ -60,9 +62,9 @@ Crypto is still: **Chat XDK** for keys and payloads; **X API** to create the gro
6062
</Tab>
6163
<Tab title="Rust">
6264
```rust
65+
// chat has keys loaded and set_identity called (see Getting Started)
6366
let mut params = GroupCreateParams::new(
64-
&sender_id, &signing_key_version, member_public_keys,
65-
&group_id, member_ids, admin_ids,
67+
member_public_keys, &group_id, member_ids, admin_ids,
6668
);
6769
params.title = Some("Project team".into());
6870
let prepared = chat.prepare_group_create(params)?;
@@ -71,8 +73,8 @@ Crypto is still: **Chat XDK** for keys and payloads; **X API** to create the gro
7173
</Tab>
7274
<Tab title="Go">
7375
```go
76+
// chat has keys loaded and SetIdentity called (see Getting Started)
7477
prepared, err := chat.PrepareGroupCreate(chatxdk.GroupCreateParams{
75-
SenderID: myUserID, SigningKeyVersion: signingKeyVersion,
7678
PublicKeys: memberPublicKeys, ConversationID: groupID,
7779
MemberIDs: memberIDs, AdminIDs: adminIDs, Title: "Project team",
7880
})
@@ -83,23 +85,20 @@ Crypto is still: **Chat XDK** for keys and payloads; **X API** to create the gro
8385
</Tab>
8486
<Tab title="C#">
8587
```csharp
86-
var prepared = chat.PrepareGroupCreate(new GroupCreateParams {
87-
SenderId = myUserId, SigningKeyVersion = signingKeyVersion,
88-
PublicKeys = memberPublicKeys, ConversationId = groupId,
89-
MemberIds = memberIds, AdminIds = adminIds, Title = "Project team",
90-
});
88+
// chat has keys loaded and SetIdentity called (see Getting Started)
89+
var prepared = chat.PrepareGroupCreate(
90+
new GroupCreateParams(memberPublicKeys, groupId, memberIds, adminIds)
91+
{
92+
Title = "Project team",
93+
});
9194
// prepared.ActionSignatures has two entries — send both
9295
```
9396
</Tab>
9497
<Tab title="Java">
9598
```java
96-
GroupCreateParams params = new GroupCreateParams();
97-
params.senderId = myUserId;
98-
params.signingKeyVersion = signingKeyVersion;
99-
params.publicKeys = memberPublicKeys;
100-
params.conversationId = groupId;
101-
params.memberIds = memberIds;
102-
params.adminIds = adminIds;
99+
// chat has keys loaded and setIdentity called (see Getting Started)
100+
GroupCreateParams params =
101+
new GroupCreateParams(memberPublicKeys, groupId, memberIds, adminIds);
103102
params.title = "Project team";
104103
PreparedConversationChange prepared = chat.prepareGroupCreate(params);
105104
// prepared.actionSignatures has two entries — send both

xchat/media.mdx

Lines changed: 52 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -122,23 +122,19 @@ Use the request bodies on the OpenAPI pages under **API reference → Media**. P
122122

123123
## Send with an attachment
124124

125-
Encrypt with a media attachment, then POST the send-message body (same field mapping as [Getting Started](/xchat/getting-started#5-send-a-message)).
125+
Encrypt with a media attachment, then POST the send-message body (same field mapping as [Getting Started](/xchat/getting-started#5-send-a-message)). The SDK generates the `message_id` and returns it on the payload—send that value, and reuse the same payload on retries so an id is never minted twice.
126126

127127
<Tabs>
128128
<Tab title="Python">
129129
```python
130-
import uuid
131130
from xdk.chat.models import SendMessageRequest
132131

133-
message_id = str(uuid.uuid4())
132+
# chat has keys loaded and set_identity called (see Getting Started)
134133
payload = chat.encrypt_message(
135-
message_id,
136-
sender_id,
137134
conversation_id,
138-
raw_conv_key,
139135
caption or "",
140-
conversation_key_version,
141-
signing_key_version,
136+
conversation_key=raw_conv_key,
137+
conversation_key_version=conversation_key_version,
142138
attachments=[{
143139
"attachment_type": "media",
144140
"media_hash_key": media_hash_key,
@@ -151,7 +147,7 @@ Encrypt with a media attachment, then POST the send-message body (same field map
151147
client.chat.send_message(
152148
conversation_id.replace(":", "-"),
153149
SendMessageRequest(
154-
message_id=message_id,
150+
message_id=payload.message_id, # generated by the SDK
155151
encoded_message_create_event=payload.encrypted_content,
156152
encoded_message_event_signature=payload.encoded_event_signature,
157153
),
@@ -160,37 +156,47 @@ Encrypt with a media attachment, then POST the send-message body (same field map
160156
</Tab>
161157
<Tab title="TypeScript">
162158
```typescript
163-
const messageId = crypto.randomUUID();
159+
// chat has keys loaded and setIdentity called (see Getting Started)
164160
const payload = chat.encryptMessage({
165-
messageId,
166-
senderId,
167161
conversationId,
168-
conversationKey: rawConvKey,
169162
text: caption || '',
163+
conversationKey: rawConvKey,
170164
conversationKeyVersion,
171-
signingKeyVersion,
172165
attachments: [{
173-
attachmentType: 'media',
174-
mediaHashKey: mediaHashKey,
166+
attachment_type: 'media',
167+
media_hash_key: mediaHashKey,
175168
width,
176169
height,
177-
filesizeBytes: plaintext.byteLength,
170+
filesize_bytes: plaintext.byteLength,
178171
filename: 'photo.jpg',
179172
}],
180173
});
181174
await client.chat.sendMessage(conversationId.replace(/:/g, '-'), {
182-
message_id: messageId,
175+
message_id: payload.messageId, // generated by the SDK
183176
encoded_message_create_event: payload.encryptedContent,
184177
encoded_message_event_signature: payload.encodedEventSignature,
185178
});
186179
```
187180
</Tab>
188181
<Tab title="Rust">
189182
```rust
190-
// Set attachments on EncryptMessageParams per chat_xdk_core AttachmentDescriptor::Media
191-
let payload = chat.encrypt_message(params_with_media_attachment)?;
183+
use chat_xdk_core::{AttachmentDescriptor, EncryptMessageParams};
184+
185+
// chat has keys loaded and set_identity called (see Getting Started)
186+
let mut params = EncryptMessageParams::new(&conversation_id, caption)
187+
.with_conversation_key(conv_key.to_bytes(), &conversation_key_version);
188+
params.attachments = Some(vec![AttachmentDescriptor::Media {
189+
media_hash_key: media_hash_key.clone(),
190+
width,
191+
height,
192+
filesize_bytes: plaintext.len() as i64,
193+
filename: "photo.jpg".into(),
194+
media_type: None,
195+
duration_millis: None,
196+
}]);
197+
let payload = chat.encrypt_message(params)?;
192198
let body = serde_json::json!({
193-
"message_id": message_id,
199+
"message_id": payload.message_id, // generated by the SDK
194200
"encoded_message_create_event": payload.encrypted_content,
195201
"encoded_message_event_signature": payload.encoded_event_signature,
196202
});
@@ -203,10 +209,12 @@ Encrypt with a media attachment, then POST the send-message body (same field map
203209
</Tab>
204210
<Tab title="Go">
205211
```go
212+
// chat has keys loaded and SetIdentity called (see Getting Started)
206213
payload, err := chat.EncryptMessage(chatxdk.EncryptMessageParams{
207-
MessageID: messageID, SenderID: senderID, ConversationID: conversationID,
208-
ConversationKey: rawConvKey, Text: caption,
209-
ConversationKeyVersion: conversationKeyVersion, SigningKeyVersion: signingKeyVersion,
214+
ConversationID: conversationID,
215+
Text: caption,
216+
ConversationKey: rawConvKey,
217+
ConversationKeyVersion: conversationKeyVersion,
210218
Attachments: []chatxdk.AttachmentDescriptor{{
211219
AttachmentType: "media",
212220
MediaHashKey: mediaHashKey,
@@ -216,42 +224,44 @@ Encrypt with a media attachment, then POST the send-message body (same field map
216224
Filename: "photo.jpg",
217225
}},
218226
})
219-
// POST payload.EncryptedContent / EncodedEventSignature to /2/chat/conversations/{id}/messages
227+
// POST payload.MessageID (generated by the SDK), payload.EncryptedContent,
228+
// and payload.EncodedEventSignature to /2/chat/conversations/{id}/messages
220229
```
221230
</Tab>
222231
<Tab title="C#">
223232
```csharp
224-
var payload = chat.EncryptMessage(new EncryptMessageParams {
225-
MessageId = messageId,
226-
SenderId = senderId,
227-
ConversationId = conversationId,
233+
// chat has keys loaded and SetIdentity called (see Getting Started)
234+
var payload = chat.EncryptMessage(new EncryptMessageParams(conversationId, caption ?? "")
235+
{
228236
ConversationKey = rawConvKey,
229-
Text = caption ?? "",
230237
ConversationKeyVersion = conversationKeyVersion,
231-
SigningKeyVersion = signingKeyVersion,
232-
// Attachments = media descriptor with MediaHashKey, Width, Height,
233-
// FilesizeBytes, and Filename (as in the Go tab above)
238+
Attachments = new[]
239+
{
240+
AttachmentDescriptor.Media(mediaHashKey, width, height, plaintext.Length, "photo.jpg"),
241+
},
234242
});
235-
// POST EncryptedContent / EncodedEventSignature as for text messages
243+
// POST payload.MessageId (generated by the SDK), payload.EncryptedContent,
244+
// and payload.EncodedEventSignature as for text messages
236245
```
237246
</Tab>
238247
<Tab title="Java">
239248
```java
240-
EncryptMessageParams params = new EncryptMessageParams();
241-
params.messageId = messageId;
242-
params.senderId = senderId;
243-
params.conversationId = conversationId;
249+
// chat has keys loaded and setIdentity called (see Getting Started)
250+
EncryptMessageParams params =
251+
new EncryptMessageParams(conversationId, caption != null ? caption : "");
244252
params.conversationKey = rawConvKey;
245-
params.text = caption != null ? caption : "";
246253
params.conversationKeyVersion = conversationKeyVersion;
247-
params.signingKeyVersion = signingKeyVersion;
248-
// params.attachments — media type with mediaHashKey, width, height, filename
254+
params.attachments = List.of(AttachmentDescriptor.media(
255+
mediaHashKey, width, height, plaintext.length, "photo.jpg", null, null));
249256
SendPayload payload = chat.encryptMessage(params);
250-
// POST to /2/chat/conversations/{id}/messages
257+
// POST payload.messageId (generated by the SDK), payload.encryptedContent,
258+
// and payload.encodedEventSignature to /2/chat/conversations/{id}/messages
251259
```
252260
</Tab>
253261
</Tabs>
254262

263+
The conversation key pair can be omitted entirely: with `set_cache_keys(true)` enabled, `encrypt_message` resolves the key and version from the conversation's latest verified key change (see [Getting Started](/xchat/getting-started)).
264+
255265
---
256266

257267
## Download and decrypt

0 commit comments

Comments
 (0)