Native credential execution in Spaces
Status: implementation and rollout runbook, September 8, 2026. The roles, access, and activity improvements shipped in PR #367. The operator has approved one Spaces deployment with Infisical remaining the secret store. Production cutover is explicit; existing external deployments retain compatibility untilCREDENTIAL_EXECUTION_MODE=native is enabled.
Target
Reuse and changes
Implementation:
packages/credential-gateway/src/core.ts is a transport-free
library shared by both modes. packages/api/src/credential-execution.ts binds
it to live Spaces authorization, exact database credential references, the
Infisical reader, and the existing Spaces database. No gateway process is
started in native mode. The writer remains in infisical-writer.ts.
Infisical supports a machine identity exchanging its client credentials for a
short-lived API token, then retrieving an individual secret by name. Reuse the
deployment’s configured Infisical region/domain; don’t hardcode a new region.
See Universal Auth
and secret retrieval.
Security tradeoff
The legacy gateway uses a proxy identity without value-read permission; the separate Infisical proxy handles injection. Removing both processes means the Spaces executor will hold retrieved values transiently. Infisical still handles storage, but compromise of the Spaces process can then reach the configured read scope. An internal module is not a process isolation boundary. Use a separate runtime read identity limited to the required environment and paths; retain the write identity’s existing scope. Do not grant broad vault read/list/admin rights. The operator approved this tradeoff on September 8, 2026. Reuse the scoped proxy reader identity during cutover; keep reader and writer identities separate. Never return values through metadata/reveal APIs, place them in agent environments, persist them in Spaces, or log request headers/bodies. Drop secret references promptly; JavaScript cannot guarantee memory zeroization. Keep outbound requests bounded and redirects disabled. Authentication injection must happen only after server-derived destination and live access checks. Block request overrides of destination, secret reference, and protected headers. Guard provider responses against credential reflection and keep error messages allowlisted. Vault and authorization outages fail closed. Do not retry an ambiguous provider mutation automatically.Configuration
Native execution is the default on a fresh host. A pre-existingCREDENTIAL_GATEWAY_URL preserves external mode until the operator explicitly
sets CREDENTIAL_EXECUTION_MODE=native. Unknown modes fail closed.
Configure the Spaces API environment:
CREDENTIAL_EXECUTION_SIGNING_KEY: a new random signing secret, at least 32 characters. Use a different key from the retired gateway so old tickets cannot cross into the new replay store.COMMONS_CREDENTIAL_DESTINATIONS_JSON: the existing destination allowlist (HTTPS origins, methods, path prefixes, allowed caller/response headers, timeout, and response size). Provider authentication headers must not be allowed as caller headers.COMMONS_CREDENTIAL_BINDINGS_JSON: exact Space/connection/destination bindings to credential IDs already in Spaces. Header names are lowercase; the prefix is literal, including any trailing space. For example:
INFISICAL_READER_CLIENT_ID/INFISICAL_READER_CLIENT_SECRET: the dedicated runtime reader identity, limited to the referenced environment and paths.- Existing
INFISICAL_DOMAIN,INFISICAL_PROJECT_ID,INFISICAL_ENVIRONMENT, writer identity, connection policies, and credential definitions remain.
POST /v0/spaces/:space/credential-calls and MCP call_external_service retain
their contracts. Low-level ticket clients use the returned gateway_url,
which now points to POST /v0/credential-execution/call on the Spaces host.
The legacy field name is retained for client compatibility. Tickets are bound
to the complete request and consumed atomically before external execution.
There is no automatic retry or fallback between execution modes.
Readiness is included in /healthz as credentials: {mode, configured, ready}.
A configured native executor with invalid configuration or unavailable
persistence makes readiness fail. An unconfigured fresh host can still run
Spaces. Readiness does not fetch vault values; each execution fails closed on
vault or authorization failure. The credential page derives its Spaces
service on Railway link from Railway’s own service environment metadata.
History and cutover
credential_execution_events and credential_execution_ticket_uses live in
the existing Spaces database. Execution events omit request paths, headers,
bodies, secret values, and provider-controlled request IDs. History imports
retain the old sanitized records, timestamps, source sequence and digest.
The authorized UI projection omits sensitive internals in either mode and
sorts by occurrence time, so importing older records does not hide new calls.
-
Deploy this code in existing external mode. Build the API, then run the
operator-only migration command inside the Spaces deployment with its
DATABASE_URL, oldCREDENTIAL_GATEWAY_URLand read-onlyCREDENTIAL_GATEWAY_AUDIT_READER_KEY:The importer validates ordered pages, runs each page transactionally, and deduplicates by(source, source_sequence). A changed digest for an existing source sequence fails the import. It prints counts only and is safe to retry. - Set the native variables, preserving all existing connection allowlists, credentials and grants, then redeploy Spaces. Verify native readiness, allowed read-only fixture calls, request substitution denial and replay rejection. Verify per-Space and per-agent history remains available.
- Drain legacy tickets for their configured maximum TTL plus request timeout. Run the history import again and verify a repeated import adds zero rows. Compare source counts and original timestamps before retiring the source.
-
Remove the gateway and proxy deployments and stop the old audit database
runtime. Preserve the old database volume for rollback/retention. Remove
obsolete
CREDENTIAL_GATEWAY_*variables from Spaces and redeploy with only the native configuration.