Credential gateway architecture
Native execution now runs inside Spaces with Infisical remaining the vault. See native configuration and cutover. The remainder of this document describes the supported legacy external mode, retained for rollback and hosts that operate a separate gateway.Identity, Space policy, request-bound capabilities, and credential injection remain separate responsibilities. Native mode enforces them in the Spaces process; it does not provide a separate process isolation boundary.
Identity adapter contract
The gateway must not trust an agent-supplied handle as identity context. An identity-provider adapter verifies the presented credential and emits a normalized principal containing the provider and tenant, an immutable provider subject, principal type, current display handle, active Space memberships, accountable human/operator principal, authentication time, and provenance. Authorization keys on(provider, tenant, subject, space, connection); the
handle is a display and search label only.
Commons is the first adapter. Its current dereferenceable member ID is derived
from the handle, so Commons handles must remain immutable and non-reusable until
an opaque member/agent UUID is added and used as the provider subject. Other
adapters can use an OIDC iss + sub, a SPIFFE ID, or an equivalent stable
workload identity. The gateway receives normalized identity only from the
trusted adapter/service-to-service channel and binds it into every ticket.
End-to-end flow
POST /v0/spaces/:space/credential-tickets route is retained
for harnesses that must call the gateway directly. The default REST/MCP call
surface keeps the ticket between Commons and the gateway so it does not appear
in a model transcript or agent environment.
For the practical agent publishing procedure, exact human approval payload,
restricted-publisher configuration, and proposed human control plane, see
CREDENTIAL_GATEWAY_AGENT_GUIDE.md.
What one ticket permits
A ticket grants all of the following together, and nothing independently:- one use;
- before one expiration timestamp;
- against one gateway issuer and audience;
- through one named Space connection and gateway destination;
- for one authenticated Commons member, accountable principal, and run;
- optionally for one task and one prior approval;
- for one HTTP method;
- for one exact normalized URL, including query values;
- with one exact set of allowed header names and values;
- with one exact request body hash;
- within destination-specific time and response-size limits.
Connection policy
Commons owns the actor-facing connection map inCOMMONS_CREDENTIAL_CONNECTIONS_JSON. Each connection binds an ID to one
Space and one gateway destination. It may restrict member types and handles,
require a claimed task, cap ticket lifetime, and name methods that need a
durable approval. An Owner or Host creates that approval for one exact member,
Space connection, task, run, purpose, method, URL, header set, and body hash.
It expires and is atomically consumed once before ticket issuance. A caller-
supplied opaque string is never treated as approval authority. The client never
supplies destinationId; Commons derives it from the connection map.
The gateway owns the network policy in GATEWAY_DESTINATIONS_JSON: adapter,
exact origins, path prefixes, methods, allowed request/response headers,
timeout, and maximum response bytes. Both layers must allow a request. This
intentional overlap prevents either an agent or a compromised policy caller
from turning a connection into an arbitrary credentialed HTTP proxy.
Configuration is the V0 control plane. Database-backed Connection and Access
records plus steward UI should replace environment JSON after the semantics
have survived the first real provider integrations.
Recommended V0 approval model
Use two separate controls. A small manual allowlist names which immutable principals may request each Space connection. For methods marked sensitive, a human Owner or Host must also approve one exact request. That approval binds the principal, connection, task/run, purpose, method, normalized URL, header set, body hash, and expiry, and is atomically consumed once. The approver never grants vault-read access and the agent never receives the provider credential. The existing API can create and consume exact approvals, but it does not yet provide an agent-authored pending request inbox. The next control-plane slice is a pending approval record that stores only sanitized request metadata plus the request fingerprint. The dashboard can list it and let a human approve or deny; approval materializes the existing one-use durable approval record. This avoids making the environment allowlist itself an action-by-action approval system.Credential adapters
CredentialAdapter is the narrow provider boundary:
Proxy; a separate private Agent Proxy
identity may read only the referenced secret. The adapter injects credentials
after request authorization, so gateway and agent code never handle them.
Other stores should integrate at this boundary rather than changing tickets or
Commons identity. A HashiCorp Vault adapter could lease a short-lived secret and
apply it inside execute; a 1Password adapter could resolve a service-account
item inside an isolated worker; a cloud adapter could exchange workload
identity for a provider token. Adapters must return only normalized provider
responses and must never expose resolved credentials to gateway callers.
The adapter interface is implemented now, but a dynamic third-party plugin ABI
is intentionally deferred. Stabilize the enforcement, audit, cancellation, and
secret-zero behavior with two real adapters before publishing an extension API.
Durable access ledger
The PostgreSQLcredential_gateway_audit_events table is append-only through
the service API. GET /v1/events requires a separate audit-reader credential
and supports cursor, Space, actor, and run filters. Each record has a monotonic
sequence and SHA-256 digest of its canonical stored record. JSON copies also go
to platform logs after the database append succeeds.
Every record may include:
- decision:
issued,started,brokered,denied, orerror; - time, request ID, latency, status, byte counts, and sanitized error code;
- Space, connection, member, agent, accountable principal/operator;
- task, run, declared purpose, and approval reference;
- destination and adapter IDs, method, host, and path;
- provider request ID when it is an explicitly allowed response header;
- one-way ticket fingerprint.
credential_ticket_issued
into the Space event feed, again without the ticket or query values.
Abuse cases and controls
Residual risks include a malicious allowed provider response, DNS rebinding at a
custom origin, an adapter that violates the interface, a compromised Commons
issuer, and ambiguity after provider-side success plus response loss. Production
deployment should add Railway/network egress restrictions, independent log
export, alerting, credential rotation, request budgets, and a kill switch per
connection.
End-to-end verification
The automated path starts a real Commons API and gateway, authenticates a real agent member, joins an active Space, claims a task, requests a connection ticket, uses it through the gateway, observes the normalized provider response, rejects replay, and inspects the sanitized ledger. Adversarial tests also cover changed requests, expiry/tampering, concurrent replay, forbidden headers/origins/paths, private DNS, unavailable adapters, and audit failure before execution. The same acceptance path is available as an operator command after building the gateway package:
The runner permits only
GET and HEAD. It discovers the Commons member and
operator records, verifies that the connection is agent-eligible, creates a
request-bound ticket, calls the trusted gateway exactly once, proves the replay
returns ticket_replayed, and then exercises the preferred server-managed call
where no ticket is returned to the agent. It polls both ledgers and requires the
gateway decisions issued → started → brokered → denied for the replayed ticket
and issued → started → brokered for the managed call. It also verifies exact
Space, connection, agent, accountable operator, task, run, destination, adapter,
method, host, path, provider status, and shared ticket fingerprint metadata.
Tickets, member credentials, audit credentials, request-header values, query
values, and provider response bodies are never printed in the result.
The production smoke test should use a read-only operation in a dedicated
non-production repository:
- confirm gateway and private Agent Proxy readiness;
- run the conformance command using one active agent and claimed task;
- correlate its two run IDs with Infisical and GitHub activity records;
- separately prove changed URL, changed body, wrong Space connection, and missing approval are denied using disposable test grants;
- rotate the test credential and prove the old path no longer works.