Raxx · internal docs

internal · gated

ADR 0130 — Vault access pattern for multi-agent environments: per-agent machine identities + header proxy

Status: Accepted — operator confirmed Option A by dispatching prod adoption (2026-06-20) Date: 2026-06-20 UTC Deciders: software-architect (proposed); operator (confirmed via dispatch of prod adoption task) Scope: Infisical vault access for all agents, services, and CI environments; Infisical MCP server integration


Context

Raxx's Infisical vault (vault.raxx.app) is fronted by Cloudflare Access (CF Access) using the non_identity service-token policy for machine callers. Every machine caller must inject two headers on every request: CF-Access-Client-Id and CF-Access-Client-Secret. Most tool integrations (the Infisical MCP server, the CLI in CI pipelines, SDK calls) have no native support for custom request headers, so they cannot reach the vault without a workaround.

The workaround in production today: the main loop dispatches sre-agent for any vault read that the main loop needs directly. This adds 30 seconds per secret one-shot and makes agent flows for anything secret-adjacent awkward. The problem compounds as more agents and repos onboard.

Two separate problems need solving:

  1. Tool compatibility: The Infisical MCP server (and any future tool) cannot send CF Access headers natively. It must either be wrapped or the access model must change.
  2. Identity sprawl: All machine callers currently share an identity. One compromise exposes all accessible paths. Revocation is an all-or-nothing disruption.

This ADR records the decision on the access pattern that resolves both problems with the least security friction and no regression in posture.


Decision

Adopt per-agent scoped Infisical machine identities as the primary security boundary, combined with a header-injecting local proxy (Option A) to retain CF Access as the outer gate.

Specifically:

  1. Each agent class and each persistent service receives its own Infisical machine identity, scoped by project, environment, path, and permission (read-only by default). A shared cross-agent identity is not acceptable for steady-state operation.

  2. A thin header-injecting reverse proxy runs alongside every tool or execution environment that cannot natively send CF Access headers. All vault-bound traffic routes through this proxy (localhost:2019 by default), which injects the CF-Access-Client-Id and CF-Access-Client-Secret headers from environment variables. CF Access service tokens remain, one per execution environment class (not one per agent).

  3. The Infisical MCP server (@infisical/mcp-server) is wired with a dedicated read-only machine identity (infisical-identity-mcp-main-loop-trademaster) scoped to the paths the main loop needs. The delete-secret and invite-member tools exposed by the MCP server are rendered inert by the identity's RBAC — 403 is the response at the Infisical layer, not a tool-level restriction. The MCP server's INFISICAL_HOST_URL points at the proxy endpoint.

  4. Every new machine identity is enrolled in the Velvet subscription manifest for automated rotation. No identity uses a static long-lived secret without a rotation schedule.

  5. The feedback_main_loop_vault_limit operational constraint (dispatch sre-agent for every vault read) is retired once the MCP server is confirmed working via a successful spike. Spike PASSED 2026-06-20 (issue #3737). Prod identity provisioned 2026-06-20 (PR #3748). Full retirement pending operator completion of vault-write step.

The network option (Option A vs Option B vs Option C) is recorded separately in the design doc (vault-multi-agent-access-pattern.md) and required operator confirmation (OQ1). Operator confirmed Option A by dispatching the production adoption task (2026-06-20). This ADR records Option A as the Accepted direction. when the operator confirms OQ1.


Language choice rationale

No new service introduced. The header-injecting proxy is a thin tool component, not a new independently deployable service. If it is implemented as a standalone process rather than embedded tooling, it is Tier 2 (Python) — it is not on the auth hot path, has no p99 latency budget below 5ms, and has no memory-safety-critical properties. Vault reads are not order-execution paths. A Go or Caddy implementation is also acceptable; the operator may choose.


Consequences

Positive

Negative / risks

Neutral


Alternatives considered

Option B — Drop CF Access for machine callers; Infisical native auth + WAF

Lowest friction. MCP server works natively with no proxy. Infisical API is internet-reachable (behind CF WAF) rather than CF Access gated.

Rejected as the primary path because: it reduces the perimeter from two independent auth gates (CF Access + Infisical) to one (Infisical). Any Infisical CVE on the API surface is now directly exploitable. The WAF provides L7 filtering for known patterns but not for novel application vulnerabilities. The friction reduction is real but the security regression is not recoverable if an Infisical 0-day is published.

Reconsidered if: Infisical publishes a formal SOC 2 or equivalent assessment, the WAF managed ruleset gains specific Infisical API rule coverage, or Infisical moves to a verified published security model. This is Option B in the design doc and can be adopted via an amendment to this ADR.

Option C — Private mesh (Cloudflare Tunnel or Tailscale)

Strongest perimeter. Vault not reachable from the public internet.

Rejected as the immediate path because: ephemeral environments (GH Actions runners, ad-hoc Claude Code sessions) require mesh client installation and auth at spawn time, adding 15–30 seconds to every CI job that needs vault access. The friction trade is unfavorable for the spike-stage goal of "reduce friction." Option C is the natural Phase 2 upgrade once Option A is stable.

Option D — Hybrid (mesh for persistent services, proxy for ephemeral)

Architecturally sound. Too operationally complex for v1. Two runbooks, two access patterns, two sets of CF service tokens. Revisit after Option A is stable.


Security / GDPR checklist


Revisit when