Core Concepts

Connectors

How Ductor dispatches work to 100+ third-party providers with credentials encrypted at rest.

Connectors are how the executed stage of the clearing lifecycle reaches the outside world — the action inventory a Route, a workflow step, or an agent draws on to actually do the work. Ductor ships with implementations for a large catalog of third-party providers (browse the live directory at ductor.io/connectors), and the same machinery lets you add your own. A step names a provider and an action; Ductor resolves the credentials, runs the action, and records the result as a durable attempt. Each action is governed, not just callable: it carries typed semantics (mutation class, idempotency, retry, approval) so the runtime — and any agent, human or automated — knows what is safe to retry and what needs a human.

This page is the summary; the deep reference is its own section

For the full treatment — every auth type, the credential encryption scheme, the management API with real payloads, triggers and polling, and how to author your own provider — see the Connectors section. This concept page is the one-screen mental model.

The registries

Connectors are described by two registries:

  • ProviderRegistry maps a providerKey to a provider definition — what it is and how to authenticate to it.
  • ActionRegistry maps a (providerKey, actionKey) pair to an ActionSpec, which carries the function that actually executes the action.

At dispatch time a step names a provider and an action; Ductor looks up the ActionSpec and runs it with a resolved authentication context. Provider implementations live under infrastructure/connector/providers/.

Connections and auth

A ConnectionService resolves the AuthContext for a connection when a step is dispatched. Credentials are AEAD-encrypted at rest using XChaCha20-Poly1305, keyed by the base64 32-byte key in connector.encryption_key (infrastructure/connector/crypto/). Plaintext secrets never touch the database — the row holds ciphertext, and decryption happens in-process at dispatch time using the injected key.

Protect the encryption key

DUCTOR_CONNECTOR_ENCRYPTION_KEY decrypts every stored credential. Treat it like a database password: inject it from your secret manager, rotate it deliberately, and never commit it. Losing it means losing access to every stored connection; leaking it means exposing all of them.

Dispatch is durable

A connector call is not a fire-and-forget SDK call — it's a workflow step, which means it inherits the durable attempt model. The Coordinator dispatches the action to a Step Worker post-commit; the worker executes it and records the outcome as an eec_workflow_step_attempt row (the eec_ prefix marks Ductor's durable Enterprise Eventing Core tables). If the call fails, the step's retry policy governs the retry, and the Coordinator folds the eventual result back into run state. Because dispatch is at-least-once, a connector action can run more than once across a crash — so provider actions that mutate downstream state should pass an idempotency key where the provider supports one (see Idempotency & Exactly-Once).

Dispatch action (post-commit) Execute action Result Record attempt row Fold result into run state Coordinator Step Worker Provider

AI connectors

Two step types — ai_action (a single LLM inference call) and ai_agent (an agentic tool-calling loop with execution bounds) — route through the same connector machinery to an AI inference proxy. From the workflow's perspective an AI call is just another durable, retryable step with encrypted credentials and uniform observability.

Custom connectors

Adding a connector is a matter of implementing the action functions and registering the provider — no changes to the core engine. Connectors contribute to the runtime through fx option groups (for example, connector_interceptors), which is the same extension pattern used by routing strategies and workflow listeners. See Extension Points.

Why it's a connector, not an SDK call

Routing a step through the connector layer instead of calling a vendor SDK inline gives you four things for free:

  • credentials are encrypted and resolved centrally, not scattered through code;
  • every call is observable through the same metrics and tracing;
  • retries are governed by the durable attempt model from Coordinator & Step Workers;
  • and the call is tenant-scoped and rate-limited like everything else.

Where to go next