/** * Route effect/capability assurance. Declarations are reflected at build/CI time; runtime beacons * are a fail-closed defence at owned effect seams. Static provenance remains the security anchor. */ import type { DataClassification } from "./classification.js"; import { type EffectLifecycleObserver } from "./effect-lifecycle.js"; import { type AroundCapabilityOptions, type CapabilityApprovalGate, type CapabilityApprovalInput, type CapabilityExecutionIdentity, type CapabilityExecutionJournal, type CapabilityInterceptor, type CapabilityInterceptorEvent, type CapabilityInterceptorNext, type CapabilityUseEvent, validCapabilityId } from "./internal/capability-runtime.js"; import { type EffectCost, type EffectPhase } from "./ledger.js"; export type { AroundCapabilityOptions, CapabilityApprovalGate, CapabilityApprovalInput, CapabilityExecutionIdentity, CapabilityExecutionJournal, CapabilityInterceptor, CapabilityInterceptorEvent, CapabilityInterceptorNext, CapabilityUseEvent, EffectLifecycleObserver, }; /** * Framework wiring: put a journal on a request context so every `executeCapability` on that request is * journaled without each call site threading it. Not for application code - install the * `durableCommand()` adapter, which calls this and declares the assurance evidence that goes with it. * * Symbol-keyed, mirroring `attachEffectLedger`: it cannot collide with an application field and does * not show up in a context spread or a log of it. * * Deliberately here rather than beside the journal TYPE in `internal/capability-runtime.ts`. That * module is reachable from a bare server - it holds the route-capability guard - so a seam living * there ships to every app, while here it arrives only with `executeCapability`, the one thing that * reads it. Measured rather than assumed: in the internal module this cost every bundle row in the * size matrix 10-18 bytes gzip, enough to put `nifra-mcp` over its ceiling. */ export declare function attachCapabilityJournal(context: object, journal: CapabilityExecutionJournal): void; /** The journal installed for this request, if any. */ export declare function capabilityJournalOf(context: object): CapabilityExecutionJournal | undefined; export type CapabilityZone = "domain" | "operational"; export type CapabilityAccess = "read" | "write"; export type CapabilityIdempotency = "none" | "request" | "durable"; export interface CapabilityDefinition { readonly id: string; /** Domain state is subject to HTTP method semantics; operational writes (logs/metrics) are not. */ readonly zone: CapabilityZone; readonly access: CapabilityAccess; /** Evidence required when this capability may execute. Default `none`. */ readonly idempotency?: CapabilityIdempotency; } export interface CapabilityImportRule { /** Exact module specifier, or a trailing `/*` prefix rule. */ readonly specifier: string; readonly capabilities: readonly string[]; /** * Allow this seam to be absent. A rule that matches no import anywhere in the project is reported * as `unmatched-provenance-seam` by default, because a specifier written differently from the * import it means to govern (`"./db/client.ts"` vs `import "./db/client"`) governs nothing and * every route touching that effect still passes. Set this only for a policy shared across projects * where some of them genuinely do not use the seam. */ readonly optional?: boolean; } export interface ForbiddenCapabilityImport { /** Exact module specifier, or a trailing `/*` prefix rule. */ readonly specifier: string; readonly reason: string; } export interface CapabilityRouteSelector { readonly methods?: readonly string[]; readonly paths?: readonly string[]; } export interface CapabilityRouteModule { readonly match: CapabilityRouteSelector; /** Project-relative modules that implement the selected routes. */ readonly modules: readonly string[]; } export interface CapabilityProvenancePolicy { readonly imports: readonly CapabilityImportRule[]; readonly forbiddenImports: readonly ForbiddenCapabilityImport[]; /** Explicit associations for contract/generated routes that source scanning cannot locate. */ readonly routeModules?: readonly CapabilityRouteModule[]; } export interface CapabilityPolicy { readonly definitions: readonly CapabilityDefinition[]; /** Required: capability assurance without a static provenance firewall is incomplete. */ readonly provenance: CapabilityProvenancePolicy; /** Default `capabilities.lock.json`. */ readonly lockfile?: string; } export type CapabilityEvidenceKind = "static" | "runtime"; /** Token-only effect evidence. `source` is an adapter/module id, never request or business data. */ export interface CapabilityEvidence { readonly id: string; readonly kind: CapabilityEvidenceKind; readonly source: string; } export interface RouteCapabilityEvidence { readonly method: string; readonly path: string; /** True only when the route's reachable module graph was actually scanned. */ readonly covered: boolean; readonly evidence: readonly CapabilityEvidence[]; } export interface CapabilityEvidenceSet { readonly routes: readonly RouteCapabilityEvidence[]; } export type CapabilityFindingCode = "unknown-capability" | "provenance-uncovered" | "undeclared-capability-evidence" | "safe-method-domain-write" | "unconfined-write-reach" | "missing-request-idempotency" | "missing-durable-idempotency" | "forbidden-effect-import" | "provenance-truncated" | "unmatched-provenance-seam"; export interface CapabilityFinding { readonly code: CapabilityFindingCode; readonly method: string; readonly path: string; readonly capability?: string; readonly message: string; } export interface AssuredCapabilityRoute { readonly method: string; readonly path: string; readonly declared: readonly string[]; readonly evidence: readonly CapabilityEvidence[]; /** Declared capabilities without static/runtime evidence. Informational, never treated as proof. */ readonly unproven: readonly string[]; readonly covered: boolean; /** Highest data-sensitivity the response carries, when the route declares it. */ readonly classification?: DataClassification; } export interface CapabilityAssuranceReport { readonly ok: boolean; readonly routes: readonly AssuredCapabilityRoute[]; readonly findings: readonly CapabilityFinding[]; } export interface CapabilitySnapshotRoute { readonly method: string; readonly path: string; readonly declared: readonly string[]; readonly evidenced: readonly string[]; readonly unproven: readonly string[]; /** Recorded so a route that starts returning `pii`/`secret` flips the lockfile and forces a review. */ readonly classification?: DataClassification; } export interface CapabilitySnapshot { readonly nifraCapabilities: 1; readonly routes: readonly CapabilitySnapshotRoute[]; } export { validCapabilityId }; /** Validate and freeze a capability/provenance policy. */ export declare function defineCapabilityPolicy(policy: CapabilityPolicy): CapabilityPolicy; /** Compare declared route capabilities against coverage-qualified static/runtime evidence. */ export declare function evaluateCapabilityAssurance(source: unknown, policyInput: CapabilityPolicy, evidenceSet: CapabilityEvidenceSet): CapabilityAssuranceReport; /** * Optional effect-ledger fields for one `useCapability` beacon. Token-only by design: an adapter * names *what* it touched and *how much resource* it used - never the value it read or wrote. */ export interface UseCapabilityOptions { /** Adapter/resource token recorded on the ledger entry (`repo:orders`). */ readonly target?: string; /** Dimensionless resource counters recorded on the ledger entry. */ readonly cost?: EffectCost; /** Keyed payload digest (see `computeEffectDigest`). */ readonly digest?: string; } export interface CapabilityOutcomeOptions extends UseCapabilityOptions { /** An outcome can only be recorded after the intent beacon succeeded. */ readonly phase: Exclude; /** Outcome error as a bounded token code. */ readonly error?: { readonly code: string; }; } /** Durable controls consumed by `executeCapability`; none of these fields enter the effect ledger. */ export interface CapabilityExecutionOptions extends UseCapabilityOptions { readonly approval?: CapabilityApprovalInput & { readonly gate: CapabilityApprovalGate; }; readonly journal?: CapabilityExecutionJournal; } /** Context passed to the owned effect callback. Use the signal for cancellation-aware I/O. */ export interface CapabilityExecutionContext { readonly effectId: string; readonly signal: AbortSignal; } export type CapabilityExecutor = (execution: CapabilityExecutionContext) => T | PromiseLike; /** A capability admission policy returned without calling `next()`. */ export declare class CapabilityDeniedError extends Error { readonly capability: string; readonly effectId: string; constructor(capability: string, effectId: string); } /** A capability admission policy exceeded its configured bound. */ export declare class CapabilityInterceptorTimeoutError extends Error { readonly capability: string; readonly effectId: string; readonly timeoutMs: number; constructor(capability: string, effectId: string, timeoutMs: number); } /** The request was cancelled while capability admission was pending. */ export declare class CapabilityAdmissionAbortedError extends Error { readonly capability: string; readonly effectId: string; constructor(capability: string, effectId: string); } /** An interceptor called its one-shot `next()` continuation more than once. */ export declare class CapabilityInterceptorProtocolError extends Error { readonly capability: string; readonly effectId: string; constructor(capability: string, effectId: string); } /** The effect may have committed, but its durable terminal transition could not be recorded. */ export declare class CapabilityJournalTransitionError extends Error { readonly capability: string; readonly effectId: string; readonly transition: "committed"; constructor(capability: string, effectId: string, transition: "committed"); } /** * Runtime effect beacon for owned adapters. It fails closed when the route omitted the capability or * when no route guard is present. Static provenance is still required: code can bypass a beacon. * When the server enabled the effect ledger, each beacon call also appends one token-only entry. */ export declare function useCapability(context: object, capability: string, options?: UseCapabilityOptions): void; /** Record the terminal outcome of an already-admitted capability without debiting admission twice. */ export declare function recordCapabilityOutcome(context: object, capability: string, options: CapabilityOutcomeOptions): void; /** * Execute one owned effect behind a fail-closed capability boundary. The boundary assigns a stable * effect id, records intent before execution, and records exactly one terminal outcome automatically. * The callback result and errors never enter the token-only ledger. */ export declare function executeCapability(context: object, capability: string, options: CapabilityExecutionOptions, executor: CapabilityExecutor): Promise; /** * Read the route's token-only declaration for admission plugins. This intentionally exposes neither * the request nor runtime evidence; it is the stable public seam for private entitlement policy. */ export declare function declaredCapabilities(context: object): readonly string[]; /** Deterministic, PII-free lockfile material. */ export declare function snapshotCapabilities(report: CapabilityAssuranceReport): CapabilitySnapshot; //# sourceMappingURL=capabilities.d.ts.map