Skip to main content
[AVAILABLE] as a mechanism, [PARTIAL] as a system-wide guarantee. packages/execution-control, packages/connector-sdk. See the scope note below before treating this as true of every connector.

What it is

Credential resolution happens exclusively inside the gateway boundary, after every gateway check has passed. The caller that proposed the action, the Runtime, and the Policy Engine never see the credential a connector uses to actually act.

Why it exists

If the entity proposing an action, increasingly an AI agent, ever held a live, reusable credential, every guarantee upstream (deterministic policy, signed authorization, gateway re-verification) would still leave that credential extractable and reusable outside the authorized flow. Credential isolation closes that gap structurally: there is no code path where a proposer receives a credential, because credentials are never routed to it in the first place.

How it behaves

A CredentialHandle is opaque, resolved material, { providerId, credentialId, value }. CredentialVaultAdapter adapts any CredentialProvider into execution-control’s CredentialVault interface, so InMemorySecureConnector resolves credentials through it exactly as it always has. Two generic providers ship in @parmana/connector-sdk: StaticCredentialProvider (an in-memory map) and EnvironmentCredentialProvider (resolves from process.env via a single connector-to-variable-name mapping). StaticCredentialProvider is what production actually wires up for NODE_ENV=test; EnvironmentCredentialProvider is real, tested library infrastructure with its own unit test (packages/connector-sdk/tests/unit/credential-provider.test.ts), but neither of the two connectors registered in production today uses it directly — see “Minimal example” below for why. A session credential is issued, consumed exactly once, and can be revoked:

What “never leaks” means, precisely

A CredentialHandle’s value necessarily carries the resolved secret, a connector (e.g. HttpConnector) needs it to set an Authorization header. What must never happen is the secret reaching anywhere durable or observable outside that one ephemeral use:
  • Never in ConnectorEvidence. buildConnectorEvidence only ever reads ConnectorRequest/ConnectorResponse fields, never ConnectorExecutionContext.credential. redactSensitiveKeys additionally strips any response-metadata key that looks credential-shaped, as defense in depth against a connector author’s mistake.
  • Never in a thrown error. Both providers’ failure messages name only identifiers (connectorId, environment variable name), never a resolved value. Tested in packages/connector-sdk/tests/unit/credential-provider.test.ts.
  • Never as a raw value reaching a Connector. Every CredentialHandle is branded at creation (brandCredentialHandle); SdkConnectorExecutor rejects any credential that isn’t a branded handle. Tested end-to-end in gateway-integration.test.ts (“rejects a raw (non-provider-resolved) credential”).
  • Never in the Execution Trust Record. Only ConnectorEvidence (never the handle itself) is placed on ExecutionResult.metadata.connector, see Execution trust records.

Minimal example

Real production connectors write a small, dedicated CredentialProvider rather than configuring the generic EnvironmentCredentialProvider directly — usually because the credential shape needs a specific field name (Razorpay’s key_id/key_secret pair isn’t expressible as EnvironmentCredentialProvider’s single connector-to-variable-name mapping at all) or because a resolved field name needs to match the connector’s own credential type exactly. HubSpot’s is the simplest real example (a single token), copied verbatim from current source:
CredentialVaultAdapter wraps whichever provider a connector registration supplies; the caller-facing pipeline (Runtime, Policy Engine) never touches it either way — this class being bespoke rather than the generic EnvironmentCredentialProvider is an implementation choice inside the credential-resolution boundary, not a different boundary.
Scope, precisely: this mechanism is real and tested, and in the default server it is exercised for the two connectors registered in production, razorpay and hubspot. packages/api/src/bootstrap/createRazorpayCredentialProvider.ts and createHubSpotCredentialProvider.ts each construct a dedicated CredentialProvider implementation, registered against their respective connector (see The gateway). The isolation guarantee (caller never sees the credential, single-use, revocable) is architecturally true for any connector routed through this seam, and is proven generically by the tests cited above, but making it true for a new (third) connector today means writing bootstrap code (a new CredentialProvider entry, a new registry registration), not configuration. There is no dynamic, general-purpose “any connector automatically gets isolated credentials” path yet. Treat the isolation guarantee as proven for the mechanism and for razorpay/hubspot specifically, not as a system-wide property of arbitrary connectors you haven’t wired yet.A third connector, vendor-payment, was previously registered here as the reference example — it has since been removed from the repository entirely (not merely superseded), and createCredentialProvider.ts (the file this warning used to cite) no longer exists. See docs/VERIFICATION-GAPS.md G-27 in the source repository for the full account.

[FUTURE]: cloud secret managers

Only the CredentialProvider interface seam is defined. No integration exists yet for HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, or Google Secret Manager. Any of these would implement CredentialProvider exactly like EnvironmentCredentialProvider does, no cloud SDK dependency has been added to this package, and none is implemented in this milestone.

Next

Gateway attestation

The request-bound signature that authenticates the gateway to a connector.

Add a connector with the Connector SDK

What wiring a new connector into this seam actually requires.