A decentralized email app where you can send encrypted messages between Ethereum addresses.
Messages are end-to-end encrypted, stored on IPFS, and metadata is recorded on the Ethereum blockchain.
- Send & receive messages between Ethereum wallet addresses
- End-to-end encryption with perfect forward secrecy (each message uses a fresh key)
- Message content stored privately on IPFS (via Pinata)
- Metadata (sender, recipient, timestamp, IPFS link) saved on-chain
- Beautiful desktop app built with Electron + React
- You generate a personal X25519 key pair in the app.
- Your public key is saved on the blockchain (via KeyRegistry contract).
- When sending a message:
- The app creates a temporary (ephemeral) key just for that message.
- Encrypts the content so only the recipient can read it.
- Uploads the encrypted message to IPFS.
- The recipient decrypts it using their private key + the temporary public key you shared.
→ Even if someone's long-term key is compromised later, old messages stay private.
Note: You cannot read messages you sent to others (this is by design for maximum privacy).
From the repo root:
npm installCreate the app env file and set contract addresses and RPC URLs (you’ll fill in the contract addresses after deploying in Step 5):
cp packages/app/.env.example packages/app/.envEdit packages/app/.env. For local development you’ll typically use:
# Contract addresses (update after Step 5 with the addresses printed by the deploy script)
VITE_CONTRACT_ADDRESS=0x5FbDB2315678afecb367f032d93F642f64180aa3
VITE_KEY_REGISTRY_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
# Local Hardhat node
VITE_RPC_URL=http://127.0.0.1:8545
VITE_WS_URL=ws://127.0.0.1:8545
# Pinata IPFS (get from https://app.pinata.cloud)
VITE_PINATA_JWT=your_pinata_jwt_here
VITE_PINATA_GATEWAY=your_gateway_subdomain.mypinata.cloudIn a first terminal, start the Hardhat node:
npm run contracts:nodeLeave this running. The node will listen at http://127.0.0.1:8545.
In a second terminal, from the repo root, deploy to the local node:
npm run contracts:deploy:localThe script will print something like:
BlockMail deployed to: 0x... chainId: 31337
KeyRegistry deployed to: 0x... chainId: 31337Copy the BlockMail and KeyRegistry addresses from the deploy output into packages/app/.env:
VITE_CONTRACT_ADDRESS= BlockMail addressVITE_KEY_REGISTRY_ADDRESS= KeyRegistry address
Save the file.
In the same second terminal (or a new one), from the repo root:
npm run app:startThe BlockMail desktop app window should open. You can use the local Hardhat accounts (and their private keys in the Hardhat output) to connect and test.
packages/app→ The Electron + React desktop applicationpackages/contracts→ Solidity smart contracts & deployment scripts
- Avatars: Profile/placeholder avatars in the app are from alohe/avatars — a collection of diverse free avatar images (MIT license).
This project is licensed under the MIT License - see the LICENSE file for details.


