Pillar 3 · Connector (the credentialed edge)
Parent: key-designs
Every credentialed contact with the outside world goes through one layer: connector.Hub + category slots. Consumers (agent capabilities, platform features, the IM bridge; the job-loop is still not a consumer) see fixed category contracts (CalendarProxy, MailProxy — backend/internal/connector/contract/) and receive a call handle — credentials never cross the boundary (AES-256-GCM at rest via cryptobox). Two kinds: openapi (per-SaaS HTTP: spec + JSONata binding, credential form derived from securitySchemes) and protocol (SMTP/CalDAV: built-in Go impls, protocol_smtp.go / protocol_caldav.go; no IMAP). A third protocol connector, telegram (category im), shipped 2026-09-04 (8c819f26d, protocol_telegram.go): it holds only the bot token — no category contract, no Verifier — and the separate im-bridge reads it via GET /internal/im/config (cmd/server/boot_im.go). connector.Service (service.go + svc_*.go; the old connectorsvc package was merged into it on 2026-07-26, 1bc9ba8b0) is the admin plane (creds/connect/oauth/activate/disconnect); retry supplies per-call-class policies; egress/SSRF guards fence the exits. Capability dependencies (Requires:["calendar"]) resolve through capreg/depresolver.go — unmet dep ⟹ capability hidden, fail-closed.
Status: proxy layer landed first; installable landed (upload flow real; red-contract estate = 66 connector-*.spec.ts files at 36789537d; fixme tail cleared; the TODO-impl mock-infra gaps were closed on 2026-07-03, 059dc5c13 — 0 remain); sync-mode landed (2026-07-08, d51805372, backend/internal/connector/sync.go) — connector.NewSyncConnector + the SyncIngester capability made ingest a first-class connector kind, and /obsidian/import now delegates through the interface rather than calling SyncVault inline (routes/admin/obsidian.go:218; DTOs rename 1:1 at the boundary, so the connector layer stays usecase-independent). 17 sync-*.spec.ts files / 118 tests at 36789537d, behaviour identical. Deliberately deferred, and still so at 36789537d (NewSyncConnector has no caller outside sync.go): registering it in the Hub for an admin-UI card, which would need cmd→server dependency threading — the sync-mode abstraction is load-bearing without it. (Connector ≠ MCP; action vs sync — confusables.)
Superseded — everything-is-a-block (eiab), 2026-09-13 → 2026-09-18 (after the 36789537d stamp above)This pillar no longer exists as a separate module:
backend/internal/connector/now holds 0 Go files. Every connector became a sandboxed JS block (backend/blocks/{caldav,smtp,google-calendar,telegram}/manifest.yaml+ a JS MCP server); the openapi kind became a generic openapi-runtime block, the protocol impls (protocol_smtp.go/protocol_caldav.go) and the category contracts (CalendarProxy/MailProxy) are retired, and dependency resolution moved tobackend/internal/plugin/effect/registry_deps.go. Credentials-at-rest + egress/SSRF fencing survive at the block-sandbox boundary. The "sync-mode landed" claim is doubly stale: there is no connector module, and vault sync (SyncVault) is still a bespoke admin endpoint (routes/admin/obsidian.go), never a connector kind (roadmap 1e). The Status line and mermaid below describe the pre-eiab shape.
Connector lifecycle (state)
Pinned by red tests: one active connector per category slot; disconnect retains creds; agent-tool exposure is a separate per-op opt-in on top of Active.
Children
- connector-diagram-coverage — the two-sided class diagrams (registry side vs caller side) + coverage checklist.
- connector-plugins — landed: kinds, contract/binding/runtime layers, JSONata-only, the red-contract estate.
- connector-deps —
Requires:[...]named providers, fail-closed hiding, the formal MCP↔Connector interface. - connector-egress-guard — SSRF/exfiltration fences.
- idempotent-booking — the booking consumer's idempotency discipline.