ENC Wallet Extension
The ENC Wallet Extension is a Chrome (MV3) browser extension that custodies a user's ENC
identity and signs on behalf of apps — so an app never touches the private key. It's a BIP-39
seed-phrase HD wallet whose identity is an x-only secp256k1 (ENC Schnorr) key. It injects a
window.enc provider any ENC web app connects to through a standard Connect-Wallet flow, and is
also NIP-07-compatible (mirrored as window.nostr). No ETH, no EVM — ENC-native crypto only.
Connecting from an app
// the provider is injected on page load; wait for it if it isn't there yet
if (!window.enc) await new Promise(r => window.addEventListener('encReady', r, { once: true }))
const { approved, pubKey } = await window.enc.connect() // first call shows an approval popup
if (approved) console.log('ENC identity:', pubKey) // x-only 32-byte hexOnce connected, signing and encryption calls run without a popup (the user approved the
origin); only connect() prompts.
The window.enc provider
Connection & accounts
| Method | Returns | Notes |
|---|---|---|
connect() | { approved, pubKey } | approval popup on first use |
getPublicKey() | string | x-only 32-byte hex (the ENC identity) |
isConnected() | boolean | |
getAccounts() | { accounts: [{ index, pubKey }] } | HD-derived + imported accounts |
switchAccount(index) | { pubKey } | emits accountChanged |
getVersion() | string |
Signing (no popup)
const sig = await window.enc.signSchnorr(hashHex) // BIP-340 over a 32-byte hash → signature hexThis is how an app gets a commit signed: build the commit hash, ask the extension to
signSchnorr it, and attach the signature. The key never leaves the extension.
Session keys (no popup)
const { session, sessionPriv, expires } = await window.enc.createSession(7200) // secondsA short-lived session key for WebSocket auth — see the session model.
ECDH & encryption (no popup)
const shared = await window.enc.ecdh(peerPubHex) // 32-byte hex
const key = await window.enc.deriveKey(shared, 'enc:dm:a-b') // HKDF → 32-byte hex
const ct = await window.enc.encrypt(key, 'secret') // XChaCha20-Poly1305 → base64
const pt = await window.enc.decrypt(key, ct)The same primitives as @enc-protocol/core, but computed inside
the extension against the custodied key.
Personal (owner-only) encryption (no popup)
const env = await window.enc.encryptPersonal(enclaveId, 'private note') // → { ciphertext, nonce }
const pt = await window.enc.decryptPersonal(enclaveId, env)Owner-only confidentiality keyed by your identity — the spec's
identity-aead plugin. The content key is
HKDF(identity_priv, "enc-personal-private:" + enclaveId), computed inside the extension: there's
no recipient, the key never leaves, and any device with this identity re-derives the same key.
Byte-identical to the SDK's identity-aead, so the same data round-trips with a key-holding
client (CLI, passkey).
Events
window.enc.on('connect', ({ pubKey }) => {})
window.enc.on('disconnect', () => {})
window.enc.on('accountChanged', ({ pubKey, accountIndex }) => {})The seed phrase and private keys are never exposed to the page — there is no exportSeed or
exportPrivKey method. They can only be revealed inside the extension popup after re-entering the
wallet password.
Install for development
The extension ships as a built zip. To run it locally:
- Build the zip (
yarn build) and unzip it somewhere stable. - Open
chrome://extensions, enable Developer Mode, click Load unpacked, and select the extractedextension-chrome/folder. - Reload any open ENC app — the Connect-Wallet row should now show the extension as available.
The content script injects window.enc on every page, so any ENC web app can detect and
connect to it.
See also
- Building with the SDK — pair the signer with the client SDK
- Identity & custody ·
corecrypto