The backend that powers Delivr — a fast, self-hostable mail service built on Bun & Hono, speaking IMAP and SMTP so your inbox stays yours.
imapflow and nodemailer; nothing proprietary in the way.Cache-Control: no-store.hono-openapi and browsable through an embedded Scalar reference.cron package.| Layer | Choice |
|---|---|
| Runtime | Bun 1.x |
| Framework | Hono 4.x |
| Language | TypeScript 6.x |
| ORM | Drizzle ORM + Drizzle Kit |
| Validation | Zod 4.x + @hono/standard-validator |
| API Docs | hono-openapi + @scalar/hono-api-reference |
| Database | SQLite (PostgreSQL · MySQL planned) |
imapflow (IMAP) · nodemailer (SMTP) · postal-mime | |
| Crypto | elliptic (ECC) |
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.
All configuration is environment-based (see example.env):
| Variable | Description | Default |
|---|---|---|
DLA_LOG_LEVEL | Log verbosity | info |
DLA_APP_URL | Required. URL of the Delivr web client | — |
DLA_API_HOST | Bind address | :: |
DLA_API_PORT | Listen port | 14123 |
DLA_DISABLE_DOCS | Disable the Scalar API reference | false |
DLA_MAX_ATTACHMENT_SIZE_MB | Maximum combined attachment size per composed mail, in MB, measured before encoding | 25 |
DLA_ENCRYPTION_KEY | Required. 32-character key for credential encryption | — |
DLA_DB_CONNECTION_URL | Database connection string / path | ./data/db.sqlite |
DLA_DB_AUTO_MIGRATE | Run migrations on startup | true |
DLA_DB_MIGRATION_DIR | Dir with the migrations files. The compiled binary resolves a relative path next to the executable, not the working directory | has to be set manually |
DLA_LOG_DIR | Log output directory | ./data/logs |
DLA_CONFIG_BASE_DIR | Config base directory | ./config |
DLA_SMTP_HOST | Outbound SMTP host for system mail (e.g. password-reset emails) | — |
DLA_SMTP_PORT | Outbound SMTP port | — |
DLA_SMTP_USERNAME | Outbound SMTP username | — |
DLA_SMTP_PASSWORD | Outbound SMTP password | — |
DLA_SMTP_FROM | From address for system mail | — |
DLA_SMTP_SECURE | Use TLS for the SMTP connection | false |
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.
| Command | Description |
|---|---|
bun run dev | Start dev server with watch mode |
bun run typecheck | Run TypeScript type checking |
bun test | Run the test suite |
bun run compile | Compile the project |
bun run start | Production entry point |
bun run db:sqlite:generate | Generate SQLite migrations |
bun run db:sqlite:migrate | Run SQLite migrations |
bun run db:postgresql:generate · :migrate | PostgreSQL migrations (schema only, runtime support planned) |
bun run db:mysql:generate · :migrate | MySQL migrations (schema only, runtime support planned) |
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/.
| Repository | Description |
|---|---|
| Delivr API (you are here) | The Bun + Hono backend |
| Delivr Web | The Nuxt 4 web client & PWA |
Licensed under the GNU AGPL-3.0.
TypeScript
73.6%
JavaScript
26.3%
The backend that powers Delivr — a fast, self-hostable mail service built on Bun & Hono, speaking IMAP and SMTP so your inbox stays yours.
imapflow and nodemailer; nothing proprietary in the way.Cache-Control: no-store.hono-openapi and browsable through an embedded Scalar reference.cron package.| Layer | Choice |
|---|---|
| Runtime | Bun 1.x |
| Framework | Hono 4.x |
| Language | TypeScript 6.x |
| ORM | Drizzle ORM + Drizzle Kit |
| Validation | Zod 4.x + @hono/standard-validator |
| API Docs | hono-openapi + @scalar/hono-api-reference |
| Database | SQLite (PostgreSQL · MySQL planned) |
imapflow (IMAP) · nodemailer (SMTP) · postal-mime | |
| Crypto | elliptic (ECC) |
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.
All configuration is environment-based (see example.env):
| Variable | Description | Default |
|---|---|---|
DLA_LOG_LEVEL | Log verbosity | info |
DLA_APP_URL | Required. URL of the Delivr web client | — |
DLA_API_HOST | Bind address | :: |
DLA_API_PORT | Listen port | 14123 |
DLA_DISABLE_DOCS | Disable the Scalar API reference | false |
DLA_MAX_ATTACHMENT_SIZE_MB | Maximum combined attachment size per composed mail, in MB, measured before encoding | 25 |
DLA_ENCRYPTION_KEY | Required. 32-character key for credential encryption | — |
DLA_DB_CONNECTION_URL | Database connection string / path | ./data/db.sqlite |
DLA_DB_AUTO_MIGRATE | Run migrations on startup | true |
DLA_DB_MIGRATION_DIR | Dir with the migrations files. The compiled binary resolves a relative path next to the executable, not the working directory | has to be set manually |
DLA_LOG_DIR | Log output directory | ./data/logs |
DLA_CONFIG_BASE_DIR | Config base directory | ./config |
DLA_SMTP_HOST | Outbound SMTP host for system mail (e.g. password-reset emails) | — |
DLA_SMTP_PORT | Outbound SMTP port | — |
DLA_SMTP_USERNAME | Outbound SMTP username | — |
DLA_SMTP_PASSWORD | Outbound SMTP password | — |
DLA_SMTP_FROM | From address for system mail | — |
DLA_SMTP_SECURE | Use TLS for the SMTP connection | false |
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.
| Command | Description |
|---|---|
bun run dev | Start dev server with watch mode |
bun run typecheck | Run TypeScript type checking |
bun test | Run the test suite |
bun run compile | Compile the project |
bun run start | Production entry point |
bun run db:sqlite:generate | Generate SQLite migrations |
bun run db:sqlite:migrate | Run SQLite migrations |
bun run db:postgresql:generate · :migrate | PostgreSQL migrations (schema only, runtime support planned) |
bun run db:mysql:generate · :migrate | MySQL migrations (schema only, runtime support planned) |
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/.
| Repository | Description |
|---|---|
| Delivr API (you are here) | The Bun + Hono backend |
| Delivr Web | The Nuxt 4 web client & PWA |
Licensed under the GNU AGPL-3.0.
TypeScript
73.6%
JavaScript
26.3%