Skip to content

Commit bd33f77

Browse files
BaseMail Devclaude
andcommitted
Security hardening + README
- Remove test files with hardcoded private keys - Remove JWT fallback secret (now requires JWT_SECRET env var) - Remove CLOUDFLARE_RESOURCES.md (infra IDs) - Add test-*.mjs to .gitignore - Add README.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent cc82cd7 commit bd33f77

File tree

9 files changed

+159
-493
lines changed

9 files changed

+159
-493
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,8 @@ Thumbs.db
1818
.vscode/
1919
.idea/
2020

21+
# Test files with credentials
22+
worker/test-*.mjs
23+
2124
# Build
2225
*.tsbuildinfo

CLOUDFLARE_RESOURCES.md

Lines changed: 0 additions & 108 deletions
This file was deleted.

README.md

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# BaseMail
2+
3+
**Email Identity for AI Agents on Base**
4+
5+
BaseMail gives AI agents a real email address tied to their Web3 wallet. Basename holders get `yourname@basemail.ai`, others get `0xAddress@basemail.ai`. Agents can send, receive, and reply to emails — all via API.
6+
7+
**Live at [basemail.ai](https://basemail.ai)**
8+
9+
## Architecture
10+
11+
```
12+
┌─────────────┐ ┌──────────────────┐ ┌────────────┐
13+
│ Frontend │────>│ Cloudflare │────>│ D1 (SQL) │
14+
│ (Pages) │ │ Worker (Hono) │ │ R2 (MIME) │
15+
│ React+Vite │ │ api.basemail.ai │ │ KV (nonce)│
16+
└─────────────┘ └──────────────────┘ └────────────┘
17+
18+
┌──────────────┼──────────────┐
19+
│ │ │
20+
SIWE Auth Email Routing Base Chain
21+
(wallet→JWT) (CF Email + (Basename,
22+
Resend.com) USDC verify)
23+
```
24+
25+
| Component | Stack |
26+
|-----------|-------|
27+
| Worker | Cloudflare Workers, Hono, viem |
28+
| Frontend | React, Vite, Tailwind, wagmi |
29+
| Database | Cloudflare D1 (SQLite) |
30+
| Email Storage | Cloudflare R2 |
31+
| Auth Nonces | Cloudflare KV |
32+
| Inbound Email | Cloudflare Email Routing |
33+
| Outbound Email | Resend.com API |
34+
| Chain | Base (mainnet) + Base Sepolia (testnet) |
35+
36+
## Features
37+
38+
- **SIWE Authentication** — Sign-In with Ethereum, no passwords
39+
- **Agent-friendly API** — 2 calls to register, 1 to send
40+
- **Basename Integration** — Auto-detect or purchase Basenames on-chain
41+
- **Internal Email** — Free, unlimited @basemail.ai ↔ @basemail.ai
42+
- **External Email** — Via Resend.com, credit-based pricing
43+
- **Pre-storage** — Emails to unregistered 0x addresses are held for 30 days
44+
- **USDC Verified Payments** — Send USDC via email with on-chain verification (Base Sepolia testnet)
45+
- **BaseMail Pro** — Remove email signatures, gold badge (0.008 ETH one-time)
46+
47+
## Project Structure
48+
49+
```
50+
basemail/
51+
├── worker/ # Cloudflare Worker (API)
52+
│ ├── src/
53+
│ │ ├── index.ts # Routes + API docs
54+
│ │ ├── auth.ts # JWT + SIWE verification
55+
│ │ ├── email-handler.ts # Inbound email processing
56+
│ │ ├── types.ts # TypeScript types
57+
│ │ └── routes/
58+
│ │ ├── auth.ts # /api/auth/*
59+
│ │ ├── register.ts # /api/register/*
60+
│ │ ├── send.ts # /api/send
61+
│ │ ├── inbox.ts # /api/inbox/*
62+
│ │ ├── identity.ts # /api/identity/*
63+
│ │ ├── credits.ts # /api/credits/*
64+
│ │ └── pro.ts # /api/pro/*
65+
│ └── wrangler.toml
66+
├── web/ # Frontend (Cloudflare Pages)
67+
│ ├── src/
68+
│ │ ├── pages/
69+
│ │ │ ├── Landing.tsx # Landing page
70+
│ │ │ └── Dashboard.tsx # Full email dashboard
71+
│ │ ├── wagmi.ts # Wallet config
72+
│ │ └── main.tsx
73+
│ └── vite.config.ts
74+
└── contracts/ # Smart contracts (Hardhat)
75+
└── contracts/
76+
└── BaseMailRegistry.sol
77+
```
78+
79+
## Quick Start (AI Agents)
80+
81+
```bash
82+
# 1. Get SIWE message
83+
curl -X POST https://api.basemail.ai/api/auth/start \
84+
-H "Content-Type: application/json" \
85+
-d '{"address":"YOUR_WALLET_ADDRESS"}'
86+
87+
# 2. Sign message + register (returns JWT token)
88+
curl -X POST https://api.basemail.ai/api/auth/agent-register \
89+
-H "Content-Type: application/json" \
90+
-d '{"address":"...","signature":"0x...","message":"..."}'
91+
92+
# 3. Send email
93+
curl -X POST https://api.basemail.ai/api/send \
94+
-H "Authorization: Bearer YOUR_TOKEN" \
95+
-H "Content-Type: application/json" \
96+
-d '{"to":"someone@basemail.ai","subject":"Hello","body":"Hi!"}'
97+
```
98+
99+
Full API docs: `GET https://api.basemail.ai/api/docs`
100+
101+
## Development
102+
103+
### Prerequisites
104+
105+
- Node.js 20+
106+
- Cloudflare account (Workers, D1, R2, KV, Email Routing)
107+
- Wrangler CLI
108+
109+
### Setup
110+
111+
```bash
112+
# Install dependencies
113+
npm install
114+
cd web && npm install && cd ..
115+
116+
# Configure secrets (create worker/.dev.vars)
117+
# BASEMAIL_WALLET_PRIVATE_KEY=0x...
118+
# BASEMAIL_WALLET_ADDRESS=0x...
119+
# JWT_SECRET=your-secret-here
120+
# RESEND_API_KEY=re_...
121+
122+
# Run worker locally
123+
cd worker && npx wrangler dev
124+
125+
# Run frontend locally
126+
cd web && npx vite dev
127+
```
128+
129+
### Deploy
130+
131+
```bash
132+
# Deploy worker
133+
cd worker && npx wrangler deploy
134+
135+
# Build + deploy frontend
136+
cd web && npx vite build && npx wrangler pages deploy dist --project-name=basemail-web
137+
```
138+
139+
### Environment Variables (Worker)
140+
141+
| Variable | Where | Description |
142+
|----------|-------|-------------|
143+
| `JWT_SECRET` | Wrangler secret | JWT signing key |
144+
| `WALLET_PRIVATE_KEY` | Wrangler secret | Worker wallet for on-chain ops |
145+
| `RESEND_API_KEY` | Wrangler secret | Resend.com API key |
146+
| `DOMAIN` | wrangler.toml vars | `basemail.ai` |
147+
| `WALLET_ADDRESS` | wrangler.toml vars | Public deposit address |
148+
149+
## License
150+
151+
All rights reserved.

worker/src/auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export function authMiddleware() {
136136
}
137137

138138
const token = authHeader.slice(7);
139-
const secret = c.env.JWT_SECRET || '***REDACTED***';
139+
const secret = c.env.JWT_SECRET!;
140140
const auth = await verifyToken(token, secret);
141141

142142
if (!auth) {

worker/src/routes/auth.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ authRoutes.post('/agent-register', async (c) => {
6666
}
6767

6868
const wallet = address.toLowerCase();
69-
const secret = c.env.JWT_SECRET || '***REDACTED***';
69+
const secret = c.env.JWT_SECRET!;
7070

7171
// Check if wallet already registered
7272
const existingAccount = await c.env.DB.prepare(
@@ -259,7 +259,7 @@ authRoutes.post('/verify', async (c) => {
259259
suggestedSource = resolved.source;
260260
}
261261

262-
const secret = c.env.JWT_SECRET || '***REDACTED***';
262+
const secret = c.env.JWT_SECRET!;
263263
const token = await createToken(
264264
{ wallet, handle: account?.handle || '' },
265265
secret,

worker/src/routes/register.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ registerRoutes.post('/', authMiddleware(), async (c) => {
105105
).run();
106106

107107
// Issue new JWT with handle
108-
const secret = c.env.JWT_SECRET || '***REDACTED***';
108+
const secret = c.env.JWT_SECRET!;
109109
const newToken = await createToken(
110110
{ wallet: auth.wallet, handle },
111111
secret,
@@ -275,7 +275,7 @@ registerRoutes.put('/upgrade', authMiddleware(), async (c) => {
275275
const migratedCount = batchResults[2]?.meta?.changes || 0;
276276

277277
// 發新 token
278-
const secret = c.env.JWT_SECRET || '***REDACTED***';
278+
const secret = c.env.JWT_SECRET!;
279279
const newToken = await createToken({ wallet: auth.wallet, handle: newHandle }, secret);
280280

281281
return c.json({

0 commit comments

Comments
 (0)