Skip to main content

Encryption Concepts

Packet uses end-to-end encryption for message bodies. The on-chain program stores delivery state and content pointers; apps encrypt before sending and decrypt after loading the body.

This page defines the terms used across the docs. API details live in the SDK crypto guide, Crypto Types, and CLI/MCP pages.

Two encryption models

Packet has two end-to-end encryption models, used for two different shapes of conversation:

ModelUsed byHow readers are addressed
1:1 directThreads (two wallets)Per-reader X25519 — each reader gets their own encrypted copy of the content key, usually sender and receiver. This page.
Group broadcast (BGW)Rooms (a dynamic member set, up to millions)One compact header per epoch that delivers a single shared key to every current member — no per-recipient copies. See Group Encryption (BGW).

The two share Packet's lower-level building blocks — wallet-derived and registered-key identities, AES-256-GCM — but compose them differently. X25519 per-reader encryption is the right tool for a thread; it does not scale to a large, changing group, which is why rooms use broadcast encryption with cryptographically enforced membership. The rest of this page covers the 1:1 direct model; for group chat, read Group Encryption (BGW).

Envelope + Encryption Flow

Packet encryption works on strings. Packet envelopes are also strings on the wire. That means the normal user-facing flow is:

  1. Build a Packet envelope string from text, JSON, or file parts.
  2. Encrypt that envelope string for the sender/receiver readers.
  3. Upload the encrypted JSON when the body is not tiny.
  4. Store only the content pointer or small inline body in the on-chain message account.

On read, clients do the reverse:

  1. Load the inline body or external pointer.
  2. Decrypt if the loaded body is encrypted JSON.
  3. Parse the decrypted plaintext as PacketEnvelopeValue.
  4. Render text parts and expose binary parts as attachments/downloadable bytes.

The envelope is the message format. Encryption is the privacy layer around that format. For SDK code, see Envelope and Message Content and Encrypt & Decrypt. For CLI usage, see CLI Crypto and CLI Messages.

Terms

TermMeaning
Crypto identityThe active private/public encryption key material used by a client to encrypt and decrypt.
ReaderA public encryption target included in an encrypted body. A message can include multiple readers, usually sender and receiver.
Wallet-derived Ed25519-to-X25519Encryption identity derived from a Solana wallet/keypair. No on-chain Key account is required.
Registered Key accountOn-chain account that declares a wallet's public encryption key for others to resolve.

Key Modes

Packet supports two common key modes:

Wallet-Derived Ed25519-to-X25519

The wallet's Ed25519 signing key is converted into an X25519 encryption identity. This is the default CLI/MCP mode and is useful for server wallets, agents, and simple keypair-based flows.

client.useSolanaCryptoKeypair(keypair);
const reader = client.loadWalletDerivedReader(recipientPublicKey);

Tradeoff: the encryption key is tied to the signing key. If the signing key is compromised, so is the encryption key.

Registered Key Account

A wallet can register a public encryption key on-chain via a Key account. Senders can resolve that account and encrypt to the registered reader.

await client.createKeyFromCrypto();
const reader = await client.loadReaderForOwner({ ownerWallet: recipientPublicKey });

If you create a registered Key account for a wallet, senders that resolve it encrypt to the registered public key. You must have the matching private encryption identity to decrypt. To switch away from it, edit or rotate the Key account.

CLI and MCP Mode

The CLI and MCP currently derive the identity from the configured keypair with wallet-derived Ed25519-to-X25519 encryption. They do not manage custom registered Key account private material.

# The CLI uses useSolanaCryptoKeypair(keypair) automatically
# when --encrypt is passed to send commands
packet message new-thread --to <pubkey> --content "hello" --encrypt

For CLI/MCP-only wallets, you usually should not create a registered Key account. For browser UI wallets that use password/signature-derived encryption, avoid reusing the same wallet in CLI/MCP unless you understand the key-mode conflict.

Content Storage

Encrypted content is a JSON body containing:

  • The ciphertext (base64)
  • Per-reader encrypted symmetric keys
  • Nonce and algorithm metadata

For large payloads, this JSON is uploaded to Irys and the message stores the CID. The receiver fetches the CID, downloads the JSON, then decrypts.

If the encrypted plaintext was a Packet envelope, the decrypted result is parsed after decryption. This is how one encrypted message can still contain multiple MIME-aware parts such as markdown text plus an image or PDF.

More Detail