ADR-0025: AuthBridge (Rosso Cortex) deployment topology — shared LLM gateway, per-sandbox egress proxy
- Status: Accepted
- Date: 2026-07-10
- Deciders: Serverless Harness team
- Spec:
../specs/2026-07-10-authbridge-egress-control-plane-poc-design.md - Refined by: ADR-0026 — RC1 injection is a dedicated
static-injectplugin, superseding the "injection uses the statelesstoken-brokerplugin backed by a smallstatic-broker" clause in the Decision below. The topology decision (shared AB1Deployment/Servicevs. per-sandbox AB2) is unchanged.
The zero-trust credential plane (Phase 2, Z-prefix) is being reframed around Rosso Cortex /
AuthBridge as the concrete injection and control mechanism, for a single-tenant PoC. AuthBridge is
a Go HTTP plugin pipeline that can run credential injection (token-broker/token-exchange) and
control plugins (SPARC grounding, IBAC intent) on the same egress hop. Adopting it forces a question
the earlier specs did not settle uniformly: where does AuthBridge physically sit?
Two harness egress hops need it, and they differ:
- harness → LLM — one fixed destination, one shared single-tenant provider key. Z3 (inference injector) had specified a shared reverse-proxy pod but explicitly rejected AuthBridge in favor of a plain Go injector — a choice made when only credential injection was in scope. Once control plugins (SPARC/IBAC) are also wanted on this hop, a plain injector is insufficient and AuthBridge is justified — which reopens the placement question.
- sandbox → external API — arbitrary destinations; Z5 (generalized credentialed egress) already
specifies a forward proxy with a baked CA. The
#89SandboxTransport inversion makes the sandbox reachable from anywhere (BYO/on-prem/other-cloud), where a central proxy cannot observe the sandbox's own outbound calls.
The forces: the harness is serverless/scale-to-zero (Knative); Kubernetes NetworkPolicy selects
pods, not containers; AuthBridge's in-memory abph_ placeholder-swap is single-process only; kagenti's
target mesh is Istio ambient (L7 policy at a shared waypoint, not per-pod sidecars); and per-caller
identity attribution depends on the deferred Z1 identity spine.
We will deploy AuthBridge in two different shapes, one per hop: the harness→LLM injector/control
gate is a shared Deployment + ClusterIP Service that every harness pod routes to (AuthBridge #1);
the sandbox→external injector/control gate is co-located with each sandbox (AuthBridge #2) and
ships with the sandbox bundle — a sidecar for in-cluster sandboxes, part of the remote bundle for BYO.
Injection uses the stateless token-broker plugin backed by a small static-broker, so the shared
gateway holds no key and is not bound by the single-process abph_ limitation.
- AuthBridge #1 as a per-harness sidecar (
envoy-sidecar/co-located) — rejected for the shared role: aNetworkPolicycannot stop the harness pod from egressing when the proxy shares its network namespace, so the enforceable "harness pod has zero public egress" boundary (Z2 L3) is lost; it also scales a proxy with every scale-to-zero replica and diverges from the ambient/waypoint target shape. (It is simpler — loopback, no harness↔gateway mTLS, local session context — and remains a valid fallback if the egress-lockdown property is waived.) - AuthBridge #2 as a shared central egress proxy — rejected: a central proxy cannot intercept a sandbox's own outbound calls, and for a BYO/remote sandbox that egress never touches the harness cluster; the proxy must travel with the sandbox.
- Plain Go inference injector (Z3 as-written), no AuthBridge on the LLM hop — rejected for this PoC: it cannot host the SPARC/IBAC control plugins that capability #3 requires on that hop.
- Positive: the harness pod can be locked to zero public egress (default-deny except → the gateway
Service), giving an enforceable "no key and can't phone home" boundary (Z2); a single shared audit and policy chokepoint for all harness→LLM traffic; alignment with the kagenti Istio-ambient waypoint production shape; the sandbox gate works identically whether the sandbox is in-cluster or BYO; because injection is via the statelesstoken-broker, the shared gateway can scale horizontally and no workload ever holds the real key (it lives only instatic-broker). - Negative / accepted cost: the shared gateway cannot attribute a request to a specific caller without
mTLS/SPIFFE — so per-user/per-session policy and per-caller audit are deferred to Z1 (acceptable
while single-tenant, where there is one principal); a future multi-replica shared gateway cannot use
the in-memory
abph_placeholder-swap (single-process only) — the PoC sidesteps this by usingtoken-broker, but it constrains that future; the SPARC/IBACinferencegate on the shared hop sees only what rides the request, so per-session intent context must be carried on the wire. - Follow-up owed: Z1 identity spine (SPIRE + mTLS on the harness→gateway hop) to unlock per-caller
attribution and multi-tenant policy; a decision on multi-replica shared-gateway HA vs. the
abph_feature; hardening of the baked-CA trust distribution forAuthBridge #2.
Assisted-By: Claude (Anthropic AI) noreply@anthropic.com