---
title: "Connector tiers"
description: "The ordered tier decision: Gate 0 on risk class, then the first-match table that fixes custody, executor, and evidence ceiling for the life of the connector."
---
The tier is not a preference. It fixes, for the life of the connector, who holds the
vendor credential, who executes the call, and what evidence trust every receipt can ever
carry. Decide it explicitly, cite the vendor brief, and record the reason in the pack's
`CONNECTOR.md`.
You do not choose a tier directly. You author `custody`, `kind`, and `target`, and
`ConnectionSnapshot::tier()` reads them back — see
[Connectors overview](/connectors/overview) for the derivation. The table below tells you
which triple to write.
Governing ADRs: **0020** (three tiers, `ConnectorBundle`; superseded in part by 0030),
**0024** (worker protocol, accepted-not-implemented; parked by ADR-0058), **0030**
(federated catalogs as primary coverage, Proposed — but its authoring rules already bind
through `AGENTS.md` invariant 9 and PRD §6), **0025** (per-source evidence trust,
Proposed).
## Gate 0 — risk class, before the table
Run this first. It decides which half of the table you are allowed to land in.
**A prod destructive tool, or an official/high-risk connector, is native or trusted
worker. Federation is default-deny there.**
The trusted-worker landing is parked (ADR-0058): there is no worker deployable, so
choosing it today is proposing to build one, not landing on a running tier. A prod
destructive tool that cannot be native has nowhere to go until ADR-0024 is built.
ADR-0030's policy supremacy clause: prod destructive on a federated route is
default-deny, and permitting it is an explicit recorded workspace decision. The reviewed
Stripe and GitHub paths stay native or trusted-worker. If the vendor is reachable *only*
through a broker or a remote MCP server, the tool cannot ship as prod destructive —
ADR-0024 rejects running connectors as remote third-party MCP because that "cannot
provide native custody or gateway-observed transport evidence." Say so and stop.
**Otherwise, for long-tail breadth, prefer federated.** ADR-0030 adopts Nango and
Composio as v1 brokers and makes federation the primary path to breadth. Building a
native or worker connector for a long-tail vendor is the treadmill ADR-0020 and PRD §6
both forbid.
Federation is cheap coverage bought with evidence. ADR-0030 states the price exactly:
for a purely federated invocation with no asynchronous stage, the fraction of receipts
that can ever read `verified` is exactly zero, enforced at validation time. **Do not
federate anything whose value is the receipt.**
## The decision table
Ordered. First match wins. Answer each row from the vendor brief produced by
[research](/connectors/adding-a-connector), not from memory — walking it from memory is
how a form-encoded body or an OAuth-only vendor gets discovered *after* the tier is
recorded.
Nango, Composio, another catalog, or a customer-run server. If yes → **federated**, stop.
Author `custody: federated`, `kind: catalog_remote`, `broker_ref` set. Obol stores only a
workspace-scoped upstream credential or an account-scoped reference. Pooled broker
accounts and a shared broker OAuth app are forbidden ([invariant
8](/concepts/invariants), ADR-0030) — a federated connection uses the customer's own
OAuth client.
If yes → **federated**, stop, even when Obol vaults the MCP bearer token.
Author `target: mcp_remote`. `tier()`'s own doc comment puts it plainly: remote MCP is
federated even when Obol vaults the MCP bearer, because the upstream still owns the
vendor connection. `RemoteMcpExecutor` stamps `EvidenceTrust::Untrusted` and returns no
numeric vendor status and no upstream id
(`apps/gateway/crates/obol-mcp/src/relay/mod.rs`).
One request whose arguments map to path, query, header, or body by JSON Schema, against
a declared host allowlist, with Obol holding the vaulted credential. If yes → **native**.
Author `kind: openapi`, `target: open_api`, `custody: obol`. This is the strongest
observation boundary: `OpenApiExecutor` stamps `EvidenceTrust::GatewayObserved`. See
[Native connectors](/connectors/native).
Fixed GraphQL is native: declare N `POST /graphql` operations with the query document
pinned in the bundle body and arguments filling variables. It is not a worker reason.
(a) a mandatory 2–4 step sequence, (b) cursor pagination that must be joined server-side
into one result, (c) a response shape needing a stable mapping, or (d) a request shape
OpenAPI genuinely cannot express. If yes → **trusted worker**.
Author `target: worker`, `custody: obol`. Under ADR-0024 the worker would plan and
normalize and the gateway would perform the credentialed HTTP. **That protocol is not
implemented** and there is no worker deployable, so choosing this tier today means
proposing to build one, not adding to something running. The shipped executor would still pass the credential to the
worker (ADR-0014). See [Trusted workers](/connectors/trusted-workers).
**Stop.** Propose a new ADR. ADR-0024: unsupported signing or native-SDK behavior stays a
reviewed exception requiring a new ADR, not a raw secret hand-off.
## What is no longer a reason to write a worker
ADR-0007 and older guidance predate ADRs 0020–0024. Each of the following is
gateway-owned by decision; none justifies tier 2.
| Old reason | Where it lives now |
|---|---|
| OAuth exchange and refresh | Gateway (ADR-0023). See [OAuth](/security/oauth) |
| Webhook receipt and signature verification | Gateway (ADR-0021, ADR-0022). Raw webhooks are signature-checked by a trusted boundary before any mapping code sees them |
| Credential injection, SSRF, egress policy, redirects | Gateway (ADR-0024 destination, ADR-0020). **That protocol is not implemented** — on the shipped ADR-0014 path, netguard is skipped for Worker targets because the gateway is not the one dialling out |
| Idempotency keys, retries, receipts, redaction | Gateway (ADR-0019; ADR-0031 gives the gateway the worker's retries too) |
| Pagination the agent can do itself | Not a reason. Only *mandatory* server-side joins are |
## What each tier costs and buys
The trusted-worker column mixes the **ADR-0024 target** with today's compiled executor.
**That protocol is not implemented.** On the shipped ADR-0014 path `WorkerExecutor`
stamps `connector_attested` on the whole observation, and there is no worker deployable.
See [Trusted workers](/connectors/trusted-workers).
| | Native | Trusted worker (parked) | Federated |
|---|---|---|---|
| `TargetSpec` | `OpenApi { base_url, spec, allowed_headers, body_encoding }` | `Worker { worker }` | `McpRemote { url }` |
| Executor | `OpenApiExecutor` (`obol-httpx`) | `WorkerExecutor` (`obol-mcp`) | `RemoteMcpExecutor` (`obol-mcp`) |
| Trust stamped on the immediate observation | `gateway_observed` | `connector_attested` | `untrusted` |
| Per-source trust (ADR-0025) | transport and derived both `gateway_observed` | ADR-0024 target: transport `gateway_observed`, derived `connector_attested`. Today the whole observation is `connector_attested` | immediate facts `untrusted`; readback `untrusted` unless a workspace-scoped identity is authenticated → `broker_attested` |
| Routing evidence ceiling | `gateway_observed` | `connector_attested` | `broker_attested` |
| Can reach `verified` | Yes | Only with `trust: allow_connector_attested` on the `EffectSpec` | **No** |
| Can `contradict` | Yes | No — only `gateway_observed` and `vendor_signed` may contradict | No |
| Vendor credential custody | Obol vault, injected on the outbound hop | Obol vault (see the note in [Trusted workers](/connectors/trusted-workers)) | Third party; Obol holds a `FederatedBrokerRef` only |
| Enterprise self-host | Full | Parked — no deployable (ADR-0058) | Requires the customer's own broker account |
| Marginal effort | ~1 h without an overlay; +4–8 h engineering and ~2 h review with one | 2–4 days plus security review and conformance fixtures | ~1–2 h, plus drift review and a per-call broker fee |
The routing ceiling row is enforced, not advisory:
`apps/gateway/crates/obol-types/src/routing.rs` computes the maximum trust per tier and
excludes any binding whose declared `evidence_ceiling` exceeds it, with reason
`EvidenceFloor`.
## Guardrails
- **Hand-written workers are capped at five**, each with a named reviewer. A sixth
proposal is a scope conversation, not an authoring task.
- **Federated tools still execute.** A remote MCP server can run a destructive tool; its
result is simply never `verified`. Gate those with [Cedar and
approvals](/policy/approvals), and render them as *executed — not verified* rather than
as a green check.
- **Freeze federated schemas.** Remote MCP tool lists are introspected once and frozen
into a reviewed workspace snapshot, never trusted to change during a call (ADR-0020).
Re-introspect on a schedule and land drift as a reviewed republication, never a hot
swap. New tools arrive invisible until policy names them.
- **Revocation is two-step for federated connections** and must be disclosed as such:
Obol-key revocation covers the Obol hop only; control must call the broker's disconnect
API (ADR-0030).
- **Namespacing** (ADR-0015): `{connector}.{tool}`, split on the first dot. Connector
prefixes are validated dot-free and `[a-z0-9_-]`; upstream names may contain dots
(`stripe.charges.list` → connector `stripe`, upstream `charges.list`). Identities are
capped at 256 characters.
## Known vendor blockers
- **AWS (S3, SQS, SigV4) breaks the ADR-0024 model.** SigV4 signs a canonical request
with the secret, which a worker must never receive under ADR-0024. **That protocol is
not implemented**; on the shipped path the worker would receive the secret (ADR-0014).
Either the gateway gains an `AwsSigV4` auth profile — a bounded standard scheme — or AWS
is unsupported. Both need a decision on the record; neither is a secret hand-off.
- **Composio is barred from the destructive path** (ADR-0030). Its credential custody is
closed on every tier, so hybrid custody is structurally unavailable, and its normal
execution path returns a computed `successful` boolean with no vendor status and no
typed vendor object id. Use it as a read-biased long-tail source only.
- **Nango requires the `proxy-forward-all-response-headers` account flag** — ADR-0030
calls it a contractual precondition, not a configuration detail: without it the
vendor's request id never reaches a receipt. Use Nango's raw proxy, never its
normalized action endpoint, because the proxy is the only shape that preserves the
vendor's numeric status.
- **Form-encoded bodies are no longer a native blocker.** ADR-0034 added `body_encoding`
to `TargetSpec::OpenApi`, the bundle, and the overlay; `obol-httpx` writes
bracket-nested `application/x-www-form-urlencoded`. Stripe and Twilio ship as native
packs.