/** * Credential provider abstraction. * * Every feature that needs an external credential (Anthropic API key, * Google OAuth tokens, OpenAI key, Slack bot token, etc.) should go through * one of the resolve*() helpers here instead of reading `process.env` * directly. That way the same feature can work in three modes: * * 1. User set their own key in .env → use it directly * 2. User connected Builder via `/cli-auth` → route through Builder proxy * 3. Neither → throw FeatureNotConfigured * * Templates catch FeatureNotConfigured and show a "Connect Builder (1 click) / * set up your own key (guide)" card. * * Today these helpers are used by the Builder-hosted LLM gateway, and the * shape is meant to grow to cover future managed credential integrations * (e.g. additional Builder-hosted services) without rewrites. */ /** * Decide which `app_secrets` scope a Builder/credential write should use. * * Org scope ("everyone in this org sees these credentials") wins when the * connecting user is an owner or admin of an active org — the write * privileges shared infra. A plain member or a user without an active * org falls through to per-user scope so a teammate can't silently * overwrite the org-shared connection. */ export declare function resolveCredentialWriteScope(email: string, orgId: string | null | undefined, role: string | null | undefined): { scope: "user" | "org"; scopeId: string; }; export declare class FeatureNotConfiguredError extends Error { readonly requiredCredential: string; readonly builderConnectUrl?: string; readonly byokDocsUrl?: string; constructor(opts: { requiredCredential: string; message?: string; builderConnectUrl?: string; byokDocsUrl?: string; }); } /** * The credential store could not be read — which is NOT the same as the * credential being absent. Never render this as "not configured": the user has * nothing to configure, they have something to retry. */ export declare class CredentialStoreUnavailableError extends Error { readonly errorCode = "credential_store_unavailable"; readonly retryable = true; constructor(cause?: unknown); } /** * The one place that decides when an unanswered credential lookup becomes a * user-visible retryable error. A store that is not configured at all is a real * "no credential"; a store that timed out or dropped the connection is not. */ export declare function assertCredentialStoreReadable(result: { lookupFailed: boolean; cause?: unknown; }): void; /** * Deployment-level credential fallback for single-tenant/local operation. * Multi-tenant call sites must gate this explicitly before calling. */ export declare function readDeployCredentialEnv(key: string): string | undefined; /** * Deployment-level credentials are safe as a runtime fallback only in local / * single-tenant contexts. In hosted production with a shared database, every * signed-in user needs their own user/org/workspace credential for * identity-bearing provider keys so one deploy key does not silently * impersonate another tenant. App-provided service credentials are different: * they configure the deployed app itself rather than identifying a user. This * includes LLM keys that let the app developer pay for model usage, email * transport configuration owned by the deployment, and OAuth client * credentials whose per-user identity remains in scoped OAuth tokens. Key-aware * callers may use those env vars. * * @deprecated Use `canUseDeployCredentialFallbackForRequest()` for generic * provider secrets. This stricter helper remains for legacy call sites with * identity-bearing deploy credentials. */ export declare function isDeployCredentialFallbackAllowed(): boolean; export declare function canUseDeployCredentialFallbackForRequest(key?: string): boolean; type BuilderCredentialSource = "user" | "org" | "workspace" | "env"; export declare function isBuilderPrivateKey(value: string | null | undefined): boolean; /** * Resolve a Builder credential for the current request. User/org credentials * win; deployment env is only a fallback. This lets local/root .env keys keep * a template working while still allowing users to connect their own Builder * account from Settings or onboarding. */ export declare function resolveBuilderCredential(key: string): Promise; /** * True when `BUILDER_PRIVATE_KEY` is set at the deployment level. This means * a deploy-level fallback exists; it does not prevent per-user connect. */ export declare function isBuilderEnvManaged(): boolean; /** * Resolve the Builder private key for the current request. User/org OAuth * credentials win; deploy-level `BUILDER_PRIVATE_KEY` is the fallback. */ export declare function resolveBuilderPrivateKey(): Promise; /** * Resolve the current user's Builder auth header. * Returns `"Bearer "` or null. */ export declare function resolveBuilderAuthHeader(): Promise; /** * Check whether the current user has a Builder private key configured * (per-user or deployment-level). */ export declare function resolveHasBuilderPrivateKey(): Promise; /** * Check whether the current request has the complete Builder credential bundle * needed for Builder-backed assistant/image-generation calls. */ export declare function resolveHasCompleteBuilderConnection(): Promise; /** * Resolve where the effective Builder assistant connection came from. This * intentionally requires a complete private+public key pair from one scope so * status UIs don't report a mixed user/org credential set as connected. */ export declare function resolveBuilderCredentialSource(): Promise; export interface BuilderCredentialsDetailed { privateKey: string | null; publicKey: string | null; userId: string | null; orgName: string | null; orgKind: string | null; subscription: string | null; subscriptionLevel: string | null; subscriptionName: string | null; isEnterprise: boolean | null; isFreeAccount: boolean | null; /** Which scope answered, or null when nothing did. */ source: BuilderCredentialSource | null; /** * True when reading the credential store itself failed (db timeout, etc). * Callers must report this as retryable rather than "not configured". */ lookupFailed: boolean; /** The failure, when there was one, so callers can classify it. */ cause?: unknown; } /** * Resolve the Builder assistant credential bundle from one complete scope, * plus where it came from and whether the credential-store read itself * failed. A partial user row is treated as a miss so the org-shared * connection can still power the assistant for teammates. * * Callers that only need the plain credential bundle (the historical shape) * should use `resolveBuilderCredentials()` instead. */ export declare function resolveBuilderCredentialsDetailed(): Promise; /** * Resolve the Builder assistant credential bundle from one complete scope. * Kept to its original return shape (no `source`/`lookupFailed`) for existing * callers; use `resolveBuilderCredentialsDetailed()` for those fields. */ export declare function resolveBuilderCredentials(): Promise<{ privateKey: string | null; publicKey: string | null; userId: string | null; orgName: string | null; orgKind: string | null; subscription: string | null; subscriptionLevel: string | null; subscriptionName: string | null; isEnterprise: boolean | null; isFreeAccount: boolean | null; }>; /** * Stale markers expire so a rejected model or a transient gateway failure * cannot pin a signed-in user to "Builder not connected" forever. Only a * successful gateway call clears the marker early, and that call never happens * while the marker is what makes the connection look broken. */ export declare const BUILDER_AUTH_FAILURE_TTL_MS: number; export interface BuilderCredentialAuthFailure { fingerprint: string; message: string; status?: number; code?: string; at: number; ownerEmail?: string | null; orgId?: string | null; } export declare function builderCredentialFingerprint(privateKey?: string | null, publicKey?: string | null): string | null; export declare function getBuilderCredentialAuthFailure(creds?: { privateKey?: string | null; publicKey?: string | null; }): Promise; export declare function recordBuilderCredentialAuthFailure(details?: { status?: number; code?: string; message?: string; }): Promise; export declare function clearBuilderCredentialAuthFailure(creds: { privateKey?: string | null; publicKey?: string | null; }): Promise; /** Stale failure markers expire so a transient 401 cannot permanently block deploy keys. */ export declare const PROVIDER_AUTH_FAILURE_TTL_MS: number; export interface ProviderCredentialAuthFailure { fingerprint: string; key: string; message: string; status?: number; code?: string; at: number; ownerEmail?: string | null; orgId?: string | null; } export declare function providerCredentialFingerprint(key?: string | null, value?: string | null): string | null; export declare function getProviderCredentialAuthFailure(opts: { key?: string | null; value?: string | null; }): Promise; export declare function recordProviderCredentialAuthFailure(opts: { key?: string | null; value?: string | null; status?: number; code?: string; message?: string; }): Promise; export declare function clearProviderCredentialAuthFailure(opts: { key?: string | null; value?: string | null; }): Promise; /** * Write Builder credentials to `app_secrets`. * * Scope decision (see `resolveCredentialWriteScope`): when the connecting * user is owner/admin of an active org we write at `scope: "org"` so every * member of that org auto-resolves the credentials via * `resolveBuilderCredential`'s org fallback — no per-user re-connect * needed. A plain member or a user with no active org writes at * `scope: "user"` (the safe default that doesn't trample the org's shared * connection). * * Stale-credential cleanup: before writing the new values we (1) clear ALL * five BUILDER_* keys at the target scope, so optional fields the new * connection doesn't carry (e.g. user picked a Builder space that returns * no orgName) don't leave the previous connection's metadata behind, and * (2) when writing at org scope, also clear the writer's own user-scope * BUILDER_* rows so a stale personal override from an earlier connect * doesn't shadow the new org write on resolution (user scope wins org * scope by design — see `resolveScopedBuilderCredential`). The org-scope * row is intentionally left alone when writing at user scope: that row is * shared with the rest of the org and a single user's personal override * shouldn't blow it away. (Victoria's "I signed in again with my Builder * space and it still says no credits" report on 2026-05-11 was exactly * this stale-shadow case.) * * Returns the actual scope/scopeId used so the caller can show "Connected * for Builder.io" vs "Connected (personal)" in the UI. */ export declare function writeBuilderCredentials(email: string, creds: { privateKey: string; publicKey: string; userId?: string | null; orgName?: string | null; orgKind?: string | null; subscription?: string | null; subscriptionLevel?: string | null; subscriptionName?: string | null; isEnterprise?: boolean | null; isFreeAccount?: boolean | null; }, options?: { orgId?: string | null; role?: string | null; }): Promise<{ scope: "user" | "org"; scopeId: string; }>; /** * Delete Builder credentials. * * Default behaviour: clears only this user's per-user override (so a * member can disconnect their personal Builder identity without * collapsing the org-wide connection for every teammate). To revoke the * org's shared connection, pass `{ orgId, role }` for an owner/admin — * matching the same authority gate `writeBuilderCredentials` uses on * write. Plain members can never reach the org-scoped row. */ export declare function deleteBuilderCredentials(email: string, options?: { orgId?: string | null; role?: string | null; }): Promise<{ scope: "user" | "org"; scopeId: string; }>; /** * Resolve a request-scoped secret. Reads from `app_secrets` first (current * user override, active org, workspace row for that org, then the solo * workspace row); falls back to `process.env` only when the deploy fallback * policy allows it. */ export declare function resolveSecret(key: string): Promise; /** * `resolveSecret` without the throw: reports whether the miss is definitive * (`lookupFailed: false` — no such row anywhere the caller can reach) or just * unknown (`lookupFailed: true` — the store or the org membership behind it * could not be read). */ export declare function resolveSecretDetailed(key: string): Promise<{ value: string | null; lookupFailed: boolean; cause?: unknown; }>; /** * True when a Builder private key is configured at the deployment level. * * This is the same env-only check as `isBuilderEnvManaged()`. For "does this * request have access to Builder via user/org/env credentials?" use the async * `resolveHasBuilderPrivateKey()`. */ export declare function hasBuilderPrivateKey(): boolean; /** The origin for Builder-proxied API calls. Overridable for testing. */ export declare function getBuilderProxyOrigin(): string; /** * Base URL for the public Builder LLM gateway, which lives at * api.builder.io/agent-native/gateway. * Override via BUILDER_GATEWAY_BASE_URL for staging / testing. */ export declare function getBuilderGatewayBaseUrl(): string; /** * Base URL for Builder-managed image generation. * Override via BUILDER_IMAGE_GENERATION_BASE_URL for staging / testing. */ export declare function getBuilderImageGenerationBaseUrl(): string; /** * Base URL for Builder-managed web search. * Override via BUILDER_WEB_SEARCH_BASE_URL for staging / testing. */ export declare function getBuilderWebSearchBaseUrl(): string; /** Authorization header value for Builder-proxied calls (env-only). */ export declare function getBuilderAuthHeader(): string | null; export {}; //# sourceMappingURL=credential-provider.d.ts.map