FederatedBrokerRef), never the underlying vendor
secret.
Federation is how coverage arrives without the iPaaS treadmill (ADR-0030, PRD §6). It is
cheap breadth bought with evidence, and the price is exact.
When a connector is federated
Either of the first two rows of the tier decision fires:- A third party holds the vendor credential and executes the call →
custody: federated,kind: catalog_remote,broker_refset. - The surface is an MCP server rather than an HTTP API →
target: mcp_remote, even when Obol vaults the MCP bearer token, because the upstream still owns the vendor connection.
SnapshotValidationError::FederatedCustodyOnNativeTarget rejects a connection with
custody: federated on any target other than mcp_remote, so a brokered route cannot be
authored over an OpenAPI target and quietly keep a gateway_observed stamp.
Custody: the customer’s own OAuth client
Invariant 8 binds the vendor-facing identity, so it forbids a shared broker OAuth app across tenants just as it forbids a pooled vendor account.- No pooled broker account and no pooled broker OAuth application. Activation fails
if a broker account identifier appears under more than one
workspace_id. - Production destructive tools require the customer’s own OAuth client at the broker.
- No vendor secret crosses either boundary — none imported broker→Obol, none exported Obol→broker.
- Custody mode, connector tier, achieved evidence ceiling, and idempotency-propagation mode are non-nullable on every receipt and snapshot tool entry, and are Cedar-visible.
The frozen catalog
packages/connectors/catalog/ holds one frozen index and one overlay template applied
to every catalog-sourced tool — deliberately not a pack per vendor.
index.json is generated by generate.py from two inputs: vendored public broker
catalogs for the federated long tail, and the reviewed native packs on disk for the
routes Obol custodies itself. Each entry names its sources and their auth modes:
github and stripe carry a default_tier that is not
federated — federation is listed for them as an additional source, never the default.
Catalog bundles are reviewed data, not live authority. Every catalog-sourced bundle
is frozen, content-addressed, and workspace-qualified. Conformance records describe
the mappings actually tested; eligible status does not certify conformance. Broker runtime
metadata is never authority; detected drift quarantines the binding and never
silently upgrades it. Broker-side schema and slug churn must be detected by digest
comparison on every snapshot build, because neither broker offers a deprecation window or
a schema-stability commitment.
All structurally valid Nango and Composio capability bindings are eligible under
cap.* (ADR-0063). Eligibility does not certify their operation mapping or connect
an account. Federated completion still returns 409 broker_verification_unavailable
until authenticated broker-account verification exists; the connection stays
pending and its handoff unconsumed.The generic federated EffectSpec
Because there is no per-vendor federated pack, there is no per-vendorEffectSpec
either. packages/connectors/catalog/effects/ holds two, applied as a template.
Read — federated-read.json. No dispatch classification, no upstream id, no
verification. A read concludes not_required.
federated-mutation.json. Conservative status classification, one
existence assertion on the broker’s object id, and idempotency: unsupported, which is
the honest binding because neither broker offers an idempotency primitive.
trust field on a response binding says what the spec is willing to accept; it
does not raise what the route stamps. A federated observation is untrusted, so the
binding’s assertions cannot reach verified however the tool self-reports — proven by
remote_mcp_false_proof_remains_inconclusive in the gateway’s verification tests.
Capability routing vs concrete federated tools
Federated tools can be called by their concrete names and can participate incap.* routing (ADR-0063). A binding must resolve to an actual tool on a matching
active connection and pass workspace and request controls. Generated broker
operations may use inferred {slug}.read / {slug}.mutate names; the frozen
catalog does not certify their per-vendor request or response shapes.
Native-only activation, mandatory curation, and mandatory native conformance
fixtures no longer restrict the candidate pool. Declared feature coverage remains
accurate, concrete tools remain the authorization subject, and invalid requests
or responses still fail validation. Broker metadata never assigns receipt trust
or authorizes execution.
The evidence floor is unchanged: a purely federated invocation with no separately
authenticated asynchronous stage can never read verified.
Evidence classes on a federated route
Per-source trust (ADR-0025), for a federated connection:broker_attested requires a pinned, workspace-scoped, versioned broker signing identity —
signing secret, mTLS identity, or signed assertion — with rotation overlap, timestamp
tolerance, and replay identity checked before parsing. Obol authenticating to a broker
never constitutes attestation; the direction that matters is broker→Obol. Where the
identity is channel-level only, the receipt records attestation: channel rather than
attestation: signed_assertion, because only the latter is non-repudiable.
And a signed assertion proves non-repudiation, not correctness. If a broker signs only
its own normalized output, it has restated its claim under a key. To mean more, the
assertion must bind workspace, broker connection, reflected invocation id, a digest of
the vendor request actually issued, the vendor’s numeric status, the vendor object id,
and a nonce.
Both broker_attested and untrusted fail the verifier’s authoritative-trust check, so
a federated mutation concludes inconclusive. Neither may assert contradicted —
a hostile federated upstream must not be able to raise a customer-visible alarm on a real
effect. See Evidence classes.
The two paths to a verified federated write
Neither is reachable today, but for different reasons — and the difference matters. Hybrid custody is genuinely unbuilt and is deferred to phase 2 (docs/PRD.md §12).
Signed vendor-webhook ingress, by contrast, does ship: the /hooks/... endpoint,
its reviewed signature profiles, correlation and receipt revision are all implemented.
What blocks path 2 is upstream of the evidence: no federated connection can be completed
at all while federated-complete returns 409 broker_verification_unavailable. Do not
promise either in a bundle you ship today.
- Hybrid custody — Obol vaults its own read-only credential for the same account
while the broker holds the write credential, yielding a
gateway_observedreadback. Available through Nango, not through Composio, which redacts tokens universally. It requires amending the connection contract’s exclusive either an Obol-encrypted credential or a federated upstream reference into a pair. - Direct vendor webhooks —
vendor_signed, needing only that the customer register Obol’s endpoint at the vendor, which most vendors permit alongside the broker’s.
What a broker is not allowed to do
Invariant 9: catalog brokers are vendors, never authority. Concretely:- Never delegate a Cedar decision to broker-side filtering. Composio’s execution allowlist is documented as bypassable and its schema-modifier hooks do not run over MCP. Authorization is Obol’s, on every tier.
- Never launder provenance through a catalog adapter. Routing federated evidence
through an adapter in
apps/connectorsto acquireconnector_attestedis laundering and is rejected by name: a federated route may never yieldconnector_attested. That class means a reviewed first-party worker under Obol’s control. - Never treat broker catalog text as instruction. Any surface a broker controls can carry text aimed at a model — Composio’s own pricing page ships a block addressed to AI agents. A bundle is frozen, content-addressed, reviewed data.
- Never accept broker-asserted authority in a response. The gateway screens a
connect-session response for authority-shaped keys —
target,policy,evidence,receipt,idempotency,idempotency_key,retry,credential— at any depth and fails the call if one is present (ADR-0038). Fields Obol simply ignores travel no further than the parse; a broker asserting authority is a fact worth failing on. - Never let a broker retry. Gateway-side idempotency is unconditional and
broker-side retries are disabled on every call. Propagation is declared honestly per
binding, and
propagation: noneis a policy-deniable attribute. An ambiguous federated mutation terminates the invocationinconclusiveand is never auto-retried or failed over.
Operating a federated connector
- Use the raw proxy, never the normalized action endpoint, where a broker offers
both — it is the only shape that preserves the vendor’s numeric status. Nango’s
proxy-forward-all-response-headersaccount flag is a contractual precondition: without it the vendor’s request id never reaches a receipt. - Federated capacity is not the customer’s capacity. Broker rate limits pool across
tenants, so a federated candidate degrades based on strangers’ behavior.
shared_quotais a declared binding attribute and a routing filter input, not an operational footnote. - Federated cost is a stack, and often unknowable. The broker component is computed
from workspace configuration rather than looked up, and the vendor component may be
genuinely unavailable — so cost attribution carries
bundled_in_brokeras a distinct value rather than a fabricated number. - Independence is a requirement. No catalog is a hard dependency of authentication, authorization, receipt minting, metering, or the vault. A broker outage degrades the connectors it serves and nothing else, and every catalog-sourced connector has a documented graduation path to native or trusted-worker custody.
- Self-hosting a broker is closed. ADR-0030 rejects self-hosting Nango on licence grounds, and Composio’s platform is enterprise-gated. An Enterprise self-host deployment using federation requires the customer’s own broker account.
- State the ceiling at connect time, not at audit time. If a dashboard collapses five evidence classes into one green check, the distinction has made verification weaker rather than stronger. Render a federated write as executed — not verified.