What it is
Credential isolation is the guarantee that a connector, the code that actually calls an external service like HubSpot, GitHub, or Paytm, never holds a long-lived, reusable credential. Instead it receives a single-use, time-bounded session credential, minted fresh for one specific execution, resolved to the real secret only at the last possible moment, and destroyed immediately after use, whether that use succeeded or failed.Why it was built
A connector is the part of this system closest to the outside world, and therefore the part most exposed to a bug, a dependency vulnerability, or a compromised third-party API response. If a connector held the platform’s real HubSpot API key or GitHub App private key directly, a single flaw in connector code could leak that credential, and the leak would be reusable by whoever obtained it, for as long as the credential remained valid. Credential isolation removes that exposure at the architecture level: even a fully compromised connector can only ever hand an attacker a credential that is already spent, or one that is about to expire in seconds and is bound to an authorization that has already been consumed.How it works
The mechanism lives inpackages/execution-control/src/, specifically two classes:
SessionCredentialVault (SessionCredentialVault.ts:18-25) exposes three operations:
issue(), consume(), and revoke(). Its in-memory implementation,
InMemorySessionCredentialVault, wraps an underlying CredentialVault (the thing that
actually holds the real, long-lived secret) and never itself stores the secret value. The
class’s own doc comment states this precisely: “issue() only confirms the underlying
credential exists, it never resolves or stores the secret” (SessionCredentialVault.ts:44-46).
issue(connectorId, authorizationId)callscredentials.getCredential(connectorId)once, purely to confirm a credential exists for that connector, then discards the resolved value immediately. It returns aSessionCredentialobject (SessionCredentialVault.ts:10-16) containing only an id, timestamps, and the connector/authorization it’s scoped to, never the secret itself.consume(sessionCredentialId)is the only method that ever calls back into the underlying vault for the real value, and it does so fresh, at the moment of use. Before it does, it checks the session record for three failure conditions, each throwing if true: already revoked, already used, or expired (SessionCredentialVault.ts:97-111). It then marks the sessionused = trueand returns the real credential exactly once. A second call toconsume()on the same session id always throws.revoke(sessionCredentialId)marks a session dead, unconditionally.
SessionCredentialSecureConnector (SessionCredentialSecureConnector.ts) is what actually
wraps a real connector’s execute() call with this lifecycle. Its own execute() method
(SessionCredentialSecureConnector.ts:74-131) does exactly this, in order:
- Asserts the connector’s policy allows this specific request (
policy.assertAllowed(...)). - Calls
sessionCredentials.issue(...), obtaining a session credential id. - Inside a
try/finally: callssessionCredentials.consume(...)to resolve the real credential just before use, passes it straight into the wrapped executor’sexecute()call, and in thefinallyblock, unconditionally callssessionCredentials.revoke(...), this runs whether the executor succeeded, threw, or the policy check itself threw after issuance. - Records exactly one audit event,
execution.completedorexecution.rejected, naming the connector, the session credential’s id (never its value), the authorization it executed under, and the capability invoked. The credential’s id is auditable; the credential’s value never appears anywhere in the audit trail.
consume() throws on a second call, and revoke() runs unconditionally on every exit
path via finally, a session credential can be used at most once, ever, no matter how the
execution ends.
One subtlety worth being precise about, from the class’s own doc comment
(SessionCredentialSecureConnector.ts:20-32): this connector is also constructed with a
gatewayAuthentication value proving it was configured with material signed by the Gateway’s
key at registration time. That value is frozen once, at construction, and reused for every
execution this connector instance will ever handle, it is not, and cannot be, a
per-request proof by itself. The actual per-request replay protection comes from a different
layer, ConnectorPolicy’s single-use GatewaySession check, enforced one level up, before a
GatewaySession is even created. The two guarantees (registration-time authenticity, and
per-request single-use) are deliberately separate mechanisms, not the same check reused twice.
How it enables things, with a concrete example
packages/api/tests/integration/credential-isolation.integration.test.ts proves this
guarantee at the HTTP boundary, not just at the library level. Its own doc comment
(credential-isolation.integration.test.ts:10-19) explains the split precisely:
packages/execution-control/tests/unit/session-credential-secure-connector.test.ts already
proves “a session credential is issued and destroyed exactly once per execution” by calling
SessionCredentialSecureConnector.execute() directly; this integration test proves the exact
same guarantee holds through a real POST /execute HTTP request against the real Express app,
through the real ExecutionGateway / SessionCredentialExecutionControl /
ExecutionControlService / SessionCredentialSecureConnector chain. It builds its own
“inspectable” execution system (packages/api/tests/bootstrap/createInspectableExecutionSystem.ts)
specifically to get a direct reference to the audit sink and the session credential vault,
which the production bootstrap chain does not expose by design. The test asserts that after a
successful /execute call, exactly one execution.completed audit event carries a
credentialId, confirming a session credential really was issued and consumed, not skipped.
packages/api/tests/integration/execution-failure.integration.test.ts is the companion case:
it proves the same revoke-on-every-exit-path guarantee when the underlying connector executor
throws, not just on the success path.
How to validate this yourself
packages/execution-control/src/SessionCredentialVault.tsandSessionCredentialSecureConnector.ts, the actual mechanism.packages/execution-control/src/types.ts,CredentialVault,ExecutionCredential,SecureConnector,ConnectorPolicyinterface shapes.packages/execution-control/tests/unit/session-credential-vault.test.ts,session-credential-secure-connector.test.ts,session-credential-execution-control.test.ts, the library-level proofs, one per class.packages/api/tests/integration/credential-isolation.integration.test.tsandexecution-failure.integration.test.ts, the HTTP-boundary proofs.packages/api/tests/bootstrap/createInspectableExecutionSystem.ts, how a test gets a direct handle on the audit sink and vault that production code never exposes.
Integration requirements
None from an operator’s perspective, this is an internal architectural guarantee, not a configurable feature. A new connector wired into this system automatically gets session credential isolation by virtue of being registered behindSessionCredentialSecureConnector;
there is no opt-out path documented in the source, and none should be added without a strong,
explicit reason.