Initial registration - First time client is created
Rotation - After Welcome messages or when queued
Generation process:
// 1. Generate key package locallylet NewKeyPackageResult { key_package, pq_pub_key } = identity.new_key_package(&provider, include_post_quantum)?;// 2. Serialize to TLS formatlet kp_bytes = key_package.tls_serialize_detached()?;// 3. Store in local database with history IDlet history_id = conn.insert_key_package_history(kp_bytes.clone())?;// 4. Upload to networkapi_client.upload_key_package(kp_bytes, is_inbox_id_credential).await?;// 5. Mark previous key packages for deletionconn.mark_key_package_before_id_to_be_deleted(history_id)?;
When fetching a key package for another user:MLS validation (OpenMLS):
let kp = kp_in.validate( crypto_provider, MLS_PROTOCOL_VERSION, openmls::prelude::LeafNodeLifetimePolicy::Verify,)?;
XMTP validation:
Extract and decode MLS credential
Fetch identity updates for the inbox
Verify installation key is in the current association state
let installation_key = key_package.leaf_node().signature_key();let state = get_state(identity_updates)?;assert!(state.installation_ids().contains(&installation_key));
// Mark all key packages before the new one for deletionconn.mark_key_package_before_id_to_be_deleted(history_id)?;// Cleaner worker deletes them from key storefor kp in conn.get_expired_key_packages()? { self.delete_key_package( kp.key_package_hash_ref, kp.pq_public_key, )?;}
LibXMTP uses last resort key packages only, meaning the same key package may be used multiple times.
Why does XMTP reuse key packages?
Due to XMTP’s decentralized nature, it is nearly impossible to use truly ephemeral (one-time) key packages.While RFC 9420 Section 10 states key packages SHOULD NOT be reused, XMTP implements aggressive rotation to minimize the window of reuse.
Reusing a key package is equivalent to using the same static key multiple times for encryption. While not inherently insecure, it allows attackers to collect multiple ciphertexts for the same public key.Combined with other factors (weak randomness, implementation bugs), this could enable attacks. XMTP’s rotation protocol minimizes this risk.
Default lifetime:XMTP uses a long lifetime for last resort key packages (typically months to years). Expired key packages are deleted by the cleaner worker.
Applications can trigger key package rotation explicitly:
// Queue rotation to occur soonclient.queue_key_rotation().await?;// Or rotate immediatelyclient.rotate_and_upload_key_package(include_post_quantum).await?;
Manual rotation is rarely needed. The automatic rotation system handles most scenarios.
If upload fails, the locally generated key package is not marked for deletion:
match api_client.upload_key_package(kp_bytes, true).await { Ok(()) => { // Success - mark old packages for deletion conn.mark_key_package_before_id_to_be_deleted(history_id)?; } Err(err) => { // Failure - keep the new package for retry return Err(IdentityError::ApiClient(err)); }}
This prevents orphaned key packages when signature validation fails on the network.