Architecture Overview
System design and technical architecture patterns for scalable email client development.
Zero is built with a modern, scalable architecture that emphasizes performance, maintainability, and user experience. This document provides a comprehensive overview of the system design, technology choices, and architectural patterns.
High-Level Architecture
The architecture follows a microservices pattern with clear separation of concerns between frontend, backend, and external services.
Technology Stack
Frontend Technologies
React 19
UI library with latest features and concurrent rendering
TypeScript
Type-safe JavaScript with enhanced developer experience
React Router v7
Full-stack web framework with Vite integration for routing and SSR
Tailwind CSS
Utility-first CSS framework for rapid styling
Zustand
Lightweight state management without boilerplate
Shadcn UI
Beautifully designed components built with Radix UI and Tailwind CSS
Framer Motion
Production-ready motion library for React
Backend Technologies
Node.js
JavaScript runtime for server-side applications
TypeScript
Type-safe backend development
Hono
Fast, lightweight web framework for Cloudflare Workers
Drizzle ORM
Type-safe database toolkit with excellent TypeScript support
Better Auth
Modern authentication and authorization library
PostgreSQL
Primary database with Docker Compose setup for local development
Cloudflare Workers
Edge computing platform for global distribution
Infrastructure Components
Docker
Containerization for consistent deployments
Cloudflare
CDN, DNS, and edge services
Cloudflare Workers
Serverless runtime for edge computing
Cloudflare Durable Objects
Stateful serverless objects for real-time collaboration
Cloudflare Vectorize
Vector database for AI-powered search and recommendations
Cloudflare AI
AI inference at the edge for smart email features
System Components
The system is organized into distinct components, each handling specific aspects of the email client functionality.
Project Structure
Zero follows a monorepo structure with clear separation between applications, packages, and configuration.
1. Frontend Architecture
State Management
We use Zustand for lightweight, performant state management without the complexity of Redux.
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
interface MailState {
emails: Email[]
selectedEmail: Email | null
filters: FilterState
// Actions
setEmails: (emails: Email[]) => void
selectEmail: (email: Email) => void
updateFilters: (filters: Partial<FilterState>) => void
}
export const useMailStore = create<MailState>()(
devtools(
persist(
(set, get) => ({
emails: [],
selectedEmail: null,
filters: { unread: false, starred: false },
setEmails: (emails) => set({ emails }),
selectEmail: (email) => set({ selectedEmail: email }),
updateFilters: (filters) =>
set({ filters: { ...get().filters, ...filters } }),
}),
{ name: 'mail-store' }
)
)
)
Component Architecture
import { memo } from 'react'
import { useMailStore } from '@/store/mail-store'
import { EmailListItem } from './email-list-item'
import { VirtualizedList } from '@/components/ui/virtualized-list'
export const EmailList = memo(() => {
const { emails, selectEmail } = useMailStore()
return (
<VirtualizedList
items={emails}
renderItem={(email) => (
<EmailListItem
key={email.id}
email={email}
onSelect={() => selectEmail(email)}
/>
)}
itemHeight={80}
className="flex-1"
/>
)
})
2. Backend Architecture
The backend follows a service-oriented architecture with clear separation between routes, services, and data access layers.
Server Structure
Database Schema
We use Drizzle ORM for type-safe database operations with excellent TypeScript integration.
import {
pgTableCreator,
text,
timestamp,
boolean,
integer,
jsonb,
primaryKey,
unique,
} from 'drizzle-orm/pg-core';
import { defaultUserSettings } from '../lib/schemas';
export const createTable = pgTableCreator((name) => `mail0_${name}`);
export const user = createTable('user', {
id: text('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
emailVerified: boolean('email_verified').notNull(),
image: text('image'),
createdAt: timestamp('created_at').notNull(),
updatedAt: timestamp('updated_at').notNull(),
defaultConnectionId: text('default_connection_id'),
customPrompt: text('custom_prompt'),
phoneNumber: text('phone_number').unique(),
phoneNumberVerified: boolean('phone_number_verified'),
});
export const session = createTable('session', {
id: text('id').primaryKey(),
expiresAt: timestamp('expires_at').notNull(),
token: text('token').notNull().unique(),
createdAt: timestamp('created_at').notNull(),
updatedAt: timestamp('updated_at').notNull(),
ipAddress: text('ip_address'),
userAgent: text('user_agent'),
userId: text('user_id')
.notNull()
.references(() => user.id),
});
export const account = createTable('account', {
id: text('id').primaryKey(),
accountId: text('account_id').notNull(),
providerId: text('provider_id').notNull(),
userId: text('user_id')
.notNull()
.references(() => user.id),
accessToken: text('access_token'),
refreshToken: text('refresh_token'),
idToken: text('id_token'),
accessTokenExpiresAt: timestamp('access_token_expires_at'),
refreshTokenExpiresAt: timestamp('refresh_token_expires_at'),
scope: text('scope'),
password: text('password'),
createdAt: timestamp('created_at').notNull(),
updatedAt: timestamp('updated_at').notNull(),
});
export const userHotkeys = createTable('user_hotkeys', {
userId: text('user_id')
.primaryKey()
.references(() => user.id),
shortcuts: jsonb('shortcuts').notNull(),
createdAt: timestamp('created_at').notNull(),
updatedAt: timestamp('updated_at').notNull(),
});
export const verification = createTable('verification', {
id: text('id').primaryKey(),
identifier: text('identifier').notNull(),
value: text('value').notNull(),
expiresAt: timestamp('expires_at').notNull(),
createdAt: timestamp('created_at'),
updatedAt: timestamp('updated_at'),
});
export const earlyAccess = createTable('early_access', {
id: text('id').primaryKey(),
email: text('email').notNull().unique(),
createdAt: timestamp('created_at').notNull(),
updatedAt: timestamp('updated_at').notNull(),
isEarlyAccess: boolean('is_early_access').notNull().default(false),
hasUsedTicket: text('has_used_ticket').default(''),
});
export const connection = createTable(
'connection',
{
id: text('id').primaryKey(),
userId: text('user_id')
.notNull()
.references(() => user.id),
email: text('email').notNull(),
name: text('name'),
picture: text('picture'),
accessToken: text('access_token'),
refreshToken: text('refresh_token'),
scope: text('scope').notNull(),
providerId: text('provider_id').$type<'google' | 'microsoft'>().notNull(),
expiresAt: timestamp('expires_at').notNull(),
createdAt: timestamp('created_at').notNull(),
updatedAt: timestamp('updated_at').notNull(),
},
(t) => [unique().on(t.userId, t.email)],
);
export const summary = createTable('summary', {
messageId: text('message_id').primaryKey(),
content: text('content').notNull(),
createdAt: timestamp('created_at').notNull(),
updatedAt: timestamp('updated_at').notNull(),
connectionId: text('connection_id').notNull(),
saved: boolean('saved').notNull().default(false),
tags: text('tags'),
suggestedReply: text('suggested_reply'),
});
export const note = createTable('note', {
id: text('id').primaryKey(),
userId: text('user_id')
.notNull()
.references(() => user.id, { onDelete: 'cascade' }),
threadId: text('thread_id').notNull(),
content: text('content').notNull(),
color: text('color').notNull().default('default'),
isPinned: boolean('is_pinned').default(false),
order: integer('order').notNull().default(0),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
});
export const userSettings = createTable('user_settings', {
id: text('id').primaryKey(),
userId: text('user_id')
.notNull()
.references(() => user.id)
.unique(),
settings: jsonb('settings').notNull().default(defaultUserSettings),
createdAt: timestamp('created_at').notNull(),
updatedAt: timestamp('updated_at').notNull(),
});
export const writingStyleMatrix = createTable(
'writing_style_matrix',
{
connectionId: text()
.notNull()
.references(() => connection.id, { onDelete: 'cascade' }),
numMessages: integer().notNull(),
style: jsonb().$type<unknown>().notNull(),
updatedAt: timestamp()
.defaultNow()
.notNull()
.$onUpdate(() => new Date()),
},
(table) => {
return [
primaryKey({
columns: [table.connectionId],
}),
];
},
);
export const jwks = createTable('jwks', {
id: text('id').primaryKey(),
publicKey: text('public_key').notNull(),
privateKey: text('private_key').notNull(),
createdAt: timestamp('created_at').notNull(),
});
API Routes
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
import { requireAuth } from '../lib/auth'
import { OpenAIService } from '../services/openai'
const ai = new Hono()
// Compose email with AI
ai.post('/compose',
requireAuth,
zValidator('json', z.object({
prompt: z.string(),
context: z.string().optional(),
})),
async (c) => {
const userId = c.get('user').id
const { prompt, context } = c.req.valid('json')
const composition = await OpenAIService.composeEmail({
userId,
prompt,
context,
})
return c.json({ composition })
}
)
// Summarize email
ai.post('/summarize',
requireAuth,
zValidator('json', z.object({
content: z.string(),
})),
async (c) => {
const { content } = c.req.valid('json')
const summary = await OpenAIService.summarizeEmail(content)
return c.json({ summary })
}
)
export default ai
3. MCP Service Integration
Zero uses the Model Context Protocol (MCP) for email operations, providing a standardized interface for AI agents to interact with email systems.
MCP Service Architecture
import { MCPServer } from '@modelcontextprotocol/sdk/server.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { ListResourcesRequestSchema } from '@modelcontextprotocol/sdk/types.js'
export class ZeroMCPService {
private server: MCPServer
constructor() {
this.server = new MCPServer(
{
name: 'zero-mcp',
version: '1.0.0',
},
{
capabilities: {
resources: {},
tools: {},
},
}
)
this.setupHandlers()
}
private setupHandlers() {
// Email management tools
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: 'zero://emails',
name: 'Email Management',
description: 'Access and manage emails',
},
{
uri: 'zero://labels',
name: 'Label Management',
description: 'Manage email labels and organization',
},
],
}
})
}
async start() {
const transport = new StdioServerTransport()
await this.server.connect(transport)
}
}
5. Authentication System
We use Better Auth for modern, secure authentication with OAuth providers and session management.
Better Auth Integration
import { betterAuth } from "better-auth"
import { drizzleAdapter } from "better-auth/adapters/drizzle"
import { db } from "../db"
import * as schema from "../db/schema"
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "pg",
schema,
}),
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
scope: [
'email',
'profile',
'https://www.googleapis.com/auth/gmail.readonly',
'https://www.googleapis.com/auth/gmail.send',
'https://www.googleapis.com/auth/gmail.modify'
]
}
},
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // 1 day
},
plugins: [],
})
export const { GET, POST } = auth.handler
4. AI Integration
AI features are powered by multiple providers including OpenAI, Perplexity, and Cloudflare AI for enhanced email functionality.
AI Service Architecture
import { generateText } from 'ai'
import { openai } from '@ai-sdk/openai'
import { perplexity } from '@ai-sdk/perplexity'
export class AIService {
static async composeEmail(params: {
prompt: string
context?: string
style?: 'formal' | 'casual' | 'professional'
}) {
const { prompt, context, style = 'professional' } = params
const systemPrompt = `You are an AI email assistant. Write a ${style} email based on the user's request.`
const { text } = await generateText({
model: openai('gpt-4'),
system: systemPrompt,
prompt: context ? `Context: ${context}\n\nRequest: ${prompt}` : prompt,
maxTokens: 500,
})
return text
}
static async summarizeThread(emails: Array<{ subject: string; body: string }>) {
const emailContent = emails
.map(email => `Subject: ${email.subject}\nBody: ${email.body}`)
.join('\n\n---\n\n')
const { text } = await generateText({
model: openai('gpt-4'),
system: 'Summarize this email thread concisely, highlighting key points and action items.',
prompt: emailContent,
maxTokens: 200,
})
return text
}
static async searchWeb(query: string) {
const { text } = await generateText({
model: perplexity('llama-3.1-sonar-small-128k-online'),
prompt: query,
maxTokens: 1000,
})
return text
}
}
Data Flow
Understanding the data flow helps in debugging and optimizing the system performance.
1. Email Access Flow
This sequence diagram shows how emails are accessed through the MCP service from external providers.
2. Email Composition Flow
Performance Optimizations
Performance optimizations are applied at every layer to ensure a responsive user experience.
Frontend Optimizations
Code Splitting
Route-based code splitting with React.lazy()
Virtual Scrolling
Efficient rendering of large email lists
Memoization
React.memo and useMemo for expensive computations
Image Optimization
Lazy loading and responsive images
Bundle Optimization
Tree shaking and chunk optimization
Backend Optimizations
Database Indexing
Optimized queries with proper indexes
Connection Pooling
Efficient database connection management
Caching
Redis caching for frequently accessed data
Background Jobs
Asynchronous processing for heavy operations
CDN
Static asset delivery via Cloudflare
Email Processing Optimizations
MCP Protocol Efficiency
Optimized MCP calls for minimal latency
OAuth Token Management
Efficient token refresh and caching
Batch Operations
Process multiple emails in single API calls
Smart Caching
Cache frequently accessed emails and metadata
Security Architecture
Security is paramount in email client architecture. Multiple layers of protection ensure data safety.
Scalability Considerations
The architecture is designed to scale from single-user installations to enterprise deployments.
Horizontal Scaling Strategy
Stateless Design
Servers can be scaled horizontally without session dependencies
Load Balancing
Distribute traffic across multiple instances
Database Scaling
Read replicas and sharding strategies
Microservices
Service-oriented architecture for independent scaling