Skip to content

dermute/inboxone

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

inboxone

⚠️ AI-Generated Project — Read Before Use

This entire application (backend, frontend, and this README) was generated by an AI coding agent (Claude) for personal use. It has not undergone any professional or human security review, and no specific security hardening has been done beyond basic good practices (encrypted credential storage, a password gate, sandboxed message rendering).

This is intended strictly for single-user, private/local-network use. Do not expose this container directly to the public internet. If you want remote access, put it behind your own trusted VPN or a reverse proxy with independent authentication. Use at your own risk.

A self-hosted webmail aggregator: one modern inbox view for all your existing mail accounts (generic IMAP/SMTP providers, plus Outlook.com/Microsoft 365 via OAuth2), running entirely in a single Docker container. Your mail never leaves the origin servers — inboxone only caches lightweight message headers/flags locally for a fast aggregated view; full bodies and attachments are always fetched live from IMAP when you open a message.

Features

  • Aggregate multiple IMAP/SMTP accounts (and Outlook.com/Microsoft 365 via OAuth2) into one chronological inbox.
  • Color-coded account indicator on every message so you always know which mailbox it came from.
  • Reply from any configured account, from a single aggregated view.
  • Per-account folder selection — sync just INBOX, or additional folders/labels too.
  • A single app-wide password gate protects the whole UI.
  • Modern three-pane UI (filter rail / message list / reading pane), not an old-school webmail look.

Architecture (brief)

  • Backend: Python (FastAPI), SQLAlchemy async + SQLite, APScheduler for background IMAP polling, IMAPClient for IMAP, aiosmtplib for basic-auth SMTP sending, msal for Microsoft OAuth2 device-code sign-in, Microsoft Graph for sending mail from Outlook accounts.
  • Frontend: React + TypeScript + Vite + TailwindCSS + TanStack Query + Zustand.
  • Data model: only message headers/flags (subject, sender, date, read/flagged state, etc.) are cached in a local SQLite database. Bodies and attachments are never persisted — they're fetched live from the mail server every time you open a message.
  • Runs as a single Docker container: the API and the background sync scheduler share one Python process (no separate worker process needed).

Requirements

  • Docker and Docker Compose.

Quick start

  1. Copy the example environment file and fill in real values:
    cp .env.example .env
  2. Generate the required secrets:
    python3 -c "import secrets; print(secrets.token_urlsafe(32))"   # SECRET_KEY
    python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"   # ENCRYPTION_KEY
    (If you don't have Python locally, run these one-liners inside the built container instead: docker compose run --rm inboxone python3 -c "...".)
  3. Set APP_PASSWORD in .env to a password of your choosing (this only seeds the initial password - see "Forgot your password?" below for how to reset it later).
  4. Pull and start (uses the published image from ghcr.io):
    docker compose up -d
  5. Open http://localhost:8000, log in with APP_PASSWORD, and add your accounts under Settings.

Adding accounts

Generic IMAP/SMTP (most providers)

Use your provider's IMAP/SMTP host, port, and your normal username/password (or an app-specific password, see below). Settings → Add account → "Generic IMAP/SMTP".

Gmail

Gmail requires an App Password rather than your normal account password once 2-Step Verification is enabled (Google no longer allows plain account passwords for IMAP/SMTP). Create one at myaccount.google.com/apppasswords and use it as the IMAP/SMTP password. Host: imap.gmail.com (port 993), smtp.gmail.com (port 587).

Outlook.com / Microsoft 365

Microsoft has deprecated basic auth for IMAP/SMTP, so Outlook accounts (personal outlook.com/hotmail.com/live.com addresses included, not just work/school ones) use OAuth2 ("modern auth") via a device-code sign-in flow. Settings → Add account → "Outlook / Microsoft 365" → enter a name → Connect. You'll get a short code to enter at microsoft.com/devicelogin, sign in with your normal Microsoft account, and you're done — no Azure setup required. inboxone ships with Microsoft's own first-party "Office" client ID pre-registered (the same well-known ID tools like Thunderbird reuse for exactly this), so you're just doing a normal OAuth sign-in, not registering an app.

Advanced: use your own Azure app instead. If you'd rather isolate your account under an app you control (e.g. the shared client ID ever gets rate-limited or blocked), click "Advanced" in the connect flow and register your own, free:

  1. Go to Azure Portal → App registrations → New registration.
  2. Name it anything. Under Supported account types, choose "Accounts in any organizational directory and personal Microsoft accounts".
  3. Leave the redirect URI blank — the device-code flow doesn't need one.
  4. Go to Authentication → Advanced settings and set "Allow public client flows" to Yes.
  5. Go to API permissions → Add a permission → Microsoft Graph → Delegated permissions, and add: IMAP.AccessAsUser.All, Mail.Send, offline_access, User.Read. No admin consent is needed for a personal registration.
  6. Copy the Application (client) ID from the app's Overview page and paste it into the "Advanced" Client ID field before connecting.

Environment variables

Variable Description
APP_PASSWORD Password required to access the web UI.
SECRET_KEY Random secret used to sign the session cookie.
ENCRYPTION_KEY Fernet key used to encrypt stored IMAP/SMTP passwords and OAuth tokens at rest. Do not rotate after accounts exist without a migration - existing encrypted values would become unreadable.
DATABASE_URL SQLite connection string (defaults to the /data bind mount).
SYNC_DEFAULT_INTERVAL_SECONDS Default IMAP polling interval per account (minimum 30s).

Forgot your password?

APP_PASSWORD in .env only sets the initial password the first time the container starts - after that, the password lives in the database and editing .env no longer has any effect. If you forget it, reset it directly from inside the running container:

docker exec -it inboxone python -m app.cli reset-password

This prompts for a new password and updates it immediately, without needing to know the old one - no need to touch .env or restart anything.

Data & privacy model

  • Mail never leaves your mail servers. The one exception is explicit deletion: hitting Delete on a message moves it to that account's Trash/Deleted Items folder on the server (the same "soft delete" any normal mail client does), it isn't just hidden locally. Nothing is ever permanently erased by this app.
  • Only message metadata (subject, sender/recipients, date, read/flagged state, folder, size) is cached locally in SQLite, so the aggregated view loads instantly.
  • Message bodies and attachments are fetched live over IMAP every time you open a message or download an attachment - they are never written to disk.
  • IMAP/SMTP passwords and Microsoft OAuth tokens are encrypted at rest with a symmetric key (ENCRYPTION_KEY); they are only decrypted in memory for the duration of a connection.

Building from source

The published image (ghcr.io/dermute/inboxone:latest) is what docker-compose.yml uses by default. To build and run from your own checkout instead, uncomment the build: block in docker-compose.yml (and comment out image:), then:

docker compose up -d --build

Development (without Docker)

# backend
cd backend
pip install -e ".[dev]"
alembic upgrade head
uvicorn app.main:app --reload

# frontend (separate terminal)
cd frontend
npm install
npm run dev

The Vite dev server proxies /api requests to http://localhost:8000.

Known limitations / roadmap

  • No IMAP IDLE or push updates yet — the backend polls each account on an interval (default 60s) and the frontend polls the API every 15s. New mail typically appears within a minute.
  • No Gmail OAuth - Gmail accounts are configured as generic IMAP/SMTP using an App Password.
  • Single user, single SQLite database - not designed for multiple concurrent users.
  • Message list "snippet" preview is not populated yet, since generating it would require fetching bodies during sync (which this app intentionally avoids to keep mail off local storage).

AI attribution

This project was developed with AI assistance (Anthropic Claude, Sonnet 5).

License

MIT — see LICENSE.

About

Self-hosted, single-user mail aggregator (IMAP/SMTP + Outlook OAuth2) in one Docker container. AI-generated, personal use only - see README.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors