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

ts
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

ts
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

ts
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

ts
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

ts
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

ts
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.

Machine-readable schemas