Plugin SDKs
ENC's confidentiality is an application-layer concern: an app seals event content with one of
the protocol's four encryption plugins, picked per data_type. The node only ever sees
opaque ciphertext. Each plugin is a (role, suite) pair with a byte-exact wire-and-key
contract — two implementations produce identical ciphertext — and each is generated from the
Lean specification into @enc-protocol/core.
| Plugin | Role (trust shape) | Suite | Typical use |
|---|---|---|---|
| ratchet-pair | pairwise (1:1) | enc-xchacha-v1 | DMs, 1:1 threads |
| mls-lazy | shared-secret (N-party) | mls-chacha-v1 | group chat, channels |
| identity-aead | single-owner | enc-xchacha-v1 | owner-only private content |
| ecdh-envelope | one-shot directed | enc-xchacha-v1 | invites, addressed notices |
The normative wire-and-key specs live in the encryption plugin catalog.
How a plugin is bound
At runtime an app dispatches encryption through the ClientPluginRegistry's
EnvelopeEncryptFn / EnvelopeDecryptFn slots. An app's generated submit* method calls
_encrypt(dataType, args) with that data_type's plugin; you can override the slot to supply a
custom envelope implementation:
import { defaultClientPluginRegistry } from '@enc-protocol/cli-sdk-base'
const plugins = defaultClientPluginRegistry()
plugins.register('EnvelopeEncryptFn', myEncrypt)
plugins.register('EnvelopeDecryptFn', myDecrypt)Where they're generated
Each plugin's JS is emitted by a Lean DSL module —
spec-lean/Enc/DSL/Modules/{DmRatchet,GroupRatchet,MlsLazy,IdentityAead,EcdhEnvelope}.lean —
proven equivalent to its core spec (Enc/Core/Plugins/*.lean) and emitted into sdk/core/*.js.
They compose only the verified crypto.js primitives (no raw @noble), which is what makes
the wire output byte-identical across implementations.
See also
- Encryption plugin catalog — the normative
(role, suite)specs @enc-protocol/core— the package that ships them