CredentialProvider SDK reference
TypeScript contract reference for provider metadata, schemas, operations, lifecycle results, setup hints, and context capabilities.
Import from @constal/sdk. Definitions are validated at module load and probed again by the managed builder.
Primary exports
import {
credentialProvider,
credentialProviderConfigSchema,
credentialProviderSetupMetadata,
type CredentialProviderDefinition,
type CredentialProviderPackage,
type CredentialProviderPackageMetadata,
type CredentialProviderContext,
type CredentialMintRequest,
type CredentialMintResult,
type CredentialInteractionStartRequest,
type CredentialInteractionAdvanceRequest,
type CredentialInteractionAction,
type CredentialInteractionEvent,
type CredentialInteractionResult,
type CredentialMaterialRequest,
} from "@constal/sdk";Definition fields
const definition: CredentialProviderDefinition = {
id: "example-token", version: "1.0.0",
displayName: "Example token", description: "Mints a short-lived token.",
credentialSlots: ["bootstrap-token"],
configSchema: credentialProviderConfigSchema({ type: "object", additionalProperties: false }),
credentialConfigSchema: credentialProviderConfigSchema({ type: "object", additionalProperties: false }),
rotation: { mode: "manual", intervalMs: null, overlapMs: 0, maxAgeMs: null, refreshBeforeMs: 0 },
egress: { rules: [] },
mintRecovery: { kind: "outcome-unknown" },
async mint(_request, context) {
const bootstrap = await context.secret("bootstrap-token");
return { material: bootstrap.value };
},
};
export default credentialProvider(definition);The core identity, descriptions, schemas, bootstrap slots, rotation, egress, and mint are required. Documentation, Console setup hints, generic interaction, verification, and source-side destruction are optional.
Interaction
interaction: {
origins: ["https://example.com"],
async start(request: CredentialInteractionStartRequest): Promise<CredentialInteractionResult> {
return { action: { kind: "redirect",
url: `https://example.com/connect?state=${encodeURIComponent(request.state)}` } };
},
async advance(request: CredentialInteractionAdvanceRequest): Promise<CredentialInteractionResult> {
if (request.event.kind !== "callback") throw new Error("Expected callback");
return { action: { kind: "complete", material: await exchange(request.event.parameters.code) } };
}
}The action vocabulary is finite: redirect, form, display, wait, and complete. Each action admits exactly one next event class. See Provider interaction lifecycle for the complete state machine and security rules.
Provider context
async function mint(request: CredentialMintRequest, context: CredentialProviderContext): Promise<CredentialMintResult> {
const bootstrap = await context.secret("bootstrap-token");
const prior = await context.state.get("refresh-state");
const response = await context.egress({
url: "https://api.example.com/oauth/token", method: "POST",
headers: { authorization: `Bearer ${bootstrap.value}` },
bodyBase64: btoa(JSON.stringify({ prior })), maximumResponseBytes: 65_536, maximumRedirects: 0,
});
await context.audit({ operation: "mint", status: response.status });
return { material: atob(response.bodyBase64), privateState: request.privateState };
}secret, bounded egress, private state, immutable store, and non-secret audit are provider capabilities. They do not exist on Agent Ctx or ChannelContext; provider code receives only the bounded lifecycle capabilities declared by its contract.
Result rules
const minted: CredentialMintResult = {
material: accessToken,
expiresAt: Date.now() + 3_600_000,
privateState: refreshToken,
};
const verified = { verified: true };
const destroyed = { destroyed: true };Consumer material and provider-private lifecycle state are separate. Never place refresh state in consumer material or access material in private state.
Resource selectors
import { scopedBindingKey, type ScopedCredentialRef } from "@constal/sdk";
const credential: ScopedCredentialRef = {
kind: "scoped", key: scopedBindingKey("github"), owner: "customer", required: true,
};A scoped Resource selector additionally pins its Resource contract. Read Scoped bindings for admission-time resolution.