Delivr-Project/Delivr-API

TypeScript

2

230 commits

updated Oct 3, 2026

See the code

See what people are saying

SourceMessageScoreDate

Delivr: A Modern Open Source Mail Client that actually works (r/selfhosted)

Hi everyone, for the past 10 months I've been working on Delivr (with help from a few great contributors), and today I'm releasing version 1.0.0. Why I built it: I wanted a self-hosted mail client that feels as good as Gmail. The open-source webmail clients I tried work, but most of them feel dated…

8

Oct 3, 2026

README

Delivr

Delivr API

A Mail Client that actually Delivers

The backend that powers Delivr — a fast, self-hostable mail service built on Bun & Hono, speaking IMAP and SMTP so your inbox stays yours.


Bun Hono TypeScript Drizzle License

Web Client →  •  Features  •  Quick Start  •  Configuration


✨ Features

  • 📬 Real mail, real protocols — connects to any IMAP/SMTP provider via imapflow and nodemailer; nothing proprietary in the way.
  • 🗂️ Nested mail resources — mail accounts → mailboxes → mails → attachments, modelled cleanly all the way down.
  • 📎 Privacy-first attachments — content is never stored or cached server-side. Attachments are re-fetched from IMAP, parsed in-memory, and streamed out with Cache-Control: no-store.
  • ⚡ Bulk mail actions — move, copy, delete, and flag many messages in one request.
  • 🖼️ BIMI brand logos — resolves sender brand logos from DNS for use as profile pictures; only record metadata is read, the logo is never fetched or stored.
  • 🔐 JWT auth + API keys — token-based sessions plus scoped API keys for programmatic access.
  • 🔒 ECC crypto — mail-backend credentials protected with elliptic-curve encryption and signing.
  • 📖 First-class OpenAPI — every route is documented via hono-openapi and browsable through an embedded Scalar reference.
  • 🗄️ Zero-setup database — SQLite out of the box, with no separate database server to run. PostgreSQL and MySQL schemas exist and runtime support is planned (#10).
  • ⏰ Scheduled tasks — background jobs via the cron package.
  • ✅ Integration-tested — a mock IMAP/SMTP harness exercises the real request paths.

🧱 Tech Stack

LayerChoice
RuntimeBun 1.x
FrameworkHono 4.x
LanguageTypeScript 6.x
ORMDrizzle ORM + Drizzle Kit
ValidationZod 4.x + @hono/standard-validator
API Docshono-openapi + @scalar/hono-api-reference
DatabaseSQLite (PostgreSQL · MySQL planned)
Mailimapflow (IMAP) · nodemailer (SMTP) · postal-mime
Cryptoelliptic (ECC)

🚀 Quick Start

Prerequisites: Bun 1.x

# 1. Install dependencies
bun install

# 2. Configure your environment
cp example.env .env
#    → set DLA_ENCRYPTION_KEY (32 characters) and review the rest

# 3. Run database migrations (SQLite by default)
bun run db:sqlite:migrate

# 4. Start the dev server
bun run dev

The API is now live at http://localhost:14123, with the interactive Scalar reference at /docs/v1 and the raw OpenAPI spec at /docs/v1/openapi (unless DLA_DISABLE_DOCS=true). The root path / redirects to the latest version's docs.

⚙️ Configuration

All configuration is environment-based (see example.env):

VariableDescriptionDefault
DLA_LOG_LEVELLog verbosityinfo
DLA_APP_URLRequired. URL of the Delivr web client—
DLA_API_HOSTBind address::
DLA_API_PORTListen port14123
DLA_DISABLE_DOCSDisable the Scalar API referencefalse
DLA_MAX_ATTACHMENT_SIZE_MBMaximum combined attachment size per composed mail, in MB, measured before encoding25
DLA_ENCRYPTION_KEYRequired. 32-character key for credential encryption—
DLA_DB_CONNECTION_URLDatabase connection string / path./data/db.sqlite
DLA_DB_AUTO_MIGRATERun migrations on startuptrue
DLA_DB_MIGRATION_DIRDir with the migrations files. The compiled binary resolves a relative path next to the executable, not the working directoryhas to be set manually
DLA_LOG_DIRLog output directory./data/logs
DLA_CONFIG_BASE_DIRConfig base directory./config
DLA_SMTP_HOSTOutbound SMTP host for system mail (e.g. password-reset emails)—
DLA_SMTP_PORTOutbound SMTP port—
DLA_SMTP_USERNAMEOutbound SMTP username—
DLA_SMTP_PASSWORDOutbound SMTP password—
DLA_SMTP_FROMFrom address for system mail—
DLA_SMTP_SECUREUse TLS for the SMTP connectionfalse

Create-mail requests have a total request limit of DLA_MAX_ATTACHMENT_SIZE_MB + 16 MB (41 MB by default), covering both JSON and multipart bodies (mail JSON, attachments and multipart framing). Exceeding this total returns a request-size error; exceeding the separate combined attachment limit returns an attachment-size error. Large text/HTML bodies count toward the total request limit.

The attachment limit counts the files' raw size. Base64 encoding makes the sent message about 37% larger, so 25 MB of attachments become roughly 34 MB on the wire: keep the limit below your mail provider's message size limit divided by 1.37. If the provider still rejects a message as too large, the send request returns a 400 error. Bun itself rejects request bodies over 128 MB, which caps the attachment limit at 112 MB.

🛠️ Commands

CommandDescription
bun run devStart dev server with watch mode
bun run typecheckRun TypeScript type checking
bun testRun the test suite
bun run compileCompile the project
bun run startProduction entry point
bun run db:sqlite:generateGenerate SQLite migrations
bun run db:sqlite:migrateRun SQLite migrations
bun run db:postgresql:generate · :migratePostgreSQL migrations (schema only, runtime support planned)
bun run db:mysql:generate · :migrateMySQL migrations (schema only, runtime support planned)

🗺️ Project Structure

src/
├── index.ts                # Entry point
├── api/
│   ├── index.ts            # API router setup
│   ├── utils/              # Response helpers, auth, OpenAPI, services
│   └── versions/v1/
│       ├── middleware/     # Auth middleware
│       ├── docs/           # OpenAPI tag definitions (Scalar UI mounted in api/index.ts)
│       └── routes/         # auth · account · mail-accounts · bimi · admin
│                           #   auth → reset-password
│                           #   account → apikeys · preferences
│                           #   mail-accounts → identities · search · mailboxes
│                           #     mailboxes → mail-bulk-actions · mails → attachments
├── db/
│   ├── index.ts            # DB connection
│   └── schema/             # Per-dialect Drizzle schema (sqlite · postgresql · mysql)
└── utils/
    ├── config.ts           # App configuration
    ├── cron.ts             # Scheduled tasks
    ├── crypto/             # ECC encryption & signing
    └── mails/              # Mail backends & parsing

Note: Drizzle schema files are dialect-specific. When adding tables or columns, mirror the change across all three files in src/db/schema/.

📦 The Delivr Project

RepositoryDescription
Delivr API (you are here)The Bun + Hono backend
Delivr WebThe Nuxt 4 web client & PWA

📄 License

Licensed under the GNU AGPL-3.0.

Built with 💙 and Bun.

Delivr-Project/Delivr-API

TypeScript

2

230 commits

updated Oct 3, 2026

See the code

See what people are saying

SourceMessageScoreDate

Delivr: A Modern Open Source Mail Client that actually works (r/selfhosted)

Hi everyone, for the past 10 months I've been working on Delivr (with help from a few great contributors), and today I'm releasing version 1.0.0. Why I built it: I wanted a self-hosted mail client that feels as good as Gmail. The open-source webmail clients I tried work, but most of them feel dated…

8

Oct 3, 2026

README

Delivr

Delivr API

A Mail Client that actually Delivers

The backend that powers Delivr — a fast, self-hostable mail service built on Bun & Hono, speaking IMAP and SMTP so your inbox stays yours.


Bun Hono TypeScript Drizzle License

Web Client →  •  Features  •  Quick Start  •  Configuration


✨ Features

  • 📬 Real mail, real protocols — connects to any IMAP/SMTP provider via imapflow and nodemailer; nothing proprietary in the way.
  • 🗂️ Nested mail resources — mail accounts → mailboxes → mails → attachments, modelled cleanly all the way down.
  • 📎 Privacy-first attachments — content is never stored or cached server-side. Attachments are re-fetched from IMAP, parsed in-memory, and streamed out with Cache-Control: no-store.
  • ⚡ Bulk mail actions — move, copy, delete, and flag many messages in one request.
  • 🖼️ BIMI brand logos — resolves sender brand logos from DNS for use as profile pictures; only record metadata is read, the logo is never fetched or stored.
  • 🔐 JWT auth + API keys — token-based sessions plus scoped API keys for programmatic access.
  • 🔒 ECC crypto — mail-backend credentials protected with elliptic-curve encryption and signing.
  • 📖 First-class OpenAPI — every route is documented via hono-openapi and browsable through an embedded Scalar reference.
  • 🗄️ Zero-setup database — SQLite out of the box, with no separate database server to run. PostgreSQL and MySQL schemas exist and runtime support is planned (#10).
  • ⏰ Scheduled tasks — background jobs via the cron package.
  • ✅ Integration-tested — a mock IMAP/SMTP harness exercises the real request paths.

🧱 Tech Stack

LayerChoice
RuntimeBun 1.x
FrameworkHono 4.x
LanguageTypeScript 6.x
ORMDrizzle ORM + Drizzle Kit
ValidationZod 4.x + @hono/standard-validator
API Docshono-openapi + @scalar/hono-api-reference
DatabaseSQLite (PostgreSQL · MySQL planned)
Mailimapflow (IMAP) · nodemailer (SMTP) · postal-mime
Cryptoelliptic (ECC)

🚀 Quick Start

Prerequisites: Bun 1.x

# 1. Install dependencies
bun install

# 2. Configure your environment
cp example.env .env
#    → set DLA_ENCRYPTION_KEY (32 characters) and review the rest

# 3. Run database migrations (SQLite by default)
bun run db:sqlite:migrate

# 4. Start the dev server
bun run dev

The API is now live at http://localhost:14123, with the interactive Scalar reference at /docs/v1 and the raw OpenAPI spec at /docs/v1/openapi (unless DLA_DISABLE_DOCS=true). The root path / redirects to the latest version's docs.

⚙️ Configuration

All configuration is environment-based (see example.env):

VariableDescriptionDefault
DLA_LOG_LEVELLog verbosityinfo
DLA_APP_URLRequired. URL of the Delivr web client—
DLA_API_HOSTBind address::
DLA_API_PORTListen port14123
DLA_DISABLE_DOCSDisable the Scalar API referencefalse
DLA_MAX_ATTACHMENT_SIZE_MBMaximum combined attachment size per composed mail, in MB, measured before encoding25
DLA_ENCRYPTION_KEYRequired. 32-character key for credential encryption—
DLA_DB_CONNECTION_URLDatabase connection string / path./data/db.sqlite
DLA_DB_AUTO_MIGRATERun migrations on startuptrue
DLA_DB_MIGRATION_DIRDir with the migrations files. The compiled binary resolves a relative path next to the executable, not the working directoryhas to be set manually
DLA_LOG_DIRLog output directory./data/logs
DLA_CONFIG_BASE_DIRConfig base directory./config
DLA_SMTP_HOSTOutbound SMTP host for system mail (e.g. password-reset emails)—
DLA_SMTP_PORTOutbound SMTP port—
DLA_SMTP_USERNAMEOutbound SMTP username—
DLA_SMTP_PASSWORDOutbound SMTP password—
DLA_SMTP_FROMFrom address for system mail—
DLA_SMTP_SECUREUse TLS for the SMTP connectionfalse

Create-mail requests have a total request limit of DLA_MAX_ATTACHMENT_SIZE_MB + 16 MB (41 MB by default), covering both JSON and multipart bodies (mail JSON, attachments and multipart framing). Exceeding this total returns a request-size error; exceeding the separate combined attachment limit returns an attachment-size error. Large text/HTML bodies count toward the total request limit.

The attachment limit counts the files' raw size. Base64 encoding makes the sent message about 37% larger, so 25 MB of attachments become roughly 34 MB on the wire: keep the limit below your mail provider's message size limit divided by 1.37. If the provider still rejects a message as too large, the send request returns a 400 error. Bun itself rejects request bodies over 128 MB, which caps the attachment limit at 112 MB.

🛠️ Commands

CommandDescription
bun run devStart dev server with watch mode
bun run typecheckRun TypeScript type checking
bun testRun the test suite
bun run compileCompile the project
bun run startProduction entry point
bun run db:sqlite:generateGenerate SQLite migrations
bun run db:sqlite:migrateRun SQLite migrations
bun run db:postgresql:generate · :migratePostgreSQL migrations (schema only, runtime support planned)
bun run db:mysql:generate · :migrateMySQL migrations (schema only, runtime support planned)

🗺️ Project Structure

src/
├── index.ts                # Entry point
├── api/
│   ├── index.ts            # API router setup
│   ├── utils/              # Response helpers, auth, OpenAPI, services
│   └── versions/v1/
│       ├── middleware/     # Auth middleware
│       ├── docs/           # OpenAPI tag definitions (Scalar UI mounted in api/index.ts)
│       └── routes/         # auth · account · mail-accounts · bimi · admin
│                           #   auth → reset-password
│                           #   account → apikeys · preferences
│                           #   mail-accounts → identities · search · mailboxes
│                           #     mailboxes → mail-bulk-actions · mails → attachments
├── db/
│   ├── index.ts            # DB connection
│   └── schema/             # Per-dialect Drizzle schema (sqlite · postgresql · mysql)
└── utils/
    ├── config.ts           # App configuration
    ├── cron.ts             # Scheduled tasks
    ├── crypto/             # ECC encryption & signing
    └── mails/              # Mail backends & parsing

Note: Drizzle schema files are dialect-specific. When adding tables or columns, mirror the change across all three files in src/db/schema/.

📦 The Delivr Project

RepositoryDescription
Delivr API (you are here)The Bun + Hono backend
Delivr WebThe Nuxt 4 web client & PWA

📄 License

Licensed under the GNU AGPL-3.0.

Built with 💙 and Bun.

Languages

TypeScript

73.6%

JavaScript

26.3%