Reach carlos from anywhere.
When the daemon is enabled, carlos can send notifications, surface approval prompts, and accept replies through messaging channels. ntfy and Telegram ship; Signal is a stub. Future channels (Discord, Slack, email) follow the same adapter contract.
Architecture (Phase G1)
The broker is daemon-owned. Each channel implements a small adapter contract; the broker handles routing, retry, dedupe, and event streaming.
- Daemon-owned broker: one process, one queue, one event log.
- Adapter contract: each channel implements the
Adapterinterface (Deliver,Resolve, optionalHandler() http.Handler). - Canonical envelopes:
Notification,Decision,Reply. - Retry + dedupe table: replayed tokens and duplicate deliveries are absorbed at the broker.
- EvtGateway events: streamed through the same event log every other surface consumes.
FakeAdapter: ships for tests; the broker is exercised without a live channel.
type Notification struct { // outbound: push to a channel ID string Title string Body string Actions []Action // up to 3 inline buttons } type Decision struct { // inbound: user resolved an approval ID string Choice string // matches Notification.Actions[i].ID } type Reply struct { // inbound: free-text reply ID string Text string }
Adapters (shipped)
| adapter | status | notes |
|---|---|---|
| ntfy (G3) | shipped | JSON publish plus up to 3 action buttons plus HMAC-SHA256-signed action tokens. Inbound replies via Handler() http.Handler. |
| Telegram (G2) | shipped | Bot API long-poll. MarkdownV2 messages. Inline keyboard for actions. callback_query resolves to a Decision envelope. |
| Signal (G6) | stub | not yet implemented in enabled mode. The adapter is wired so the routing matrix can reference it once the bridge lands. |
| Custom | contract | Implement the Adapter interface; register via the daemon. Same envelopes, same retry plumbing. |
Approval-queue routing (G4)
When the manage view surfaces an item awaiting input, the broker can deliver to whichever channel the user wired. Inline keyboard and action buttons resolve back into a Decision envelope that hits the same approval surface as an in-TUI prompt. One queue, two surfaces.
Future: /away mode gates gateway delivery so notifications only leave the box when you're actually away. Until then, gateway delivery is always-on once enabled. Plan accordingly if your phone is loud.
Setting up: onboarding wizard
Four-stage wizard. carlos gateway add runs the same flow standalone (Phase G5).
- Enable. Switch on the broker; defer until later if you're not ready.
- Channel picker. ntfy, Telegram, or Signal stub.
- ntfy. Server URL plus topic plus (optional) HMAC secret for signed action tokens.
- Telegram. Bot token plus chat ID (the wizard hints how to get both).
Inside the TUI onboarding, gateway defaults to "set up later" (Phase O-5). Skipping is the safe default; nothing breaks if you wire it up after the fact.
Testing the wiring
carlos gateway test <channel>
Dispatches a fixed Notification envelope through one adapter via the UDS gateway-test verb. Successful delivery proves token, network path, and adapter wiring without waiting for an organic event.
Action tokens (ntfy)
ntfy buttons fire URLs back to a configured handler. To survive untrusted middlemen, action tokens are HMAC-SHA256-signed with the topic secret. Replayed tokens are rejected via the dedupe table, so a refreshed-tab double-tap can't double-resolve an approval.
Onward (Phase G deferred)
- SIGHUP hot-reload of routing and retry config.
- Routing-matrix onboarding screen.
- G7 custom HITL side-app (separate repo, deferred until the Telegram UX ceiling is concrete).
- Multi-user gateway identity (the
gateway_identitiestable lands when carlos goes multi-user). - Discord, Slack, and email adapters; each is a small slice against the broker contract.
Related
Schedule covers the daemon-fired runs that route notifications through this gateway. Sub-agents covers the supervisor surface that emits the approval events the broker delivers. Slash commands documents /gateway and the future /away toggle; config covers every gateway field.