Channels are tracked in the channel registry: each one is installed and enabled (or not), and its credentials live in the vault. Email and Signal are toggleable — enable them once their credentials are configured. HTTP and CLI are always on and cannot be disabled, so you can never accidentally lock yourself out of your own instance.
The four channels
- Email
- Signal
- CLI
- HTTP API
Email is Curia’s primary channel for external communication. It connects via the Nylas API, which abstracts away the differences between Gmail, Outlook, and other providers.Inbound: Curia polls the Nylas Messages API every 30 seconds (configurable) for new messages. Emails are parsed, attachments extracted, and participants auto-added to your contacts. The adapter persists its high-water mark (the timestamp of the last message it saw) so a restart never re-reads or skips mail, and a watchdog raises an alert if polling stalls for several intervals.Outbound: Replies and new emails are sent via the Nylas Send API. The Coordinator injects its persona’s display name and email signature into every outbound message.CC detection: The email adapter determines whether Curia is a direct recipient (
to), copied (cc), or blind-copied on each message. When Curia is CC’d, the dispatcher tags the message with the primary recipients so the Coordinator knows who the email was actually addressed to and can respond appropriately — typically by using email-reply to the thread rather than composing a new message.Email validation: Before processing, the email adapter checks SPF, DKIM, and DMARC headers. Messages that fail validation are tagged sender_verified: false in their metadata — the Coordinator uses this signal when deciding whether to act on consequential requests.Required secrets (vault): nylas_api_key, nylas_grant_id, nylas_self_emailTrust level: Low — email From headers are trivially spoofable, and even with SPF/DKIM validation, email is the least trustworthy channel.Channel trust levels
Every inbound message is tagged with the trust level of the channel it arrived on. The Coordinator uses this trust level — along with the sender’s individual trust score — to decide what actions it is willing to take on that message.| Channel | Trust level | Rationale |
|---|---|---|
| CLI | High | Requires SSH or physical access to the host |
| Signal | High | Strong identity via phone number + Signal protocol |
| HTTP API | Medium | Token-authenticated, but tokens can be shared or leaked |
| Low | From headers are spoofable; even SPF/DKIM is not identity verification |
Channel trust is separate from contact trust (
low → medium → high → ceo). Channel trust reflects how much identity assurance the transport provides. Contact trust reflects how much you trust a specific person. Both feed into the message trust score that governs what actions Curia will take.Unknown sender policy
Each channel can be configured with one of two policies for messages from senders not yet in your contacts:hold_and_notify— the message is held in a queue and you are notified via a high-trust channel (CLI or Signal). You can identify the sender, dismiss the message, or block them. Once identified, the message is replayed through normal processing.ignore— messages from unknown senders are silently discarded.
hold_and_notify. When a held message is waiting, the Coordinator proactively mentions it the next time you interact: “By the way, you have a held email from [email protected] about ‘Q3 Numbers’. Want me to identify them?”
Dismissing a held message discards the message — it does not delete the sender’s contact record. Their email address remains in your contacts as provisional. You can look them up later with
contact-lookup.Context bridging on non-threaded channels
Email has native threading — each reply carries amessage_id that links it to the original conversation, so Curia always knows what it last said. Signal, CLI, and HTTP have no equivalent concept: each inbound message arrives without a reference to Curia’s prior response.
Without bridging, a Signal follow-up like “can you make it 4pm instead?” gives the coordinator no way to know what it’s modifying.
Context bridging solves this for non-threaded channels. After every outbound response, the dispatch layer writes a compact context memo to working memory. When the user sends a follow-up, the memo is injected as a preamble — giving the coordinator full awareness of what it last said before it reads the new message.
You configure which channels use threading in config/local.yaml:
threaded: false get context bridging automatically. Channels with threaded: true (email) use native thread identifiers instead.
Context memos are short and structured. They are not the full response — only enough to anchor a follow-up. The coordinator still has access to its full working memory and entity context on every turn.
Channel security
Channel adapters operate within a hard architectural constraint: they can pass inbound messages into the system and receive outbound messages for delivery — and nothing else. They cannot invoke skills, read memory, or trigger agent tasks directly. This is enforced at the framework level, not by policy. A compromised email adapter could flood inbound messages, but it cannot execute skills, access your data, or impersonate another layer. The boundary is structural.Email setup
Connect your email via Nylas.
Signal setup
Set up Signal messaging via signal-cli.