Skip to content

Email / IMAP

The live email connector. If you're setting up a connector for the first time, this page is the reference — later connector pages follow the same shape.

What it does

A read-only, incremental IMAP poll (imaplib.IMAP4_SSL) that reads new messages in one folder and keeps a per-folder UID cursor so each run only fetches what's new. It reuses the same email normalization as the mbox backfill, so live-polled and backfilled mail end up as identical cap_items shapes.

The mailbox is opened read-only (select(folder, readonly=True)) — the connector never deletes, moves, or marks messages.

Prereqs

  • An IMAP account (host, port, and folder — usually INBOX).
  • An app password for that account, not your regular login password.
  • No host access needed — this connector runs entirely inside the container scheduler.

Env / secret_ref

Pick a secret_ref name (this page uses IMAP_MAIN) and set its two env vars in .env:

JARVIS_IMAP_MAIN_USER=you@example.com
JARVIS_IMAP_MAIN_PASSWORD=your-app-password

Both are commented out in .env.example and already wired through to the scheduler service's environment in compose.yaml — uncomment and fill them in in your own .env, don't edit .env.example.

The cap_connectors row

{
  "kind": "email",
  "enabled": true,
  "secret_ref": "IMAP_MAIN",
  "config": {
    "host": "imap.example.com",
    "port": 993,
    "ssl": true,
    "folder": "INBOX"
  },
  "owner_addresses": ["you@example.com"]
}

owner_addresses marks which address is yours, so messages you sent are tagged direction="out". If you leave it empty, the connector falls back to the comma-separated JARVIS_OWNER_EMAIL env var.

Turn it on

  1. Set JARVIS_IMAP_MAIN_USER and JARVIS_IMAP_MAIN_PASSWORD in .env.
  2. Restart the stack so the scheduler picks up the new env vars:
./jarvis down
./jarvis up
  1. Create the cap_connectors row above via the PocketBase admin UI — see Overview.

The scheduler's capture_agent.poll cron (every 10 minutes) picks up the new row on its next run — no restart needed after step 3. The cursor it writes back looks like:

{"INBOX": {"uidvalidity": 12345, "last_uid": 987}}

Verify

./jarvis logs scheduler

Look for a line like capture poll: {'connectors_polled': 1, 'ingested': N, 'errors': 0}. Then, in the PocketBase admin:

  • cap_items grows by roughly the number of new messages.
  • The cap_connectors row's last_poll_at updates each cron run.
  • The row's last_error is empty.

Errors

  • Missing credentials — if either env var isn't set when the poll runs, the row's last_error is set to missing credentials (set the secret_ref env vars) and the poll skips it (other connectors keep working).
  • UIDVALIDITY change — if the mailbox reports a different UIDVALIDITY than the cursor remembers (the server rebuilt its UID space), the connector resets last_uid to 0 and re-scans the whole folder from the start. This is safe: cap_items dedupes by external_id, so already-ingested messages are skipped and only genuinely new ones get written.