/** * Route assurance: reflection-time proof that every route is classified and carries the enforcement * evidence its policy requires. Evaluation is pure and never runs on the request hot path. */ import { type CapabilityAccess, type CapabilityAssuranceReport, type CapabilityDefinition, type CapabilityPolicy, type CapabilityZone } from "./capabilities.js"; import { type DataClassification } from "./classification.js"; import type { AssuranceAttachment, AssuranceDeclaration, AssuranceEvidence, AssuranceScope } from "./internal/route-assurance.js"; import { withRouteAssurance } from "./internal/route-assurance.js"; import type { SealedEffectLedger } from "./ledger.js"; import type { NifraManifestSigner } from "./manifest.js"; import { type ReflectedRoute } from "./reflection.js"; import type { Method } from "./router/router.js"; export { type SecurityBaselineLevel, type SecurityBaselineOptions, securityBaseline, } from "./security/baseline.js"; export type { AssuranceAttachment, AssuranceDeclaration, AssuranceEvidence, AssuranceScope }; export { withRouteAssurance }; /** Isolated request executor used by adversarial contract verification. */ export type InvariantExecutor = (request: Request) => Response | Promise; /** Canonical evidence ids emitted by Nifra's official middleware modules. */ export declare const NIFRA_ASSURANCE: Readonly<{ readonly AUTHENTICATED: "nifra.authenticated"; readonly BODY_BOUNDED: "nifra.body-bounded"; readonly CSRF: "nifra.csrf"; readonly DURABLE_COMMAND: "nifra.durable-command"; readonly IDEMPOTENCY_KEY: "nifra.idempotency-key"; readonly IP_RESTRICTED: "nifra.ip-restricted"; readonly RATE_LIMITED: "nifra.rate-limited"; readonly SECURITY_HEADERS: "nifra.security-headers"; readonly RESPONSE_CONTRACT: "nifra.response-contract"; }>; /** * Publish enforcement evidence from OUTSIDE the plugin chain - the deployment shell that wraps the * app, the mount site, or the call that hands it to `serve`. When the thing doing the enforcing is * not a nifra plugin (a gateway, a service mesh, an outer framework), the alternative is switching * the affected rules off; this records what covers the routes instead, so the policy keeps running. * * const app = buildApp() // no assurance-bearing plugin in its .use() chain * assure(app, { id: NIFRA_ASSURANCE.AUTHENTICATED, source: "edge-gateway" }) * serve(app) * * `scope` defaults to `global`: the shell runs after every route is registered, so the evidence is * retroactive and app-wide. Narrow it with `methods`/`paths` (absolute globs), or pass * `scope: "subsequent"` to cover only routes registered after the call. * * Provenance is always `declared` - nifra did not install this enforcement and cannot observe it, so * a rule with `requireProvenance: "runtime"` still rejects the route, by design. */ export declare function assure(app: unknown, evidence: AssuranceAttachment | readonly AssuranceAttachment[]): void; export interface AssuranceRouteSelector { /** Omit for every method. */ readonly methods?: readonly Method[]; /** Absolute route globs. `*` matches one segment; final `**` matches zero or more. */ readonly paths?: readonly string[]; /** Restrict the rule to MCP tool routes (`true`) or non-tool routes (`false`). */ readonly tools?: boolean; /** * Match routes declaring ANY of these capability tokens. * * Lets a policy be written about what a route DOES rather than where it lives: "anything that writes * to the database must be authenticated" survives a route being moved or renamed, which a path glob * does not. Reflection already carries the declared tokens, so this reads what the route said about * itself. * * Naming exact tokens is precise but closed: a rule listing `db.write` does not cover `storage.write`, * and a capability added next year escapes it in silence. Prefer `access`/`zone` for a rule that is * meant to hold for a CLASS of effect. */ readonly capabilities?: readonly string[]; /** * Match routes declaring any capability whose definition has this access. * * This is the selector to reach for when the rule is "anything that writes must prove who asked": * it is keyed on what the capability IS rather than what it is called, so a token introduced later * is covered the day it is declared instead of the day someone remembers to widen the rule. * * Requires capability definitions. A policy using it without them is refused rather than quietly * matching nothing - a selector that matches nothing lets the route fall through to a laxer rule. */ readonly access?: CapabilityAccess; /** * Match routes declaring any capability in this zone. Combined with `access`, both must hold for the * SAME capability, so `{ access: "write", zone: "domain" }` is "writes business state" and does not * match a route that only writes an audit log. */ readonly zone?: CapabilityZone; /** Match routes whose response classification is at least this sensitivity. */ readonly classificationAtLeast?: DataClassification; /** * Match routes by whether they declare a request-body schema. `true` selects routes that parse a * body (the buffered-read surface F-001 is about); `false` selects bodyless routes. Reflection * already carries `schema.body`, so this reads what the route declared, not where it lives. */ readonly hasBody?: boolean; /** * Match routes by their effective transport body policy. `"unlimited"` is the explicit * streaming/upload exemption; `"bounded"` is any finite cap (an explicit number or the inherited * server default); `"unset"` is a route that declared no `bodyLimit` at all. Lets a baseline say * "a body-schema route may never be unlimited" as a first-class, movable invariant rather than a * path list. A route with no schema reports `"unset"` unless it names a limit. */ readonly bodyLimit?: "bounded" | "unlimited" | "unset"; } /** Extra inputs an assurance evaluation needs beyond the routes themselves. */ export interface AssuranceEvaluationOptions { /** * Capability definitions, required by any rule selecting on `access`/`zone`. Normally * `config.capabilities.definitions`. */ readonly definitions?: readonly CapabilityDefinition[]; } export interface AssuranceRule { /** Stable human-readable classification included in diagnostics. */ readonly name: string; readonly match: AssuranceRouteSelector; /** Evidence ids the route must carry. */ readonly require?: readonly string[]; /** Evidence ids the route must not carry (useful for public webhooks and health routes). */ readonly forbid?: readonly string[]; /** * Provenance required for every id in require. any (default) preserves compatibility with * existing policies; runtime rejects schema.assurance author assertions and accepts only * evidence installed by middleware/plugins or framework runtime policy. declared is useful for * explicitly reviewing in-handler assertions and should not be used as an enforcement gate. */ readonly requireProvenance?: "any" | "runtime" | "declared"; /** * When true, an authenticated route selected by this rule must also carry runtime CSRF evidence. * Enable this on rules covering cookie/session-authenticated browser routes; bearer-only APIs * should use a separate rule because they do not have browser ambient-authority exposure. */ readonly requireCsrfWithAuthenticated?: boolean; } export interface AssurancePolicy { /** First matching rule owns a route. Put exceptions before broad defaults. */ readonly rules: readonly AssuranceRule[]; /** Default `error`: an unclassified route fails closed. */ readonly unmatched?: "error" | "ignore"; /** Default false: reject an empty reflected source so a wrong import cannot pass CI silently. */ readonly allowEmpty?: boolean; /** * Default false. When true, a route matched by a **pure-classification** rule (no `require`, no `forbid`) * that carries NO enforcement evidence is reported (`classified-no-evidence`). This surfaces the gap the * feedback flagged: a classification-only policy silently degrades "proof" to a "label". Opt-in, because * a genuinely public route legitimately carries no evidence; enable it once your guards emit evidence * (inline `schema.assurance` or a `withRouteAssurance` middleware) to keep classification honest. */ readonly flagClassifiedWithoutEvidence?: boolean; } export type AssuranceFindingCode = "no-routes" | "unclassified-route" | "missing-evidence" | "forbidden-evidence" | "classified-no-evidence"; export interface AssuranceFinding { readonly code: AssuranceFindingCode; readonly method: string; readonly path: string; readonly rule?: string; readonly evidence?: string; readonly message: string; } export interface AssuredRoute { readonly method: string; readonly path: string; readonly rule?: string; readonly evidence: readonly AssuranceEvidence[]; readonly missing: readonly string[]; readonly forbidden: readonly string[]; } export interface AssuranceReport { readonly ok: boolean; readonly routes: readonly AssuredRoute[]; readonly findings: readonly AssuranceFinding[]; /** Present when the config enables capability/effect assurance. */ readonly capabilities?: CapabilityAssuranceReport; } /** Application-supplied verification rules. The CLI validates the executable rule shape at runtime. */ export interface AssuranceRulePack { readonly name: string; readonly rules: readonly unknown[]; } export interface IdempotencyWorkload { readonly name: string; readonly run: () => Promise | SealedEffectLedger; } export interface AssuranceSizeBudget { readonly outDir?: string; readonly maxBytes?: number; readonly maxGzipBytes?: number; } export interface AssuranceConfig { readonly source: unknown; readonly policy: AssurancePolicy; readonly capabilities?: CapabilityPolicy; /** Off-request-path manifest artifact/signing integration. Private keys remain behind `signer`. */ readonly manifest?: { /** Default `nifra.manifest.json`. */ readonly path?: string; /** Resolve an operator key reference to a signer (KMS/HSM/local WebCrypto). */ readonly signer?: (keyRef: string) => NifraManifestSigner | Promise; }; /** Dynamic invariant execution is opt-in and must use a disposable/sandboxed target. */ readonly invariants?: { readonly executor: InvariantExecutor; }; /** Optional application-supplied rule packs appended after built-in verification rules. */ readonly rulePacks?: readonly AssuranceRulePack[]; /** Optional sink for an assurance bundle. The CLI validates the callable shape at runtime. */ readonly assureSink?: unknown; /** Optional deterministic effect workloads for the tests gate. */ readonly idempotency?: readonly IdempotencyWorkload[]; /** Optional output-size budget for the assurance bundle's size gate. */ readonly size?: AssuranceSizeBudget; } /** Validate and freeze an ordered assurance policy. */ export declare function defineAssurancePolicy(policy: AssurancePolicy): AssurancePolicy; /** Identity helper for a `nifra.assurance.ts` default export. */ export declare function defineAssuranceConfig(config: AssuranceConfig): AssuranceConfig; export declare function matchesAssuranceSelector(route: Pick, selector: AssuranceRouteSelector, definitions?: ReadonlyMap): boolean; /** Evaluate reflected route evidence against the first matching policy rule. */ export declare function evaluateRouteAssurance(source: unknown, policyInput: AssurancePolicy, options?: AssuranceEvaluationOptions): AssuranceReport; //# sourceMappingURL=assurance.d.ts.map