Full documentation on the OutLayer dashboard.
Blockchain-native email for NEAR accounts. Every NEAR account automatically has an email address: alice.near -> alice@near.email
External World (Gmail, etc.)
│
│ SMTP (port 25)
▼
┌─────────────────────────────────────────┐
│ smtp-server (Rust) │
│ - Receives *@near.email │
│ - Derives public key from account_id │
│ - Encrypts email with ECIES │
│ - Stores encrypted blob in PostgreSQL │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ PostgreSQL │
│ - Encrypted emails (only owner decrypt)│
└─────────────────────────────────────────┘
│
│ HTTP API
▼
┌─────────────────────────────────────────┐
│ OutLayer TEE │
│ - Verifies NEAR signature │
│ - Derives private key from master │
│ - Decrypts emails for owner │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ web-ui (Next.js) │
│ - Connect NEAR wallet │
│ - Sign message to prove ownership │
│ - View decrypted inbox │
└─────────────────────────────────────────┘
| Directory | Description |
|---|---|
smtp-server/ |
Rust SMTP server that receives and encrypts emails |
wasi-near-email-ark/ |
OutLayer WASI module for decryption |
db-api/ |
HTTP API for database access from WASI |
web-ui/ |
Next.js frontend for inbox |
The critical feature: SMTP server can derive public keys WITHOUT knowing the master secret.
Master Keypair (generated once in TEE):
master_private_key → stored in OutLayer secrets
master_public_key → published, available to SMTP server
Public Key Derivation (SMTP server, no secret needed):
user_pubkey = master_pubkey + SHA256("near-email:v1:" + account_id) * G
Private Key Derivation (OutLayer TEE, requires secret):
user_privkey = master_privkey + SHA256("near-email:v1:" + account_id)
# 2. Set environment variable
export MASTER_PUBLIC_KEY=<from wasi>
# 3. Run everything
docker-compose up -dcd smtp-server
cp .env.example .env
# Edit .env with your settings
cargo runcd db-api
DATABASE_URL=postgres://... cargo runcd wasi-near-email-ark
./build.sh
# Deploy to OutLayer with secrets:
# PROTECTED_MASTER_KEY=<auto-generated-in-tee>...
# DATABASE_API_URL=https://your-db-api-urlcd web-ui
npm install
npm run devThe web UI supports two modes of operation:
- Each operation sends a NEAR blockchain transaction
- Authenticated via wallet signature
- A NEAR deposit per operation, returned less the run's cost when it completes: the compute estimate (about 0.015 NEAR), plus 0.00001 NEAR per byte of the answer received (when it is 1 KB or more) and of the email sent above 32 KB. The OutLayer contract holds those bytes in its state while the request runs, and the deposit guards it against spam. Inbox, send and delete answers come in pages of up to 30 000 bytes (about 0.35 NEAR held); an attachment download holds about 10 NEAR per MB of attachment until it completes
- Limited to ~1.5 MB response size and ~0.8 MB of attachments to send (one transaction carries at most 1.5 MiB)
- Operations via HTTPS API (no blockchain transactions)
- Authenticated via Payment Key
- Costs deducted from prepaid USDC/USDT balance
- 25 MB response size (larger attachments)
- Faster (no transaction confirmation wait)
-
Get a Payment Key from OutLayer Dashboard:
- Create a new Payment Key
- Top up balance with USDC/USDT
- Copy the key (format:
owner:nonce:secret)
-
Configure in web UI:
- Click on your account dropdown
- Click "Configure" under Payment Key section
- Paste your key
- Toggle "Payment Key" switch ON
-
Switch between modes:
- Toggle ON: Uses HTTPS API (Payment Key balance)
- Toggle OFF: Uses blockchain transactions (NEAR wallet)
| Parameter | Transaction | Payment Key |
|---|---|---|
| max_output_size | 1.5 MB | 25 MB |
| max_memory_mb | 128 | 256 |
| max_instructions | 100M | 500M |
| max_execution_seconds | 60 | 120 |
- Payment Key stored in browser
localStorage - Key and toggle state stored separately
- Never sent to any server except OutLayer API
| Variable | Description |
|---|---|
DATABASE_URL |
PostgreSQL connection string |
MASTER_PUBLIC_KEY |
Hex-encoded secp256k1 public key |
SMTP_PORT |
SMTP listen port (default: 25) |
| Variable | Description |
|---|---|
PROTECTED_MASTER_KEY |
Hex-encoded secp256k1 private key, generated in tee |
DATABASE_API_URL |
HTTP API URL for database access |
| Variable | Description |
|---|---|
NEXT_PUBLIC_NETWORK_ID |
NEAR network: mainnet or testnet |
NEXT_PUBLIC_OUTLAYER_API_URL |
OutLayer API URL for HTTPS mode |
NEXT_PUBLIC_PROJECT_ID |
OutLayer project ID (e.g., zavodil2.testnet/near-email) |
NEXT_PUBLIC_SECRETS_PROFILE |
Secrets profile name (default: default) |
NEXT_PUBLIC_SECRETS_ACCOUNT_ID |
Account that stores secrets |
near.email. MX 10 mail.near.email.
mail.near.email. A <your-server-ip>
near.email. TXT "v=spf1 ip4:<your-server-ip> -all"
- Server compromise: Emails encrypted, server only has public key
- Master key leak: Key stored only in OutLayer TEE
- Spam: Rate limiting before encryption
- Impersonation: DKIM/SPF/DMARC for outgoing mail
See DEPLOY.md for full deployment guide including:
- VPS setup (Hetzner/OVH/Vultr)
- DNS configuration
- SSL certificates
- OutLayer secrets
- Monitoring and backups
Apache-2.0 — see LICENSE and NOTICE.
Unlike the examples in wasi-examples/, which are dual MIT OR Apache-2.0 so they can be copied
into a proprietary product without an attribution burden, NEAR Email is a finished product and
carries the same licence as the OutLayer platform itself. The reason is Apache-2.0 §3: an express
patent grant with defensive termination, which matters more here than elsewhere because this
codebase implements key derivation and message encryption.