Dear Robot is a self-hosted, AI-first email client built for people who want fast mail handling without giving up control. It keeps the core mechanics inspectable: SQLite for state, SvelteKit for the app, server-side secrets for credentials, and explicit review before anything destructive or external happens.
At a glance, Dear Robot gives you:
- Multi-account IMAP and SMTP email handling
- Review-first AI suggestions for replies, triage, and task planning
- A fast inbox UI with folder navigation, bulk actions, search, and conversation view
- Drafts, compose, reply, reply all, forward, attachments, and offline-ish compose persistence
- A built-in MCP endpoint for external agent tooling
- Optional Obsidian vault integration for durable notes and agent memory
- A single Docker container with a single SQLite database file
- Self-hosted email client with IMAP and SMTP support
- Folder actions for archive, spam, trash, sent, drafts, and provider-specific folder mapping
- AI-assisted triage and response drafting
- Explicit approve/execute lifecycle for agent tasks
- Contact autocomplete from real usage and import/export flows
- Persistent drafts and action/audit logs
- PWA support for a more app-like browser experience
- Optional Obsidian vault mount for long-lived notes and handoffs
- Optional live-provider test paths for IMAP, SMTP, and AI integrations
- Email-first browser automations that can collect dashboard reports and queue reviewed Farin uploads
- Handling multiple mailboxes from one interface
- Keeping AI action visible and user-approved
- Giving you a durable local store for mail, memory, and automation history
- Running in a simple Docker deployment without external queues or extra services
- It is not a fully autonomous email autopilot. Send, move, delete, and task execution remain explicit actions.
- Delete means move to Trash when a Trash folder is available. Permanent deletion is intentionally not the default behavior.
- AI behavior depends on the providers you configure. If a provider is unreachable or misconfigured, the related feature will fail closed.
- IMAP is the primary sync path. Provider-specific behavior varies depending on IMAP flags, special-use folders, and IDLE support.
- Gmail requires the OAuth flow in the UI. Generic accounts require working IMAP and SMTP credentials.
- The Obsidian vault integration is optional. It only appears as a tool when the vault is mounted, readable, and enabled in Settings.
- The app expects one operator account model. It is not designed as a multi-tenant SaaS.
- Production requires persistent secrets. Do not rely on development defaults.
Dear Robot can teach an agent a repeatable, multi-step dashboard task such as
signing in to DoorDash or Uber Eats, opening the latest report, and downloading
the CSV. Start from the email that requests the report: open it and choose
Automate this report. Dear Robot finds the links in that message, creates
the isolated server profile and recipe behind the scenes, and opens a guided
tab in the browser you are already using through the optional Dear Robot
Browser Bridge. Complete the login and report download there, then return to
the email and choose Done — save automation. Choose Test on server in
the same dialog to verify that the server can repeat the login and download;
the test never uploads to Farin. Inspect its downloaded file before enabling
the weekly workflow. A saved recording alone is not a successful server test.
The bridge is a small
Chrome/Firefox extension installed once from
static/browser-bridge/extension; the
launcher explains the install in context. The current bridge verifies a
short-lived, server-issued capability before it opens a recording tab, so a
random webpage cannot impersonate Dear Robot and initiate a capture.
Runs replay only allowlisted HTTP(S) URLs and store downloads beneath
DATA_DIR/browser/downloads. Review the downloaded file before approving the
separate Farin upload step in the generated workflow. Uploads are explicit and
approval-gated in workflows. The Farin tenant
CLI currently accepts JSON request bodies but does not expose a multipart file
upload command, so the app uses Farin's authenticated multipart upload endpoint
for the final file transfer (or its accounting automation secret endpoint when
configured). No iframe or shared default Chrome profile is used.
If the bridge is not installed, the launcher clearly labels the server-window fallback. That fallback is useful for local development only: the headed window appears on the machine running Dear Robot, not inside a remote browser.
The setup dialog can save an encrypted username and password on the server.
During recording, common username/email fields and password fields are saved
only as username/password references; the typed values never enter recipe
JSON. Record the login as well as the download: the client browser's cookies
are not copied to the server. Recognizable one-time-code fields use an
email_code reference. On replay, Dear Robot checks the source mailbox and
the enabled account matching the saved login for a fresh verification message,
scoped to the recipient and service host. It waits up to two minutes and never
writes the code to recipes or run logs. SMS, authenticator apps, CAPTCHA,
unrecognized challenge forms, and unsupported SSO still need human attention.
These automations replay demonstrated actions; they are not an unrestricted computer-use agent. A changed portal or a report that takes hours to generate may need another recording or a later retry. Prefer a stable dashboard URL, choose a relative period or the portal's latest payout, and demonstrate the final download rather than stopping at “report requested.” A run cannot pass without a server-side downloaded file.
Browser automations are intentionally not a separate Operations setup screen. After the email-first setup, the generated workflow appears with the other reviewable workflows in AI Operations. Manage saved profiles, encrypted credentials, recipes, and recent runs under Settings -> Browser automations; configure the optional Farin destination under Settings -> Agent Tools. The inbox flow remains the only place to create a browser automation.
Recipes can create a paused, dry-run every 7d workflow. Enable it only after reviewing the generated steps; the browser collection and Farin upload remain separate, inspectable actions.
For server-side replay (and the local-only fallback), install Chromium once:
npx playwright install chromiumSet FARIN_API_KEY and FARIN_COMPANY_ID in the server environment, or enter
them under Settings -> Agent Tools. Secrets are encrypted with
ENCRYPTION_KEY and are never serialized to the client.
If you are hosting Dear Robot on a VPS (like Hetzner, OVH, or AWS) and find that your IP is blocked by Google (Gemini) or other providers, you can use a Cloudflare Worker as a relay.
We've provided a ready-to-use worker in workers/proxy.
- Navigate to the worker directory:
cd workers/proxy - Install dependencies:
npm install - Login to Cloudflare:
npx wrangler login - Deploy:
npx wrangler deploy
Take note of your worker URL (e.g., https://proxy.your-subdomain.workers.dev).
- Go to Settings -> AI & Model Registry.
- Find the profile you want to proxy (e.g., Fallback / Gemini).
- Enable the Cloudflare Worker Proxy toggle.
- Paste your worker URL into the Proxy URL field.
- Click Save Profile.
- Use Test Connection to verify that the relay is working.
Unlike standard IMAP accounts which support instant push notifications via the IDLE command, Gmail's API relies on Google Cloud Pub/Sub to deliver real-time email notifications. If you connect a Gmail account via OAuth, it will default to a 5-minute polling fallback unless you configure Pub/Sub.
To enable instant real-time sync for Gmail:
- Set up a Topic: In your Google Cloud Console, create a new Pub/Sub Topic (e.g.,
dear-robot-gmail-sync). - Grant Permissions: Give
gmail-api-push@system.gserviceaccount.comthePub/Sub Publisherrole for your new topic. - Create a Push Subscription: Create a subscription for your topic. Set the Delivery Type to Push.
- Set the Endpoint URL: Set the Endpoint URL to your Dear Robot instance's webhook route:
https://your-domain.com/api/webhooks/google. - Watch the Mailbox: You must tell the Gmail API to start publishing to this topic. You can do this via the Gmail API Watch method or by following the Gmail Push Notification Guide.
Once configured, Google will instantly ping your webhook when an email arrives, and Dear Robot will immediately sync the inbox and broadcast the update to your connected browser via Server-Sent Events (SSE).
The recommended deployment is the included Docker image or the bundled docker-compose.yml.
- Create a persistent data directory on the host, or use a named Docker volume.
- Create a
.envfile with the required runtime secrets. - Build and start the container.
- Sign in, configure AI profiles, then add a mailbox.
- If you want Obsidian support, mount
/obsidianand enable it in Settings.
docker compose up -d --buildCreate .env manually using the template below before starting the container.
NODE_ENV=production
PORT=3000
DATA_DIR=/data
APP_SESSION_SECRET=replace-with-a-long-random-string
APP_PASSWORD=replace-with-a-password
ENCRYPTION_KEY=32-byte-hex-or-other-long-random-secret
MCP_AUTH_TOKEN=replace-with-a-bearer-token
AI_PROVIDER=deepseek
AI_MODEL=deepseek-v4-flash
AI_BASE_URL=https://api.deepseek.com
AI_API_KEY=
AI_FALLBACK_PROVIDER=gemini
AI_FALLBACK_MODEL=gemini-2.5-flash
AI_FALLBACK_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai/
AI_FALLBACK_API_KEY=
AI_ADVANCED_PROVIDER=deepseek
AI_ADVANCED_MODEL=deepseek-v4-pro
AI_ADVANCED_BASE_URL=https://api.deepseek.com
AI_ADVANCED_API_KEY=
IMAP_HOST=
IMAP_PORT=993
IMAP_USERNAME=
IMAP_PASSWORD=
SMTP_HOST=
SMTP_PORT=465
SMTP_USERNAME=
SMTP_PASSWORD=services:
dear-robot:
build: .
ports:
- '3000:3000'
env_file:
- .env
environment:
DATA_DIR: /data
PORT: 3000
volumes:
- dear-robot-data:/data
- dear-robot-obsidian:/obsidian # optional, remove if you do not want Obsidian support
volumes:
dear-robot-data:
dear-robot-obsidian:docker build -t dear-robot .
docker run -d \
--name dear-robot \
-p 3000:3000 \
--env-file .env \
-e DATA_DIR=/data \
-v dear-robot-data:/data \
-v dear-robot-obsidian:/obsidian \
dear-robotIf you do not want Obsidian support, remove the /obsidian volume mount. The feature stays disabled unless you enable it in the UI.
/dataholds the SQLite database, memory files, backups, skills, and other runtime state./data/browserholds isolated Playwright profiles and downloaded report files. Keep this volume private./obsidianis the optional vault mount used by the Obsidian tool.
Do not set DB_PATH manually in production. The app derives it from DATA_DIR.
- Start the container.
- Open the app in a browser.
- Sign in with the password from
APP_PASSWORD. - Go to Config -> AI Profiles and save the provider settings you want to use.
- Add your mailbox account after AI profiles are configured.
- If you want Obsidian access, enable the Obsidian tool in Settings -> Agent Tools and point it at the mounted vault path.
| Variable | Purpose |
|---|---|
APP_SESSION_SECRET |
Signs the session cookie and CSRF/session state. Use a long random value. |
APP_PASSWORD |
Password used for app login. |
ENCRYPTION_KEY |
Encrypts stored secrets such as mail and provider credentials. |
MCP_AUTH_TOKEN |
Protects the MCP endpoint from unauthenticated use. |
DATA_DIR |
Absolute path to the persistent runtime directory. |
| Variable | Default | Purpose |
|---|---|---|
NODE_ENV |
development |
Runtime mode. Must be production in a real deployment. |
PORT |
3000 |
Port the Node server listens on. |
DATA_DIR |
/data |
Root directory for SQLite, memory, backups, and skills. Must be absolute. |
DB_PATH |
derived from DATA_DIR |
Not meant to be set manually. The app derives the SQLite path automatically. |
BROWSER_HEADLESS |
true |
Headless browser replay default. Recording always opens a headed window. |
BROWSER_MAX_RUNTIME_MS |
180000 |
Maximum navigation/action timeout for a browser run. |
BROWSER_MAX_DOWNLOAD_BYTES |
31457280 |
Maximum browser download and Farin upload size. |
FARIN_API_HOST |
https://farin.app |
Farin tenant API origin (localhost HTTP is allowed for development). |
FARIN_API_KEY |
unset | Optional server bootstrap tenant API key. Prefer the encrypted UI setting. |
FARIN_COMPANY_ID |
unset | Farin company id paired with the tenant key/automation secret. |
FARIN_AUTOMATION_SECRET |
unset | Optional shared accounting automation secret used by the ingest endpoint. |
| Variable | Default | Purpose |
|---|---|---|
AI_PROVIDER |
deepseek |
Primary AI provider preset. |
AI_MODEL |
deepseek-v4-flash |
Primary AI model. |
AI_BASE_URL |
https://api.deepseek.com |
Primary AI endpoint. |
AI_PROXY_URL |
unset | Optional Cloudflare Proxy URL for the primary profile. |
AI_API_KEY |
unset | Optional bootstrap API key for the primary profile. |
OPENAI_API_KEY |
unset | Optional OpenAI API key used when an OpenAI profile is selected and no profile key is saved. |
AI_FALLBACK_PROVIDER |
gemini |
Fallback AI provider preset. |
AI_FALLBACK_MODEL |
gemini-2.5-flash |
Fallback AI model. |
AI_FALLBACK_BASE_URL |
https://generativelanguage.googleapis.com/v1beta/openai/ |
Fallback AI endpoint. |
AI_FALLBACK_PROXY_URL |
unset | Optional Cloudflare Proxy URL for the fallback profile. |
AI_FALLBACK_API_KEY |
unset | Optional bootstrap API key for the fallback profile. |
AI_ADVANCED_PROVIDER |
deepseek |
Advanced planner provider preset. |
AI_ADVANCED_MODEL |
deepseek-v4-pro |
Advanced planner model. |
AI_ADVANCED_BASE_URL |
AI_BASE_URL or https://api.deepseek.com |
Advanced planner endpoint. |
AI_ADVANCED_PROXY_URL |
unset | Optional Cloudflare Proxy URL for the advanced profile. |
AI_ADVANCED_API_KEY |
unset | Optional bootstrap API key for the advanced profile. |
AI_MAX_REPAIR_ATTEMPTS |
1 |
Number of JSON repair attempts before the AI call fails. |
DEBUG_AI |
false |
Logs additional AI request and response data in development. |
| Variable | Default | Purpose |
|---|---|---|
IMAP_HOST |
unset | Optional live IMAP host used by integration tests and live account setup. |
IMAP_PORT |
993 |
IMAP port. |
IMAP_USERNAME |
unset | Optional IMAP username for live-provider testing. |
IMAP_PASSWORD |
unset | Optional IMAP password for live-provider testing. |
SMTP_HOST |
unset | Optional SMTP host used by live-provider testing. |
SMTP_PORT |
465 |
SMTP port. |
SMTP_USERNAME |
unset | Optional SMTP username for live-provider testing. |
SMTP_PASSWORD |
unset | Optional SMTP password for live-provider testing. |
MAILBOX_OP_MIN_INTERVAL_MS |
120 |
Minimum delay between mailbox operations. |
RUN_LIVE_PROVIDER_TESTS |
false |
Enables live integration test flows that require real provider credentials. |
TEST_EMAIL_FROM |
unset | Optional test sender address. |
TEST_EMAIL_TO |
unset | Optional test recipient address. |
| Variable | Default | Purpose |
|---|---|---|
ATTACHMENT_MAX_BYTES |
15728640 |
Maximum attachment size in bytes. |
ATTACHMENT_SCAN_STRICT |
false |
Fails closed when attachment scanning reports warnings. |
API_RATE_LIMIT_PER_MINUTE |
180 |
API request limit per minute. |
| Variable | Default | Purpose |
|---|---|---|
AUTOPILOT_INTERVAL_MINUTES |
15 |
Polling interval for autopilot processing. |
BACKUP_RETENTION_DAYS |
30 |
How long backups are kept before cleanup. |
BACKUP_MAX_COUNT |
30 |
Maximum number of retained backups. |
ALLOW_DANGEROUS_DB_RESET |
false |
Test-only override for destructive local reset paths. Do not use in production. |
Obsidian support is optional and disabled by default.
- Lets the agent search, read, write, and append notes inside a mounted vault
- Makes it practical to keep decisions, summaries, and handoff notes close to the app
- Exposes the vault as a built-in tool named
obsidian_vault
- Mount a host directory or Docker volume to
/obsidian. - Make sure the mounted path is readable and writable by the container user.
- Open Settings -> Agent Tools.
- Enable the Obsidian vault integration.
- Confirm the vault path is set to the mounted path, usually
/obsidian.
- The vault path must be absolute.
- The mounted path must exist as a real directory.
- Symlink escapes are blocked inside the tool layer.
- If the vault is missing or not writable, the app will keep the tool disabled.
- Put concise daily logs in append mode.
- Keep final decisions and handoff notes as curated notes.
- Search the vault before asking the model to recreate context.
- Session and CSRF protection are server-side.
- API secrets stay on the server.
- AI tool usage is opt-in and review-first.
- Obsidian write actions require an enabled vault mount.
- Dangerous deletion is intentionally routed through Trash instead of permanent removal.
npm install
npm run devUseful checks before merging:
npm run check
npm run lint
npm run test:unit
npm run test:e2e
npm run buildThis project is licensed under the MIT License. See LICENSE.
