GatewayAdapter registry for client configuration and
inference-proxy behavior. Adding an adapter makes the gateway type recognizable;
a production integration also needs a provisioner, deployment configuration,
and end-to-end evidence.
Gateway adapters are trusted, in-tree Rust code. An organization selects one
with gateway.type, but policy cannot load executable adapter behavior at
runtime. Unknown types fail during Control API and inference-proxy startup and
again at the client boundary.
Two gateway flows
The control plane turns an operator choice into agent configuration:Contract ownership
gateway.type selects the compiled adapter. gateway.provisioner.type selects
credential lifecycle behavior and may use a different name.
Compiled adapter contract
Every method is pure, so the same static adapter can be used safely by all Blue binaries:GatewayRoute contains only the Blue inference-proxy URL and inference JWT. The
dispatcher attaches the harness-owned AuthPlacement and wire_api afterward,
so a gateway cannot replace those decisions. Route and wiring debug output
redacts the token.
UpstreamCredentialPlacement declares bearer authentication or a named header;
it never contains the credential itself. The proxy strips Authorization,
X-API-Key, and the adapter-declared credential header before applying exactly
one resolved credential. Named credential headers cannot be hop-by-hop headers,
Host, or Content-Length.
InvalidCredentialReason is a provider-neutral closed enum: NotFound,
Blocked, or Revoked. Return a reason only when the upstream response proves
the credential is unusable. Expiration remains a Control API decision.
inspect_response_status prevents the proxy from buffering ordinary streaming
responses. For selected statuses, the body is inspected only when its declared
length is at most 64 KiB.
During the compatibility release, internal service payloads carry the new
upstream_credential and reason fields alongside the deprecated
virtual_key and classification fields. New services accept either shape
and reject conflicting dual values. Do not use the deprecated names in new
integrations.Choose the integration path
- Bearer-compatible gateway
- Different upstream behavior
Use the existing bearer placement when the upstream accepts the resolved
credential as
Authorization: Bearer …. Return the incoming path unchanged
when the gateway exposes the same API paths as the agent. You still must
define which response statuses are inspected and map only definitive
invalid-credential responses to NotFound, Blocked, or Revoked.Add a gateway
1
Specify the upstream behavior
Choose a stable lowercase key. Record supported agent protocols, upstream
paths, credential placement, and the exact status/body evidence that proves
a credential is invalid. Treat rate limits, model denial, and transient
authentication infrastructure failures as ordinary upstream responses, not
invalid credentials.
2
Implement the complete adapter
Add
crates/gh-gateway/src/<gateway>.rs and implement every method. Validate
required client runtime fields in client_route; never perform I/O or store
administrator configuration in the adapter. Add one static instance to
GATEWAY_ADAPTERS in lib.rs.3
Provide credential lifecycle support
Prefer the existing digest-pinned executable protocol when it can implement
the provider’s ensure and revoke calls. Follow
Custom gateway provisioners. Add a
built-in
GatewayProvisioner module and Control API Cargo feature only when
the provider integration must ship in the public server binary.4
Extend policy only when necessary
Reuse
type, proxy_url, token, and auth_style when possible.
auth_style is retained for compatibility and must be bearer; it describes
agent-to-Blue authentication, not the adapter’s upstream credential header.
New non-secret client fields require coordinated changes to gh-service,
the canonical OpenAPI contract, Control API personalization, and the docs
snapshot. Provider credentials never enter client policy.5
Wire the deployment
Set the same adapter key in
gateway.type and HARNESS_GATEWAY_TYPE.
Compose passes the latter to the proxy. Helm uses blue.gatewayType and
injects it into both the Control API and proxy; the value is required when
blue.enableInferenceProxy=true. Add the upstream URL, provisioner
configuration, and secrets through the existing deployment mechanisms.6
Certify the integration
Add registry and adapter unit tests, proxy forwarding tests, provisioner
contract tests, deployment rendering checks, gateway-mode golden plans for
every harness, and an end-to-end request through the real gateway. Retain
governance-only coverage to prove it gains no gateway dependency.
Change checklist
Verification
At minimum, demonstrate:- Unique, nonempty lowercase registry keys and lookup from all three consumers.
- Rejection of unknown gateway types and non-
bearerclientauth_style. - Correct client route normalization without exposing an inference JWT in debug output.
- Correct upstream path and credential header, with incoming authentication headers removed.
- Unchanged streaming for uninspected responses and bounded inspection for adapter-selected statuses.
- Precise provider-neutral invalid-credential reasons without treating authorization or availability errors as revoked credentials.
- Replacement—not appending—of an attacker-supplied adapter credential header.
- New-only, legacy-only, matching dual, and conflicting dual internal wire payloads during the compatibility release.
- Provision, retain, rotate, revoke, invalidate, and bounded-recovery behavior without credentials in logs.
- Successful inference and user attribution through every supported harness.
Invariants
- The server decides whether gateway mode exists; a client may only downgrade to governance-only mode.
gh-configremains the only writer of agent configuration files.- Session-bound inference JWTs may reach clients. Upstream credentials and administrator secrets may not.
- Adapter decisions are pure and contain no credential bytes.
- Governance-only clients gain no hard dependency on the proxy, provisioner, database, Redis, or upstream gateway.
- Native CLI arguments, terminal behavior, signals, resize handling, and exit codes remain unchanged.
