πŸ” End-to-End Encrypted Β· On-Chain Verifiable

Bitcoin-native
Encrypted Messaging

Wallet-to-wallet messages secured by ECDH P-256 key exchange and AES-256-GCM encryption. Your keys never leave your browser. Proofs live on Bitcoin.

πŸ”‘ECDH P-256
πŸ›‘οΈAES-256-GCM
⛓️On-chain proofs
β‚ΏBitcoin L2
S

satoshi.btc

πŸ”’ E2E encrypted

ser, is this actually encrypted?
yeah β€” ECDH P-256 + AES-256-GCM per message πŸ”
so the relay can't read our messages?
server only sees {ciphertextHex, nonceHex}. that's it.
πŸ™ finally. gm
β‚ΏAnchored on Bitcoin
Architecture

Off-chain speed. On-chain truth.

Messages relay instantly via Redis and Server-Sent Events. Proofs are anchored permanently to Bitcoin via OPNet smart contracts.

⚑ Off-chain relay (fast path)
AliceBitcoin Wallet
ECDH EncryptAES-256-GCM
Next.js RelayRedis / Vercel KV
SSE Push~2s latency
BobBitcoin Wallet

7-day message TTL in Redis Β· Messages delivered in real-time via EventSource

⛓️ On-chain proofs (optional, permanent)
AliceBitcoin Wallet
anchorReceipt()hash + seq + convId
MessageReceiptsAssemblyScript
Bitcoin L2OPNet β€” forever

Immutable receipt on-chain Β· verifyReceipt(hash) β†’ bool β€” independently auditable

Encryption

End-to-End Encrypted

ECDH P-256 key agreement + AES-256-GCM authenticated encryption. The relay server never receives or stores plaintext β€” not even the operators can read your messages.

01
Key GenerationP-256 ECDH

Alice and Bob each generate a P-256 keypair locally in the browser using Web Crypto API. Private keys are stored as JWK in localStorage. Public keys are uploaded to the ProfileRegistry smart contract on Bitcoin L2.

02
ECDH Key ExchangeWeb Crypto API

Alice fetches Bob's public key from the on-chain ProfileRegistry. Using ECDH, she derives a shared secret from her private key and Bob's public key β€” the shared secret is never transmitted.

03
AES-256-GCM EncryptionAES-256-GCM

The shared secret seeds an AES-256-GCM cipher. Each message gets a cryptographically random 12-byte nonce. The relay stores only {ciphertextHex, nonceHex} β€” it never sees plaintext.

04
Relay via SSEServer-Sent Events

Encrypted payloads are POSTed to /api/messages/send and stored in Redis (inbox:{address}). Bob subscribes to /api/messages/stream via Server-Sent Events and receives new messages in real-time.

05
DecryptionZero-Knowledge Relay

Bob derives the same shared secret using his private key + Alice's public key (ECDH is symmetric). He decrypts each ciphertext with the stored nonce. The relay never saw plaintext β€” zero knowledge relay.

Key Exchange Diagram

Alice
privKey_A
pubKey_A β†’
Bob
privKey_B
← pubKey_B
↓ ECDH(privKey_A, pubKey_B) == ECDH(privKey_B, pubKey_A)
sharedSecret
never transmitted
↓ AES-256-GCM key + random 12-byte nonce
Encrypted payload
{
ciphertextHex: "0x...",
nonceHex: "0x..."
}
POST /api/messages/send β†’ Redis inbox
↓ SSE stream
Bob decrypts with privKey_B + pubKey_A
On-Chain

Anchored on Bitcoin

Two AssemblyScript smart contracts deployed to OPNet (Bitcoin L2) handle identity registration and immutable message receipt proofs.

πŸ‘€

ProfileRegistry

AssemblyScript / OPNet

Registers wallet identities with usernames and ECDH P-256 public keys (65 bytes). Enables wallet-to-wallet contact discovery and key lookup without a centralised server.

registerProfile(
username: bytes32,
messagingKey: bytes65,
metadataURI: string
)
lookupByUsername(u) β†’ address
getProfile(addr) β†’ Profile
Set NEXT_PUBLIC_PROFILE_REGISTRY_ADDRESS
πŸ“œ

MessageReceipts

AssemblyScript / OPNet

Anchors message hashes to Bitcoin permanently. Any party can call verifyReceipt(hash) to confirm a specific message existed at a given block β€” immutable, censorship-resistant proof.

anchorReceipt(
messageHash: bytes32,
recipient: address,
convId: bytes32,
seq: u64
)
verifyReceipt(hash) β†’ bool
getReceipt(hash) β†’ Receipt
Set NEXT_PUBLIC_MESSAGE_RECEIPTS_ADDRESS

The β€œAnchored on Bitcoin” badge

just anchored this one πŸ™Œ
β‚ΏAnchored on Bitcoin

When a message receipt is anchored via anchorReceipt(), the chat view shows this badge beneath the message with a link to the on-chain transaction. Anyone can independently verify the message hash existed at that block height β€” no trust required.

Customize

Your vibe, your rules

Five themes, four background textures, and six notification sounds β€” all configurable instantly from the settings panel.

🎨 Themes

gm ser πŸ‘‹
encrypted as always πŸ”
Serika Dark
Warm charcoal Β· golden yellow
gm ser πŸ‘‹
encrypted as always πŸ”
Nord
Arctic blue Β· frost white
landing page
gm ser πŸ‘‹
encrypted as always πŸ”
Dracula
Dark purple Β· violet accent
gm ser πŸ‘‹
encrypted as always πŸ”
Catppuccin
Deep navy Β· soft lavender
gm ser πŸ‘‹
encrypted as always πŸ”
HODL
Warm parchment Β· burnt orange

πŸ”” Notification Sounds

None

Pure silence. Very mysterious. Very encrypted.

Discord

Pavlov spent years conditioning you for this exact moment.

default
Messenger

The satisfying swoosh of a blue double-tick.

MSN

Hello, is it 2005? Your crush just sent you a Nudgeβ„’

MySpace

Tom says hi πŸ‘‹ β€” he was always your first friend.

Telegram

Crisp, efficient, vaguely European.

Browser autoplay policy requires one user interaction before sound previews work.

Built With

Tech Stack

Next.js 15React 19Tailwind CSSOPNetAssemblyScriptBitcoin L2Vercel KV (Redis)P-256 ECDHAES-256-GCMWeb Crypto APIServer-Sent EventsOPWallet