Amarnai
Architecture

Architecture

High-level overview of how Amarnai is built.

Amarnai is a TypeScript monorepo managed with pnpm workspaces and Turborepo. It is composed of several applications and a set of shared packages. The runtime services are web, api, and worker; the remaining apps are supporting surfaces (marketing site, browser extension, docs) and a shelved mobile app.

The stack is Node 24, Next.js 16, React 19, Prisma 6 on PostgreSQL, and BullMQ on Redis. The Next.js apps (web and site) build with --webpack; Turbopack is not used because it lacks the workspace .js extension alias these packages rely on.

Applications

AppDescription
webNext.js 16 frontend, the user-facing app
apiHono HTTP server, the only service that writes to the database
workerBackground job runner: inbox polling, AI triage, queue processing
siteNext.js 16 marketing site, deployed to Cloudflare via OpenNext
extensionBrowser side-panel extension (Chrome and Firefox, MV3)
docsThis documentation site (Fumadocs)
mobileExpo/React Native Android app. Shelved/paused (see the repo CLAUDE.md)

Packages

PackageDescription
dbPrisma schema, migrations, and generated client
aiAI provider abstraction, prompts, embedding sorter, output validation
mailProvider-neutral mail seam (the MailProvider interface + factory)
gmailGmail API client, OAuth, AES-256-GCM token encryption
outlookOutlook provider over Microsoft Graph (read-only)
authCredentials, JWT/Bearer for native clients, connection guards
billingStripe subscriptions, cancellation, and cleanup
emailTransactional email sending (Resend or SMTP)
coreFramework-free view-model logic (emails, taxonomy, identity)
uiShared React components and email templates (web)
tokensFramework-agnostic design tokens and theme
i18nLingui catalogs and helpers (16 locales)
api-clientTransport-agnostic typed API client (web + native)
queueBullMQ job definitions and queue configuration
sharedShared types and Zod schemas
configEnvironment variable loading and validation

Key design decisions

Threads, not messages. Amarnai triages email threads as a unit. A new message in an existing thread triggers re-triage of the whole thread, so the sort order always reflects the full conversation.

One provider seam, read-only. Gmail and Outlook are reached through a single MailProvider interface in packages/mail. The concrete adapter is chosen per EmailConnection, not by a global env switch, so both providers stay at feature parity. Neither adapter can send or mutate mail beyond what read-only access allows.

Multi-tenant by default. Every resource in the database is scoped to a workspace. API costs are attributable per user so the hosted offering can track and control spend.

AI output is untrusted. All LLM responses are validated with Zod before any action is taken. Policy code, not prompts, decides final labels and destinations.

Idempotent jobs. All background jobs are safe to retry. Duplicate runs produce the same result rather than double-applying effects.

On this page