Skip to main content

Crypto Overview

Packet messages are end-to-end encrypted. The crypto layer is fully separate from the on-chain program — you encrypt before sending and decrypt after fetching. The PacketClient exposes encryption through the client.crypto accessor, which returns a PacketEncryptionClient.

How it works

  1. A random 256-bit content key is generated for each message.
  2. The plaintext is encrypted with AES-256-GCM (or XSalsa20-Poly1305) using that key.
  3. For each intended reader, the content key is wrapped (encrypted) using the reader's public key and their key algorithm.
  4. The encrypted body — ciphertext plus all wrapped keys — is stored as a JSON blob, either inline on-chain or off-chain via IPFS/Irys/Arweave.

To decrypt, a reader finds their wrapped key entry, unwraps the content key with their private key, and decrypts the ciphertext.

Key algorithms

Two asymmetric algorithms are supported for wrapping the content key:

AlgorithmConstantKey derivationBody encryption
X25519AsymmetricEncryptionAlgorithm.X25519HKDF-SHA256AES-256-GCM
Solana wallet-derivedAsymmetricEncryptionAlgorithm.SOLANA_ED25519_X25519NaCl Box BeforeXSalsa20-Poly1305

SOLANA_ED25519_X25519 lets a Solana wallet sign messages and derive an X25519 key from its Ed25519 key, so the same wallet used for transactions can also decrypt messages without a separate key pair.

Setting up an identity

Before encrypting or decrypting, set an identity on the client. There are three helpers depending on your environment:

Node / server-side (keypair available)

client.useSolanaCryptoKeypair(keypair);

Uses the raw Keypair directly. Not available for browser wallets — they don't expose secret keys.

Browser wallet (password-based)

const derived = await client.useWalletPasswordCrypto({
password: "my-password",
signMessage: wallet.signMessage, // from your wallet adapter
});

Derives a deterministic X25519 identity from password + wallet address + a signed message. The user's wallet never exposes its secret key.

Manual

import { AsymmetricEncryptionAlgorithm } from "xpkt-sdk";

client.useCrypto({
ownerWallet: myPublicKey,
keyAlg: AsymmetricEncryptionAlgorithm.X25519,
publicKey: myX25519PublicKey,
privateKey: myX25519PrivateKey,
});

Typical flow

// 1. Set up identity (once per session)
client.useSolanaCryptoKeypair(keypair);

// 2. Encrypt — self is auto-included as a reader by default
const content = await client.crypto.encryptToContent({
plaintext: "Hello!",
readers: [
client.crypto.reader({
ownerWallet: recipientAddress,
keyAlg: AsymmetricEncryptionAlgorithm.SOLANA_ED25519_X25519,
publicKey: recipientPublicKeyBytes,
}),
],
});

// 3. Send the encrypted content as a normal text message
await thread.sendMessage({
messageType: MessageType.Text,
content,
});

// 4. Fetch the message and decrypt
const msg = await thread.loadLastMessage();
const loaded = await msg.loadContent();
const result = await client.crypto.maybeDecrypt(loaded.text!);

if (result.encrypted) {
console.log(result.plaintext);
}

The full method reference is in Encrypt & Decrypt. Type definitions are in Crypto Types.