docs(architecture): ADR-0007 egress auth mechanism — edge-inject vs protocol-broker#234
Conversation
…rotocol-broker Select the egress credential-attachment mechanism by upstream: edge-inject (fixed-client bearer, e.g. an LLM API) or protocol-broker (high-value scoped-by-rights credential, e.g. a PAT or object store). v1 ships edge-inject only; the protocol-broker mechanism is the existing Storage-broker zone, deferred for other upstreams. Egress posture becomes a ladder by need (deny-all / transparent pass-through / egress-wide bump / external SDS source) rather than a two-mode switch. Bump is the default only when an upstream credential is configured, so the one-click solo path holds at every rung; the per-deployment CA is auto-generated and its public certificate auto-injected into the sandbox trust store at start. The bump substrate is the Envoy data plane plus a self-hosted SDS minting service for per-SNI leaves; a config-time-enumerable allow-list uses pre-minted leaves over a file SDS source instead. mitmproxy/Squid are rejected-as-default for dropping the Envoy data plane. Injection is gated on a scoped credential the request presents, never on network origin (tightens P6-E2). The guest holds no long-lived upstream secret but may carry a short-lived session-scoped handle (corrects the prior categorical "guest holds nothing"). Propagated across §7, NFRs (FLEX-15, SEC-05/17/23/27/37/50/57/73), the threat model, component-06, glossary, c4 container/context, contracts, the trust-zone and threat-model diagrams, ADR-0005/0006 forward-refs, and the BoM. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
WalkthroughThis PR refactors egress architecture documentation by replacing a two-mode egress model (transparent vs MITM-inspecting) with a rung-based posture ladder. It introduces ADR-0007 for edge-inject credential attachment via Envoy SDS and updates all trust-boundary, threat-model, component, and security requirements documentation to use consistent "bump rung" terminology and clarify that guests never hold real upstream secrets. ChangesEgress Posture Ladder & Edge-Inject Authentication Mechanism
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/architecture/adr/0007-egress-auth-mechanism.md`:
- Around line 1-2: Replace the existing SPDX header lines that read
"SPDX-License-Identifier: FSL-1.1-Apache-2.0" and the accompanying copyright
comment with the required new-file header: add a top-of-file comment line "#
SPDX-License-Identifier: BUSL-1.1" and a following copyright comment "#
Copyright (c) 2025 Open Computer Use Contributors"; update the header in the ADR
file that currently contains "FSL-1.1-Apache-2.0" so the file conforms to the
mandated new source file license format.
In `@docs/architecture/components/06-egress-trust-edge.md`:
- Line 37: The doc's statement that "the edge receives the credential from SDS
at injection time... and drops it after the connection terminates; it holds no
credential at rest" is inaccurate for Envoy SDS; revise the phrasing around the
"edge receives the credential from SDS..." sentence and the other referenced
lines to say that secrets are "SDS-managed in-process secret state" (kept in
Envoy process memory via SDS subscription/push) rather than asserting
per-connection fetch-and-drop or absolute "no credential at rest" guarantees,
and explicitly note that on-demand fetching is conditional/configurable rather
than default.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 0257dc41-7398-483c-8104-97375ad976c8
📒 Files selected for processing (20)
docs/architecture/02-trust-boundaries.mddocs/architecture/03-c4-context.mddocs/architecture/04-bounded-contexts.mddocs/architecture/05-c4-container.mddocs/architecture/06-threat-model.mddocs/architecture/08-contracts.mddocs/architecture/adr/0005-egress-credential-delivery-envoy-sds.mddocs/architecture/adr/0006-egress-forward-proxy-substrate.mddocs/architecture/adr/0007-egress-auth-mechanism.mddocs/architecture/adr/README.mddocs/architecture/components/00-overview.mddocs/architecture/components/05-session-sandbox.mddocs/architecture/components/06-egress-trust-edge.mddocs/architecture/diagrams/02-trust-boundaries.mmddocs/architecture/diagrams/06-threat-model.mmddocs/architecture/diagrams/c4-container.mmddocs/architecture/glossary.mddocs/architecture/manifesto/02-nfrs.mddocs/architecture/manifesto/05-licensing-posture.mddocs/architecture/primitives-backlog.md
| <!-- SPDX-License-Identifier: FSL-1.1-Apache-2.0 --> | ||
| <!-- Copyright (c) 2025 Open Computer Use Contributors --> |
There was a problem hiding this comment.
Update SPDX header to match the required new-file license format.
This new file uses FSL-1.1-Apache-2.0, but the review guideline requires # SPDX-License-Identifier: BUSL-1.1 and # Copyright (c) 2025 Open Computer Use Contributors for new source files.
As per coding guidelines, **/*: “All other new source files MUST include SPDX license header: # SPDX-License-Identifier: BUSL-1.1 with # Copyright (c) 2025 Open Computer Use Contributors”.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/architecture/adr/0007-egress-auth-mechanism.md` around lines 1 - 2,
Replace the existing SPDX header lines that read "SPDX-License-Identifier:
FSL-1.1-Apache-2.0" and the accompanying copyright comment with the required
new-file header: add a top-of-file comment line "# SPDX-License-Identifier:
BUSL-1.1" and a following copyright comment "# Copyright (c) 2025 Open Computer
Use Contributors"; update the header in the ADR file that currently contains
"FSL-1.1-Apache-2.0" so the file conforms to the mandated new source file
license format.
| The credential receiver is Envoy's SDS client: it receives the upstream credential over Secret Discovery Service (SDS, gRPC xDS) from the SDS source and binds it for injection on the outbound leg. The source is a static file (solo deployments) or a customer-provided SDS-compatible store (enterprise deployments); its lifecycle is the source's, per [ADR-0005](../adr/0005-egress-credential-delivery-envoy-sds.md). | ||
|
|
||
| Owned state: the destination allow-list (resolved-IP + SNI rules) and the per-destination mode binding (transparent vs MITM). The edge receives the credential from SDS at injection time, attaches it for the upstream leg, and drops it after the connection terminates; it holds no credential at rest and no rotation or revocation responsibility ([NFR-SEC-29](../manifesto/02-nfrs.md)). It authors no denylist and holds no kill-switch route — the denylist is the Control/operator API's; the edge reads it as a deny signal. It holds no object-store credential — the broker signs its own backend leg ([NFR-SEC-25](../manifesto/02-nfrs.md)), and the broker backend leg (F9) traverses the edge as one allow-list destination with no TLS termination, so the broker-produced signature is forwarded byte-intact. | ||
| Owned state: the destination allow-list (resolved-IP + SNI rules) and the per-destination rung binding (transparent pass-through vs egress-wide bump). The edge receives the credential from SDS at injection time, attaches it for the upstream leg, and drops it after the connection terminates; it holds no credential at rest and no rotation or revocation responsibility ([NFR-SEC-29](../manifesto/02-nfrs.md)). It authors no denylist and holds no kill-switch route — the denylist is the Control/operator API's; the edge reads it as a deny signal. It holds no object-store credential — the broker signs its own backend leg ([NFR-SEC-25](../manifesto/02-nfrs.md)), and the broker backend leg (F9) traverses the edge as one allow-list destination with no TLS termination, so the broker-produced signature is forwarded byte-intact. |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
For Envoy SDS and HTTP credential injection, does Envoy cache SDS-delivered secrets in process memory between requests/connections, or fetch them strictly at injection time per request? Please cite Envoy official docs.
💡 Result:
Envoy behavior for SDS-delivered secrets (including those used by the HTTP credential injector): Envoy establishes an SDS subscription and keeps the resulting secret material in its process memory; it does not “fetch strictly at injection time per request” when the secret is already present. For TLS, Envoy explicitly supports both “warm/prefetch via SDS subscription” and “on-demand” fetching only when configured or when the secret is not already present. 1) SDS is a push/subscription mechanism and secrets are applied without needing per-request re-fetch Envoy’s SDS documentation describes SDS as a mechanism where an SDS server “will push certificates to all Envoy instances” and “Envoy will use the new ones right away without re-deployment” [1]. This implies the dataplane holds and updates the secret state in memory as SDS updates arrive, rather than doing a fresh remote fetch on every request. 2) By default, Envoy does SDS fetching ahead of traffic (not on each request) The SDS docs state: “By default SDS certificate fetching blocks initialization of the listeners and the clusters that reference them.” [2] That is, Envoy waits for the required SDS-delivered secrets to be available before serving traffic, which again indicates secrets are not re-fetched per connection/request. 3) On-demand behavior exists, but it is conditional/configured (i.e., not the default) Envoy also documents an on-demand mode for TLS: “Envoy provides an on-demand certificate selector that pauses the TLS handshake to issue an SDS request for a certificate if not present, and then continues the handshake after receiving the response.” [1][3]. The on-demand selector is explicitly described as “Fetches the secret on-demand while allowing the parent cluster or listener to accept connections without warming… During the handshake… an SDS resource request starts, and the handshake is paused… Once an SDS response is received… the handshake is resumed…” [4]. So, per-request fetching happens only in the on-demand/certificate-missing case, not as a general “always fetch at injection time.” 4) HTTP credential injector uses configured credential sources; the official filter docs do not indicate per-request SDS fetching semantics The Envoy CredentialInjector filter description says “The filter is configured with one of the following supported credential_injector extensions. Extensions are responsible for fetching the credentials from the source. The credentials obtained are then injected into the Authorization header…” [5]. The (proto) docs for the credential injector also describe that SDS can be used for credential configuration (example shows sds_config under the injected credential) [6]. However, the official credential injector documentation does not state that SDS-sourced credentials are fetched anew for every request; instead, SDS overall is designed to push/update secret material (see #1 and #2). Therefore, with SDS-backed injected credentials, the expected model is “SDS updates secret state in the Envoy process, then injection uses the current in-process secret,” unless a specifically configured on-demand mechanism is used. Answer to the question For Envoy SDS and HTTP credential injection, Envoy caches (maintains) SDS-delivered secrets in process memory between requests/connections via SDS subscription/update semantics, rather than fetching strictly at injection time per request in the default case [1][2]. Strict per-request fetching happens only when you configure an on-demand SDS flow such that Envoy must request the secret during a handshake/injection because it is not present (“if not present”) [1][3][4].
Citations:
- 1: https://www.envoyproxy.io/docs/envoy/latest/configuration/security/secret.html
- 2: https://www.envoyproxy.io/docs/envoy/latest/configuration/security/secret
- 3: https://www.envoyproxy.io/docs/envoy/v1.27.5/configuration/security/secret
- 4: proposal: a new light extension to load certificates through SDS on-demand envoyproxy/envoy#30600
- 5: https://spiffe.io/docs/latest/microservices/envoy/
- 6: https://dev.to/jamesli/xds-protocol-deep-dive-the-universal-control-plane-api-behind-envoy-and-istio-2de9
Reword edge credential-lifecycle claims to avoid unsupported “no credential at rest / fetch-drop” guarantees with Envoy SDS
The wording implies per-connection fetch-and-drop semantics and “no credential at rest” in the edge. Envoy’s SDS model is subscription/push and keeps SDS-delivered secret material in Envoy process memory (with on-demand fetching only as a conditional/configured path when the secret is missing). Update the doc to reflect “SDS-managed in-process secret state” and avoid absolute guarantees about not holding credentials outside the connection lifetime (apply to the same wording at the other referenced lines).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/architecture/components/06-egress-trust-edge.md` at line 37, The doc's
statement that "the edge receives the credential from SDS at injection time...
and drops it after the connection terminates; it holds no credential at rest" is
inaccurate for Envoy SDS; revise the phrasing around the "edge receives the
credential from SDS..." sentence and the other referenced lines to say that
secrets are "SDS-managed in-process secret state" (kept in Envoy process memory
via SDS subscription/push) rather than asserting per-connection fetch-and-drop
or absolute "no credential at rest" guarantees, and explicitly note that
on-demand fetching is conditional/configurable rather than default.
Decision
ADR-0007 selects the egress credential-attachment mechanism by upstream:
v1 ships edge-inject only. The protocol-broker mechanism is the existing Storage-broker zone, named and abstraction-ready, deferred for other upstreams. Injecting user PATs through the egress edge is recorded as an explicit anti-example.
Egress posture model
deny-all/transparent pass-through(no CA) /egress-wide bump/external SDS source. Bump is the default only when an upstream credential is configured, so the one-click solo path holds at every rung; the per-deployment CA is auto-generated and its public cert auto-injected into the sandbox trust store at start.Scope
New:
adr/0007-egress-auth-mechanism.md. Propagated across §7 trust-boundaries, NFRs (FLEX-15, SEC-05/17/23/27/37/50/57/73), the threat model + diagram, component-06 / component-05 / 00-overview, glossary, c4 container & context, 08-contracts, the trust-zone diagram, ADR-0005/0006 forward-refs, the BoM, and the primitives backlog. 20 files.Verification
egress-wide bump, notMITM mode), the delta-4/delta-5 invariants stated in the owning component spec, and ADR-0007 cross-referenced from every doc it governs.🤖 Generated with Claude Code