Skip to content

Gateway

The Gateway is the protocol edge — the boundary between email and the engine. Everything about mail lives here, and nothing else does: routing an inbound message to the right agent, guarding against loops and unwanted senders, and turning a reply back into correctly-threaded outbound mail. The inference core never learns what a mailbox is, and the Gateway never learns what a model is.

Inbound

Every received email passes through a fixed sequence of gates before it can reach inference. Each gate is a hard stop: the message is dropped, bounced, or accepted, and only an accepted message becomes a run.

Inbound email (customer → support@thescoop.com)


 recursion limit reached?     ── yes ─► silently drop
 no agent owns the address?   ── yes ─► bounce (unrouted)
 sender not allowed in?       ── yes ─► bounce (access denied)
 attachments over the limit?  ── yes ─► bounce (attachment rejected)

   └─ accepted ─► enqueue an inference run
  • Recursion guard. Every email an agent sends is stamped with a hop count — 1 on fresh mail, the inbound message's count plus one on a reply. Human mail clients never echo the header back, so a person replying anywhere in the conversation resets the chain; only an unbroken agent-to-agent reply chain accumulates. An inbound email whose count has reached the limit (config.recursion_limit, default 50) is silently dropped — this is what stops two agents from replying to each other forever.
  • Routing. Recipients are gathered from both To: and Cc: — an agent copied on a reply-all is still reached — and each address is matched to an agent, honouring plus-tag subaddressing, so support+order42@thescoop.com still routes to Sundae at support@thescoop.com. Every addressed agent gets its own independent run: an email sent to both Sundae and Scout is handled by each on its own. When no address matches, the email bounces as unrouted.
  • Access guardrail. The sender is checked against each addressed agent's inbound access rules before the message reaches inference. This is how Scout stays reachable only from inside @thescoop.com. The check is per-agent, so one email can reach some of its addressed agents and be refused for others. See Security for how the layers compose.
  • Attachment limits. Oversized or too-numerous attachments are rejected at the edge, before anything is stored.

The Gateway only claims mail for domains Protege actually receives for, so mounting the engine never hijacks the host app's other inbound mail.

Outbound

Agents never talk to a mail transport. An agent's plain assistant text is never delivered on its own; the only way a message leaves the building is the send_email tool, which hands the composed mail to the Gateway. That single exit point is where the outbound guarantees live:

  • Identity is enforced. Every send is re-stamped with the sending agent's own address — an agent cannot impersonate anyone else. Sundae's replies always come from support@thescoop.com.
  • Threading is preserved. A reply inherits the original subject and threading, so it lands back in the same conversation rather than starting a new one.
  • Transport is chosen automatically. Console conversations stay in-app; real mail goes out over SMTP. The inference core doesn't know or care which.
  • History is recorded. The outbound message is persisted on its thread, with attachments stored by reference so a file is uploaded once and thereafter only pointed at.

Threading

A conversation is a thread, identified by the mail's own message-id and reference chain. An inbound message and the reply it triggers resolve to the same thread, which is what gives an agent conversational memory for free — the next turn can replay everything said so far (see Resolvers and the Harness).