Trusted workers are a reserved design, not a shipped tier (ADR-0024, parked by ADR-0058). ADR-0024 decided that a reviewed, out-of-process MCP server in apps/connectors would plan requests and normalize responses while the gateway performed credentialed HTTP. That protocol is not implemented. There is no package, no image, no deployed service, and no connector pack declares tier: trusted_worker. The compiled WorkerExecutor still runs ADR-0014’s x-obol-upstream-authorization hand-off. Nothing leaks today, because no worker exists to receive the header. The rest of this page records the decision, the unbuilt protocol, and the shipped hand-off the executor would use if a worker were added. Do not treat either as a running product surface. The intended job is connector velocity on the messy minority of vendors, and nothing else. No Python and no per-vendor logic goes in the gateway process (invariant 2) — that decision is settled and deny.toml bans pyo3 by name.

What a worker is for

Under ADR-0024, a trusted worker would be a request planner and a response normalizer. That is the whole job. The tier decision would admit it only when exactly one of four things is true:
  • a mandatory 2–4 step sequence,
  • cursor pagination that must be joined server-side into one result,
  • a response shape needing a stable mapping, or
  • a request shape OpenAPI genuinely cannot express.
Pagination the agent can page itself is not a reason. Only mandatory joins are.

What is not a reason to write a worker

Older guidance — ADR-0007’s own text included — predates ADRs 0020 through 0024. Each of the following is gateway-owned by decision. A vendor whose request signing Obol cannot express as an auth profile is a reviewed exception requiring a new ADR, not a secret hand-off. AWS SigV4 is the standing example: it signs a canonical request with the secret, so either the gateway gains an AwsSigV4 auth profile or the vendor is unsupported.

The credential boundary

On the shipped path a worker does receive the vendor credential. ADR-0024 requires that it never should — but that protocol is not implemented, and the code still runs ADR-0014’s hand-off, sending the rendered credential in x-obol-upstream-authorization. See the shipped path below for exactly what crosses the hop today.What holds on both paths: a worker cannot assign trust, evaluate Cedar, mint a receipt, or choose a verification conclusion, and it never sees an inbound ob_live_ key.
Nothing is exposed by this today, because no worker exists to receive the header: apps/connectors ships no package and no image, and no deployment target defines a connector-worker service. The boundary below is what ADR-0024 requires before one returns.

Target design (ADR-0024, not implemented)

ADR-0024’s versioned protocol would be a bounded exchange in which the worker never holds a credential and never dials a vendor:
1

prepare

The gateway would send the invocation, tool, canonical arguments, bundle and plan digest, and idempotency key to the worker’s prepare phase.
2

RequestPlan

The worker would return a RequestPlan: a declared operation id, method, relative path, selected safe headers, and a bounded body. Never an absolute arbitrary destination, and never a credential.
3

Validate and dispatch

The gateway would validate the plan against the pinned ConnectorBundle — endpoint and method allowlists, schema, step budget, idempotency binding — then inject the Obol-vaulted credential and perform the HTTP itself.
4

Observe

The gateway would retain numeric status, selected headers, safe ids, body digest, and a temporary bounded body as gateway-observed transport evidence.
5

normalize

Where normalization is required, the gateway would send only the bounded response data the bundle allows to normalize. The worker would return a WorkerCallReport that the gateway converts into a DispatchObservation.
Multi-step connectors would use a bounded host-call loop declared in the bundle, each step independently validated and receipted as part of the same invocation, with worker deployments denying public egress and the gateway the only caller over the private service boundary. The split that would result: worker-derived semantic fields connector_attested; HTTP status, digest, and gateway-selected response fields gateway_observed. On today’s code path the whole observation is connector_attested instead — see Evidence a worker route can carry.

The shipped path is still ADR-0014

Treat every new worker as a credential-custody decision. ADR-0024’s prepare/normalize/RequestPlan/WorkerCallReport protocol is not implemented in this tree — there is no RequestPlan and no WorkerCallReport type in the workspace. obol-types/src/worker.rs still carries the ADR-0014 WorkerCallRequest / WorkerCallResponse shape, and WorkerExecutor sends the rendered vendor credential in x-obol-upstream-authorization (obol-mcp/src/worker/transport.rs). The worker calls the vendor itself, and netguard is skipped for Worker targets because the gateway is not the one dialling out (obol-gateway/src/hooks.rs).Do not extend the ADR-0014 shape. ADR-0024 supersedes it; the code has not caught up.
Under the shipped ADR-0014 hand-off, two credentials cross the gateway→worker hop and neither is an Obol virtual key — workers never see inbound ob_live_ keys:
  • Authorization: Bearer <service JWT> — minted per call by ServiceJwtMinter with aud = obol-connectors, 60-second TTL, HS256 on the shared OBOL_SERVICE_JWT_SECRET. It authenticates the gateway.
  • x-obol-upstream-authorization — the rendered vendor credential, present only when the connection has one, marked sensitive, never logged, and never persisted by the worker.
The credential rides in a header rather than the body deliberately: bodies get logged, buffered, and snapshotted by frameworks far more often than headers, and MCP handlers see bodies but not headers. The only log line carries worker, connection, tool, idempotency_key, last4, and an invariant test (upstream_authorization_never_logged) holds that line. Retry policy under ADR-0014 is exactly one retry after 200 ms, and only when the worker declared the error retryable or the failure happened before the request was sent. A post-send transport failure is never retried — the worker may have executed. ADR-0031 moves retry reservation into the gateway: each attempt is reserved before dispatch, and a reservation failure means no second attempt is sent.

Evidence a worker route can carry

WorkerExecutor stamps EvidenceTrust::ConnectorAttested on the observation (obol-mcp/src/worker/mod.rs). Two consequences follow.
  • connector_attested verifies only by explicit permission. The EffectSpec must set trust: allow_connector_attested, or trust_is_authoritative rejects it. A worker tool whose spec says gateway_observed_only can never verify on today’s code path, because the whole observation is attested rather than just its derived fields — the gap ADR-0025’s per-source trust exists to fix.
  • connector_attested may never contradict. Only gateway_observed and vendor_signed evidence may assert contradicted.
A worker’s route is also the only place connector_attested may originate. Routing federated evidence through an adapter in apps/connectors to acquire it is laundering and is forbidden by name (ADR-0030): a federated route may never yield connector_attested. See Evidence classes.

Worker obligations

These are the ADR-0014 contract the compiled WorkerExecutor expects. No worker exists to honour them; they would apply if one were added on the shipped path, not under ADR-0024’s unbuilt protocol.
  • An idempotency store keyed on idempotency_key. The gateway reuses the same key on a retry precisely so the worker’s store collapses duplicates. This is load-bearing, not optional.
  • No retries on non-idempotent writes without a vendor idempotency key.
  • A stable error envelope. Vendor errors map to {code, retryable, message} plus vendor_http_status; ok: false becomes an upstream dispatch error. A missing structuredContent is a transport error.
  • Never log an Authorization header or a PAN.
Note that idempotency: unsupported forecloses redispatch entirely: gateway_retry_allowed computes vendor_retry_safe as “the binding is not Unsupported,” so an ambiguous responded-but-unknown result is never re-attempted. That is correct behavior, and a real product limitation worth recording in the connector note.

Guardrails

  • Hand-written workers are capped at five, each with a named reviewer, enumerated in the connector registry. A sixth proposal is a scope conversation.
  • A worker is not a new deployable. When apps/connectors is built it will be one image hosting many connectors behind tool prefixes, scaled and circuit-broken independently — not one deployable per vendor. A standalone compiler, registry, catalog adapter, or verification service is prohibited without a new ADR (ADR-0005, ADR-0022).
  • Conformance fixtures are mandatory, and 2–4 days plus a security review is the realistic cost. Compare that against an overlay, which is hours.

There is nothing to deploy yet

The tier is parked (ADR-0058). apps/connectors/ contains one README and nothing else. There is no package, no lockfile, no obol_connectors module, and no Dockerfile — the previous Dockerfile was a build recipe for source that never existed, and was removed along with the connectors service in Compose, infra/fly/connectors.fly.toml, and Helm’s connectors.yaml with its values block, ServiceAccount and NetworkPolicy. No deployment target sets OBOL_CONNECTORS_URL. No connector pack under packages/connectors/ declares tier: trusted_worker either — all 103 bundles are native/obol. Every shipped connector executes in the gateway’s in-process OpenAPI executor or federates over remote MCP; neither path touches this directory. apps/connectors/README.md records the four preconditions for an image to return: ADR-0024’s protocol frozen in packages/proto, a Python package serving /mcp/{worker} and /healthz, a Dockerfile citing ADR-0024 rather than ADR-0014, and deployment wiring restored across all three infra/ targets in the same change.