[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
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 none of the connectors
registered in production today use 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
ACredentialHandle’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.buildConnectorEvidenceonly ever readsConnectorRequest/ConnectorResponsefields, neverConnectorExecutionContext.credential.redactSensitiveKeysadditionally 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 inpackages/connector-sdk/tests/unit/credential-provider.test.ts. - Never as a raw value reaching a Connector. Every
CredentialHandleis branded at creation (brandCredentialHandle);SdkConnectorExecutor(packages/execution-gateway/src/connector-execution/SdkConnectorExecutor.ts) rejects any credential that isn’t a branded handle (Connector "<id>" rejected a raw credential). Tested for a real provider ingithub-app-credential-provider.test.ts(“brands the returned handle”). - Never in the Execution Trust Record. Only
ConnectorEvidence(never the handle itself) is placed onExecutionResult.metadata.connector, see Execution trust records.
Minimal example
Real production connectors write a small, dedicatedCredentialProvider rather than
configuring the generic EnvironmentCredentialProvider directly, usually because the
credential shape needs a specific field name that doesn’t fit
EnvironmentCredentialProvider’s single connector-to-variable-name mapping (a two-part
credential like a key_id/key_secret pair, for instance) or because a resolved field
name needs to match the connector’s own credential type exactly. HubSpot’s is the current
real example (a single token), copied verbatim from current source. The variable holds the
token itself, or, with PARMANA_SECRETS_PROVIDER=aws-secrets-manager, the name of a secret
in AWS Secrets Manager (see below):
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.
Where the secrets come from
PARMANA_SECRETS_PROVIDER chooses how a connector’s credential variable is read
(packages/api/src/bootstrap/secrets/SecretsProviderBootstrap.ts):
env(the default): the variable’s value is the secret.aws-secrets-manager: the variable’s value is the name or ARN of a secret in AWS Secrets Manager, read with the server’s AWS role (AwsSecretsManagerProvider). See Environment variables.
PARMANA_SECRETS_PROVIDER=<value> is not implemented.).
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.