This page is for people who want to understand imbox before they change it, or before they trust it. It describes what runs, where data lives, and how a message travels from your mail provider to your screen.
The pieces
imbox is one Node.js server and one PostgreSQL database. A reverse proxy in front adds HTTPS. Nothing else is needed.
| Piece | What it does |
|---|---|
| Web app | A React app. The desktop and the phone are separate sets of screens, not one squeezed layout. The desktop app is the same web app in a native window, loaded from your server. |
| API | A Hono app. It answers the web app, the extension and agent keys, and serves the built web app. |
| Background job | Runs once a minute inside the same process. It syncs mail, sends scheduled messages, brings back bubbled-up threads and runs agents. |
| PostgreSQL | Holds everything: mail, contacts, decisions, settings, sessions, encrypted secrets. |
| Live events | A long-lived GET /api/events stream. PostgreSQL tells the server when threads change, and the server tells each open tab. |
| Caddy | Gets certificates and passes requests to the app. On a script install it also routes /docs to the docs site. |
How a message arrives
Every route into imbox ends in the same place: storing the message, then deciding what its thread and sender mean.
- Sync is a pull. Each minute, the job asks every connected mailbox what is new. A first connection imports nothing from the past.
- Cleaning happens on arrival. Tracking pixels are removed when the message is stored. Remote images stay on the sender's server until you allow them.
- The decision belongs to the sender, not the message. Once you let someone in, every later message follows that choice, across all your mailboxes.
- Attachments are not stored in bulk. Message text and metadata are in the database. Files are fetched from the provider when you open them.
How a request is handled
Three kinds of caller reach the API, each with its own narrow door:
| Caller | Credential | What it can reach |
|---|---|---|
| The web and desktop app | Session cookie imbox_session, 30 days | Everything the owner can do |
| The browser extension | Token imx_... | Seven read and triage endpoints |
| An outside agent | Key hfa_..., per mailbox | Only that mailbox, under /agent/v1 |
State-changing requests made with the cookie must carry an Origin that matches the app's address. Every route that takes an id checks that the row belongs to the signed-in user. The details are in API.
What the background job does
The job is a list of independent steps, run one after another. Each has a time budget and a hard deadline, and only one run of each at a time. One stuck step never stops the others, and a failed or slow step leaves a line in the log, such as job mail-sync error.
| Step | What it does |
|---|---|
| Mail sync | Pulls new mail from each mailbox |
| Scheduled sends | Sends messages whose time has come |
| Bubble ups | Brings threads back at the time you picked |
| Calendar sync | Updates Google and CalDAV calendars |
| Auto agents | Lets agents read new mail and write replies |
| Agent webhooks | Tells outside agents that mail arrived |
| Vacation notices | Sends away notices |
| Push and cleanup | Web push notices, and removing old rows |
Long work lives here and never inside a web request, so a slow provider can't make the app feel slow. With more than one web process, a single coordinator runs the job. See a busier server.
Where the data lives
One PostgreSQL database and plain SQL. The only extension it uses is pg_trgm, for fast search. The rough shape:
- Migrations only add tables and columns, and run once each at start-up. That is why an older release still works on a newer database. See Database.
- Secrets are encrypted with
SESSION_SECRET: mailbox passwords, AI keys, app secrets and two-factor secrets. Passwords and session tokens are stored as hashes. See Security and encryption for what is and is not encrypted.
Where AI fits
AI is optional and only runs when asked. The server calls your provider with your key. Nothing else in imbox depends on it.
Mail is treated as untrusted input. After the assistant has read mail, code holds back risky actions, whatever the model says. See AI assistant and Agents in the app.
Safety boundaries
- Outbound requests go through a timeout and a destination check, so a user-supplied address can't reach your private network.
- Cookie requests are checked for origin. Extension tokens and agent keys never use the cookie.
- Passkeys, two-factor and lockout protect sign-in. Five failed logins lock it for 15 minutes.
- One owner. There are no shared accounts, so there is no per-team permission model to get wrong.
Where to go next
- Self-hosting: how this is laid out on a server.
- Development: where each piece lives in the repository.
- API: the endpoints and the three kinds of credentials.