ContextVM encryption in this SDK is controlled by EncryptionMode and GiftWrapMode.
At a high level, direct traffic can be sent as plaintext ContextVM events or as encrypted NIP-44 payloads wrapped in gift-wrap events, depending on the configured policy on both peers.
EncryptionMode has three modes:
Optional: accept both plaintext and encrypted trafficRequired: require encrypted trafficDisabled: reject encrypted traffic and use plaintext only
These semantics are not only conceptual; they are also exercised by the transport integration tests.
GiftWrapMode controls which outer encrypted event kind is used:
Optional: accept both modes and default to persistent wrapping until ephemeral support is learned from the peerEphemeral: use kind21059Persistent: use kind1059
The helper methods allows_kind() and supports_ephemeral() show the expected policy behavior.
Plaintext ContextVM messages use kind 25910 and keep the MCP JSON-RPC payload in the event content.
Encrypted ContextVM messages:
- serialize the MCP payload to JSON
- encrypt it with NIP-44
- wrap it as a gift-wrap event using kind
1059or21059
The implementation details live in the SDK encryption module.
One important implementation detail is that server responses mirror the client’s inbound encryption format when policy allows it.
This behavior is verified by the transport integration tests and is important for interoperable mixed-mode deployments.
Both client and server transports deduplicate encrypted outer gift-wrap event ids before delivering them.
This is covered by the deduplication and encrypted transport integration tests.
use contextvm_sdk::core::types::{EncryptionMode, GiftWrapMode};
use contextvm_sdk::transport::client::NostrClientTransportConfig;
let config = NostrClientTransportConfig::default()
.with_relay_urls(vec!["wss://relay.damus.io".to_string()])
.with_server_pubkey("<server-hex-pubkey>")
.with_encryption_mode(EncryptionMode::Optional)
.with_gift_wrap_mode(GiftWrapMode::Optional);Encryption support is also surfaced through discovery tags and first-message capability learning.
In practice, this matters for:
- public announcements
- the first direct server-to-client message
- stateless operation