/** * Policy you can review: guardrails, routing, budgets, residency, and credentials as one versioned * document the application supplies, validated when the client starts, compared and checked by * `nexus policy check` in CI, and recorded on every operation it decides. */ import { type SecretSource } from '../credentials/index.js'; import { type BudgetUsageStore } from '../lifecycle/budget.js'; import { type RegionRule } from '../residency/index.js'; import type { NexusAIConfig } from '../types/config.js'; import type { PolicyReference, ProviderGate } from '../types/lifecycle.js'; import type { RegionDefinition } from '../types/residency.js'; import type { SecurityConfig } from '../types/security.js'; export type { PolicyReference, ProviderGate } from '../types/lifecycle.js'; /** Which providers and models may serve: allow lists, where only what is listed may, and deny lists. */ export interface PolicyAccess { /** Only these providers may serve, by the name each is registered under. Absent means any. */ allowProviders?: string[]; /** These providers never serve. */ denyProviders?: string[]; /** Only these models may serve. Absent means any. */ allowModels?: string[]; /** These models never serve. */ denyModels?: string[]; } /** A policy's routing rules: for every tenant, and narrowed per tenant. */ export interface PolicyRouting extends PolicyAccess { /** Each tenant's own rules, applied after those for every tenant. */ tenants?: Record; } /** A policy's budgets, in US dollars per period. */ export interface PolicyBudgets { /** How long a budget lasts before it resets. Defaults to `month`. */ period?: 'hour' | 'day' | 'week' | 'month'; /** The budget of a tenant with none of its own, and of operations with no tenant. Absent means none. */ default?: number; /** Each tenant's budget. */ tenants?: Record; } /** A policy's residency: regions, what runs in each, and each tenant's regions, as `residency()` takes them. */ export interface PolicyResidency { /** Every region, with its providers and twins. */ regions: Record; /** Each tenant's regions: `any`, `refuse`, a region, or a list, the first its home. */ tenants?: Record; /** The regions of a tenant `tenants` does not name. Defaults to `any`. */ unassigned?: RegionRule; /** The regions of an operation with no tenant. Defaults to `any`. */ untenanted?: RegionRule; } /** Where each tenant's provider keys are read, as `tenantCredentials()` takes it. */ export interface PolicyCredentials { /** The secret's name, with `{tenant}` and `{provider}` filled in. Defaults to `{tenant}/{provider}`. */ secretName?: string; /** A tenant with no secret: `refuse`, the default, or `config`, the provider's configured key. */ missing?: 'refuse' | 'config'; /** How long a credential is reused, in milliseconds. */ cacheMs?: number; /** How long a missing secret is remembered as missing, in milliseconds. */ missingCacheMs?: number; /** While the secret source fails, how long a credential past its time still carries calls. */ staleMs?: number; } /** A policy as its file holds it: plain JSON, every section optional but the name and the version. */ export interface PolicyDocument { /** The policy's name, such as `production`. */ name: string; /** Its version, as you number it: raised with every change, and recorded on every operation. */ version: string; /** What it is for. */ description?: string; /** Guardrails: the client's `security` settings, as plain data. */ guardrails?: SecurityConfig; /** Which providers and models may serve, for every tenant and per tenant. */ routing?: PolicyRouting; /** What each tenant may spend. */ budgets?: PolicyBudgets; /** Which regions each tenant may reach, and what runs where. */ residency?: PolicyResidency; /** Where each tenant's provider keys are read. */ credentials?: PolicyCredentials; /** * Changes this version makes that widen what a tenant may reach, or that need a reviewer's eye, by * the path `nexus policy check` names, such as `residency.tenants.acme`: acknowledged here, in the * file a reviewer reads. */ acknowledged?: string[]; } /** A validated policy, with what each operation records of it. */ export interface Policy extends PolicyReference { /** The document, as validated. */ document: PolicyDocument; /** What each operation records. */ reference: PolicyReference; } /** One thing wrong with a policy document, at its path. */ export interface PolicyIssue { /** Where, as a dot path such as `residency.tenants.acme`. */ path: string; /** What is wrong. */ message: string; } /** Raised when a policy document is not valid, with every issue found. */ export declare class PolicyError extends Error { /** Every issue, in document order. */ readonly issues: PolicyIssue[]; constructor( /** Every issue, in document order. */ issues: PolicyIssue[]); } /** * Reads and validates a policy document: its JSON text, or the document itself. Throws a * `PolicyError` listing every issue, so a bad policy fails as the client starts, not on a request. */ export declare function parsePolicy(input: string | unknown): Policy; /** The SHA-256 of a policy's content, with its keys in order, so formatting never changes it. */ export declare function policyHash(document: PolicyDocument): string; /** What `applyPolicy()` needs that a document cannot hold. */ export interface ApplyPolicyOptions { /** Where tenants' provider keys are read, when the policy has a `credentials` section. */ secrets?: SecretSource; /** Where budget totals are kept, shared by every replica. Defaults to memory. */ budgetStore?: BudgetUsageStore; /** Replaces the clock, for tests. */ now?: () => Date; } /** * A client's configuration under a policy: its guardrails as the `security` settings, its global * model rules on routing, and, in `lifecycle`, its residency, its credentials over `secrets`, its * budgets as a ledger, a provider gate for its per-tenant routing rules, and the policy itself, which * every operation then records. What the policy does not say keeps the configuration's own. * * ```ts * const policy = parsePolicy(await readFile('policy.json', 'utf8')); * const ai = new NexusAI(applyPolicy({ providers }, policy, { secrets: awsSecrets(client) })); * ``` */ export declare function applyPolicy(config: NexusAIConfig, policy: Policy, options?: ApplyPolicyOptions): NexusAIConfig; /** * A provider gate from a policy's routing rules: every tenant's first, then the operation's tenant's. * A name in a list matches itself, and `*` in it matches anything, as `gpt-4o*` or `*`. */ export declare function providerGate(routing: PolicyRouting, label?: string): ProviderGate; /** How a change moves what tenants may reach. */ export type PolicyChangeKind = 'widened' | 'narrowed' | 'changed'; /** One change between two versions of a policy. */ export interface PolicyChange { /** Where, as a dot path such as `residency.tenants.acme`. */ path: string; /** `widened` lets something reach more; `narrowed`, less; `changed`, neither, but a reviewer should see it. */ kind: PolicyChangeKind; /** The value before. */ before?: unknown; /** The value after. */ after?: unknown; /** A sentence saying what changed. */ summary: string; } /** What `checkPolicyChange()` found. */ export interface PolicyCheck { /** Every change, in document order. */ changes: PolicyChange[]; /** Changes that widen or need review and are not acknowledged. */ unacknowledged: PolicyChange[]; /** True when the content changed but the version did not. */ versionUnchanged: boolean; /** True when nothing is unacknowledged and the version moved with the content. */ ok: boolean; } /** * Every change between two versions of a policy, each said to widen what a tenant may reach, narrow * it, or change it otherwise: a region a tenant gains, a provider or twin a region gains, an allow * list grown or a deny list shrunk, a budget raised, guardrails lowered, or a tenant's missing key * falling back to the shared one all widen; a home region moved, a guardrail setting changed, or the * name changed need review. */ export declare function comparePolicies(before: PolicyDocument, after: PolicyDocument): PolicyChange[]; /** * Checks a new version of a policy against the one before: every change, which of them widen or need * review without an acknowledgment, in the new document's `acknowledged` or `accept`, and whether the * version moved with the content. `ok` is what CI gates on. */ export declare function checkPolicyChange(before: PolicyDocument, after: PolicyDocument, options?: { accept?: readonly string[]; }): PolicyCheck;