2026-07-05 22:28:49 +02:00
2026-03-30 13:57:39 +00:00
.
2026-03-30 16:58:37 +00:00
2026-03-29 18:45:25 +02:00
.
2026-03-30 16:36:59 +00:00
.
2026-03-30 14:42:49 +00:00
.
2026-03-30 14:42:49 +00:00
2026-07-05 22:28:49 +02:00
.
2026-03-30 16:36:59 +00:00
2026-03-30 13:57:39 +00:00
.
2026-03-30 14:42:49 +00:00
2026-03-30 13:57:39 +00:00

Docker Mailserver User Management Panel

A small full-stack admin tool for managing Docker Mailserver accounts with Bun, ElysiaJS, and React.

1. Proposed stack

Backend

  • Bun
  • ElysiaJS
  • Bun SQLite
  • Docker CLI through fixed argument arrays
  • Zod for small, explicit validation

Why this backend:

  • Bun keeps the runtime, package manager, and SQLite support simple.
  • ElysiaJS is a good fit for a small internal API without extra framework weight.
  • SQLite is enough for admin sessions and a small amount of panel metadata, and keeps deployment straightforward.
  • Docker CLI integration stays close to Docker Mailserver's real operational model instead of hiding it behind unnecessary layers.

Frontend

  • React
  • Vite
  • Plain CSS

Why React:

  • It is easy for most full-stack teams to read and maintain.
  • This panel only needs basic local state and forms, so React works well without routers or global state libraries.
  • The UI stays minimal while still being easy to grow later.

2. Folder structure

apps/
  api/
    src/
      auth/
      docker/
      lib/
      mail/
      users/
  web/
    src/
      api/
      components/
      pages/
      styles/
packages/
  shared/
data/
  app.db
infra/
  nginx/

3. Backend implementation

The backend is intentionally flat:

  • apps/api/src/index.ts Defines the HTTP routes and keeps request flow obvious.
  • apps/api/src/auth/service.ts Handles admin login, bootstrap admin seeding, session cookies, and session lookup.
  • apps/api/src/docker/ Wraps Docker CLI calls with fixed argument arrays only. No shell interpolation.
  • apps/api/src/users/service.ts Contains the mailbox user operations, pending mailbox invites, and the one-time link flow for password changes.
  • apps/api/src/lib/ Holds config parsing, SQLite access, validation, HTTP helpers, and small security utilities.

API design

  • GET /api/auth/session
  • POST /api/auth/login
  • POST /api/auth/logout
  • GET /api/users
  • POST /api/users
  • DELETE /api/users/:email
  • POST /api/users/:email/password-reset
  • POST /api/password-resets/inspect
  • POST /api/password-resets/consume

4. Frontend implementation

The frontend is a small React app with three views:

  • Login page
  • Dashboard page
  • Public reset-password page

The dashboard includes:

  • mailbox listing
  • simple search
  • create user modal
  • 48-hour setup link generation for new mailboxes
  • temporary link generation for password changes on existing mailboxes
  • delete action
  • no direct password change from the admin panel

The create flow now generates a 48-hour setup link. The mailbox does not exist yet at that point. It is created only when the user opens the link and chooses a password.

5. Password reset flow

The reset flow is built to stay secure without becoming heavy:

  1. For a new mailbox, the admin generates a 48-hour setup link.
  2. The mailbox is not created yet.
  3. The backend stores only the SHA-256 hash of the invite token.
  4. The admin shares the one-time setup link manually with the user.
  5. When the user chooses a password through the link, the backend creates the mailbox in Docker Mailserver.
  6. For existing mailboxes, the same public page is reused for password reset links.
  7. Every link is one-time and expires automatically.

6. Docker CLI integration strategy

The app talks to Docker Mailserver through docker exec with explicit argument arrays:

  • setup email list
  • setup email add <email> <password>
  • setup email update <email> <password>
  • setup email del <email>
  • sendmail -t -i

Security choices here:

  • No sh -c
  • No user input inserted into shell strings
  • Input is validated before command execution
  • Docker commands have a timeout
  • Errors are returned as API failures instead of leaking raw implementation detail to the browser

7. Security notes

  • Admin authentication uses an Argon2id-hashed bootstrap account from environment variables.
  • Panel login uses a single access key instead of an email/password pair.
  • Access key login is protected by an intentionally aggressive in-memory rate limit with escalating lockouts.
  • Sessions are server-side, opaque, and stored in an HttpOnly cookie with SameSite=Strict.
  • Password reset tokens are random, one-time, time-limited, and stored only as hashes.
  • New mailbox setup links are also one-time, time-limited, and stored only as hashes.
  • The admin panel never changes mailbox passwords directly; it only generates one-time links.
  • Admin-only routes require a valid session cookie.
  • Validation is applied to logins, emails, passwords, and reset payloads.
  • Allowed origins are restricted by APP_ORIGINS.
  • Setup emails for new accounts are sent from the backend only. No secrets are exposed to the frontend.
  • Docker Mailserver access is treated as privileged infrastructure access.

8. Run locally

Prerequisites

  • Bun installed
  • Docker available locally
  • A running Docker Mailserver container
  • The API process must be allowed to access Docker

Setup

  1. Copy .env.example to .env.
  2. Update at least:
    • HOSTED_DOMAINS
    • MAILSERVER_CONTAINER
    • APP_ADMIN_EMAIL
    • APP_ADMIN_ACCESS_KEY
    • MAILBOX_INVITE_TTL_HOURS=48
  3. Install dependencies:
bun install
  1. Start the API:
bun run dev:api
  1. In another terminal, start the frontend:
bun run dev:web
  1. Open http://localhost:5173.

Production-style local run

If you want the built frontend to be served by the API itself:

bun run build
bun run dev:api

Then open http://localhost:3001.

The API serves files from apps/web/dist by default, configurable with WEB_DIST_PATH.

Docker permission note

If you see:

permission denied while trying to connect to the docker API at unix:///var/run/docker.sock

the app itself is fine, but the API process cannot talk to Docker.

Common fixes:

  • Run the API with a user that belongs to the docker group
  • If the API runs in a container, mount /var/run/docker.sock:/var/run/docker.sock
  • Make sure the Docker daemon is actually running
  • Make sure MAILSERVER_CONTAINER matches the real Docker Mailserver container name

Notes and assumptions

  • This panel assumes Docker Mailserver is using the file-based account provisioner and supports the standard setup email commands.
  • APP_ADMIN_EMAIL is now an internal admin identity for sessions and audit trails. The actual panel login uses APP_ADMIN_ACCESS_KEY.
  • The app stores only panel-specific metadata in SQLite. Mailbox accounts themselves remain managed by Docker Mailserver.
S
Description
No description provided
Readme
169 KiB
Languages
TypeScript 78.9%
CSS 20.1%
HTML 0.5%
JavaScript 0.5%