Connectors Overview¶
A connector reads one communication channel — an email inbox, a Discord
server, iMessage/SMS, a WhatsApp export — and turns its messages into
normalized rows in the shared cap_items inbox. From there the rest of the
stack (classify, enrich, chat/RAG) treats every channel the same way.
Connectors come in two shapes:
- Live — an incremental poll, driven by an enabled row in
cap_connectors, that the scheduler runs on a cron. - Backfill — a one-shot CLI import of a bulk export or local database
(
python -m capture_agent.backfill <source> ...), for history that predates the live poll or for channels with no live half.
Capture is disabled by default
A fresh install ingests nothing. The live poll only processes
cap_connectors rows where enabled = true, and that collection starts
empty — you opt in per channel by creating a row.
The enable gate: cap_connectors¶
Every live connector is gated by one PocketBase collection, cap_connectors.
| Field | Type | Purpose |
|---|---|---|
kind |
select | Which connector implementation polls this row: email, discord, or imessage. |
enabled |
bool | The poll only processes rows where this is true. |
config |
json | Connector-specific settings (IMAP host/port/folder, Discord channel allowlist, ...). |
secret_ref |
text | Non-secret name of the env-var group holding this row's credentials (see below). Never the credentials themselves. |
owner_addresses |
json | The owner's own handles/addresses on this channel (e.g. an email address, a Discord user id), used to tag items the owner sent as direction="out". |
cursor |
json | Opaque incremental position the connector owns and rewrites after every poll (per-folder IMAP UID, per-channel Discord snowflake, ...). |
last_poll_at |
date | Timestamp of the row's most recent poll attempt. |
last_error |
text | The last error message for this row; empty when the row is healthy. |
Secrets: secret_ref + a key → an env var¶
Credentials are never stored in PocketBase — only the non-secret
secret_ref name that points at an environment-variable group. A connector
declares which keys it needs (email: USER + PASSWORD; a Discord bot:
TOKEN), and each is resolved from:
JARVIS_{secret_ref}_{KEY}
For example, secret_ref=IMAP_MAIN with key USER resolves to
JARVIS_IMAP_MAIN_USER.
If any required env var is missing when the poll runs, that row's last_error
is set to:
missing credentials (set the secret_ref env vars)
and the poll moves on — one connector's missing creds never stop the others (per-connector isolation).
Where each connector runs¶
The email and discord connectors poll inside the container scheduler
service, on the same cron as the rest of capture. imessage is host-only:
chat.db is a local file behind macOS Full Disk Access, so it's excluded from
the containerized poll and instead run by the owner under a launchd job on
the Mac. The Homelab connector is a separate module (not a cap_connectors
kind) and is likewise host-only — see Homelab.
Connectors at a glance¶
| Connector | Live poll | Backfill | Runs where | kind value |
|---|---|---|---|---|
| Email / IMAP | Yes | Yes (mbox) | Container (scheduler) | email |
| Discord | Yes | Yes (data export package) | Container (scheduler) | discord |
| iMessage / SMS | Yes | Yes | Host (launchd) |
imessage |
| No | Yes (chat export) | n/a — backfill CLI only | not a cap_connectors kind |
|
| Homelab | — | — | Host (launchd) |
not a cap_connectors kind |
Creating a cap_connectors row¶
There's no dedicated setup UI yet — rows are created directly in the PocketBase admin:
- Open http://127.0.0.1:8091/_/ and log in with the superuser credentials (see Quickstart).
- Go to Collections → cap_connectors → New record.
- Fill in
kind,enabled,config,secret_ref, andowner_addressesas described on the connector's own page.
See Email / IMAP for a full worked example, including the
env vars, the exact config JSON, and how to verify the row is polling
successfully — later connector pages follow the same shape.