import type { FastifyReply, FastifyRequest } from "fastify"; import type { ApiKeyCapability, MetadataAuthorKind, ResolvedApiKey } from "@uptimizr/db"; import type { CollectorStore } from "./store.js"; /** * Request-boundary authentication and capability checks for the collector * (#309, ADR 0051 §7). * * A key resolves to a **capability set**, not a single capability, so the same * plumbing serves reads (`query`), raw per-session access (`query:raw`, which * additionally requires `ENABLE_RAW_SESSION_RETENTION` — ADR 0003) and the * metadata write path (`annotate`, whose endpoints arrive with #310). * * Resolution happens once per request, in an `onRequest` hook, so that: * * 1. the rate limiter can key on the API key id and honour the key's own * per-key budget before any handler runs, and * 2. handlers and the audit hook read the already-resolved key instead of * hitting the metadata store again. */ declare module "fastify" { interface FastifyRequest { /** * What this request's `x-api-key` resolved to, or `null` when * unauthenticated. This is the key's **record** — project, key id, * capabilities, rate limit — and deliberately never the key itself, which * is read from the header and discarded. */ resolvedKey: ResolvedApiKey | null; /** Rows in the response body, when it serialized to an array (audit log). */ auditRowCount: number | null; /** * What the audit log should record as this request's parameters, when the * querystring is not the whole story. `POST /api/v1/query` carries its * query in the body (ADR 0051 §3), so without this the audit trail would * show a bare path and nothing about what was asked for. */ auditParams: unknown; } } /** Header a first-party UI sets to identify itself (see `isDashboardRequest`). */ export declare const CLIENT_HEADER = "x-uptimizr-client"; /** * Whether this request is "the dashboard's own session" — the requests the * audit log deliberately skips so an agent's activity is not buried under a * dashboard's panel refreshes. * * It is identified by the `x-uptimizr-client: dashboard` header that * `@uptimizr/react`'s `CollectorApi` sets by default; the in-browser assistant * and any other agent client send a different value (or none) and are audited. * * This is a **volume filter, not a security boundary**: anyone holding the key * could send the header, and anyone holding the key can already do everything * the key allows. Set `AUDIT_DASHBOARD_REQUESTS=1` to record every * authenticated request without exception. */ export declare function isDashboardRequest(request: FastifyRequest): boolean; /** The route the bearer-header alias below is accepted on, and only that one. */ export declare const MCP_ROUTE_URL = "/mcp"; /** * Accept `Authorization: Bearer ` as an alias for `x-api-key` **on the * hosted MCP route only** (ADR 0051 §7, design sketch §G.1). * * MCP clients configure a remote server as a URL plus headers and send the * bearer form the MCP specification describes; the rest of the collector has * always used `x-api-key`. Normalising one into the other here — in the same * `onRequest` hook, *before* {@link attachApiKey} — keeps exactly one * key-resolution path, so the capability check, the audit row and the per-key * rate-limit bucket all work for a bearer-authenticated MCP client too. * * Scoped to `/mcp` on purpose: this is an alias for one route, not a new * site-wide authentication scheme. An explicit `x-api-key` always wins, and a * malformed or empty bearer value is ignored rather than rejected, so the usual * "no key → 401" path handles it. */ export declare function normalizeMcpBearer(request: FastifyRequest): void; /** * Who a metadata write (#310, ADR 0051 §5) is attributed to. * * It reuses the surface marker the audit log already distinguishes on rather * than inventing a second notion of "who": the dashboard's own session is a * **person** clicking in a UI, and everything else holding an `annotate` key — * an MCP client, the in-browser assistant writing up its own answer, a * scheduled report — is an **agent**. * * It is derived from the request, never read from the payload, so a stored row * cannot claim an authorship its writer did not send. Like the audit filter it * is an honest label, not a security boundary. */ export declare function metadataAuthorKind(request: FastifyRequest): MetadataAuthorKind; /** * Resolve the request's `x-api-key` into {@link FastifyRequest.resolvedKey}. * Never replies and never throws: an absent, unknown or revoked key simply * leaves `resolvedKey` null, and the helpers below turn that into a 401. */ export declare function attachApiKey(request: FastifyRequest, store: CollectorStore): Promise; /** * Require an authenticated key holding `capability`. Sends a 401 (no/invalid * key) or 403 (key lacks the capability) and returns `null` on refusal. * * Reads are always scoped to the authenticated project — a client-supplied * project id is never trusted. */ export declare function requireCapability(request: FastifyRequest, reply: FastifyReply, store: CollectorStore, capability: ApiKeyCapability): Promise; //# sourceMappingURL=auth.d.ts.map