/**
 * Content-addressed fact serving — cell-scoped, per-reference reads.
 *
 * Two routes serve fact bytes from the PG-backed fact store:
 *
 * - `GET /api/cells/:cell_id/facts/:hash` — the **per-reference read**.
 *   The request names the referencing cell. Authz is scoped to that one
 *   reference: `can_view_cell(caller, cell) AND cell.refs includes hash`.
 *   This is the path non-admin callers use, and the only path for
 *   confidential content.
 * - `GET /api/facts/:hash` — the **bare-hash read**, restricted to admins.
 *
 * Embedded facts stream from the `fact.bytes` PG column; external facts
 * (filesystem-backed `file:<shard>/<rest>` URLs) either return an
 * `X-Accel-Redirect` header pointing into nginx's internal facts location
 * (production) or stream from disk via the filesystem `FactExternalFetcher`
 * (dev / tests). The runtime mode is selected by the optional
 * `x_accel_redirect_prefix` factory option — set in prod, unset in dev.
 *
 * REST, not RPC: binary responses don't fit the JSON-RPC envelope.
 *
 * ## Authorization — authz lives on the cell→fact edge, not the hash
 *
 * Facts are global, content-addressed, owner-less bytes: identical bytes
 * from different owners dedup to **one** `fact` row. Keying access control
 * on the bare hash therefore unions visibility across every owner that
 * references it — A's private bytes leak the instant B references identical
 * bytes from a public cell. The fix is to scope authz to the
 * `(cell, hash)` edge: a caller reads a fact *through a specific cell it
 * can view that references the hash*. Dedup becomes a pure storage
 * optimization with zero authz consequence — whether two owners' bytes
 * share a `fact` row is invisible to the read check.
 *
 * The cell-scoped route resolves the named cell, requires
 * `can_view_cell(caller, cell)`, and requires `cell.refs` to include the
 * hash. B publishing identical bytes from B's public cell makes them
 * readable *via B's cell* — it never touches A's private reference.
 *
 * The bare-hash route is **admin-only**: an admin's reach already spans
 * every cell, so serving by bare hash grants no escalation. Non-admin
 * callers are rejected at the auth phase and never reach the handler.
 * (Explicitly-public facts — a producer opting bytes into world-readable
 * status — are a future refinement; there is no such concept today, so
 * the bare-hash route stays strictly admin-gated.)
 *
 * Auth shape on the cell-scoped route is `{account: 'none', actor: 'none'}`
 * — the dispatcher's authorization phase is skipped for pure-public routes,
 * so the handler builds the `RequestContext` itself from `c.var.account_id`
 * (populated by the `/api/*` session middleware) by resolving the caller's
 * single actor and loading their role_grants. Unauthed callers pass through
 * with `req_ctx: null` and are admitted only by a `cell.visibility ===
 * 'public'` cell. Multi-actor accounts fall through with `req_ctx: null`
 * — there's no `acting?` slot on a pure-public route, so multi-actor
 * callers are treated as anonymous.
 *
 * 404 is the universal "not viewable" response: missing fact, missing or
 * unviewable cell, or the cell doesn't reference the hash. We deliberately
 * don't distinguish 403 from 404 — neither the existence of a fact nor the
 * existence of a cell→fact edge should leak through the public surface.
 *
 * Content-addressed serving of inline `blake3:` images (a markdown doc cell
 * with embedded image refs) works through this model: the referencing cell
 * is the doc cell, so serving goes view-doc-cell → doc-cell-refs-include-hash
 * → serve.
 *
 * ## Defense-in-depth
 *
 * The `external_url` regex is re-validated before issuing
 * `X-Accel-Redirect` even though `PgFactStore.put_ref` only writes
 * `file:<shard>/<rest>`-shaped URLs. A future row-injection bug
 * upstream would otherwise hand nginx an attacker-controlled path.
 *
 * @module
 */
import type { Logger } from '@fuzdev/fuz_util/log.js';
import { type RouteSpec } from '../http/route_spec.js';
import type { AppDeps } from '../auth/deps.js';
export interface CreateServeFactRouteSpecOptions {
    /**
     * App deps reference. Currently unused at handler time (cell + fact
     * tables are read via `RouteContext.db`); kept on the factory
     * signature for symmetry with sibling route factories and to give
     * future role_grant-scoped viewer extensions somewhere to read other
     * deps without changing the public shape.
     */
    deps: AppDeps;
    /** Absolute path of the facts directory. Used for the dev/test streaming path. */
    facts_dir: string;
    /**
     * When set, external facts return an `X-Accel-Redirect` pointing at
     * `${prefix}<shard>/<rest>` — nginx's internal facts location serves
     * the bytes. When unset, external facts stream from
     * `<facts_dir>/<shard>/<rest>` directly. Production sets this (e.g.
     * `/_facts/`); tests + dev leave it unset.
     */
    x_accel_redirect_prefix?: string;
    log: Logger;
}
/**
 * Build the cell-scoped `GET /api/cells/:cell_id/facts/:hash` `RouteSpec`
 * — the per-reference read.
 *
 * Resolves the named cell (404 if missing / soft-deleted), requires
 * `can_view_cell(caller, cell)` AND `cell.refs` to include the hash
 * (else 404, masked), then serves the bytes. Authz is scoped to this one
 * `(cell, hash)` edge — never unioned across the fact's other referrers.
 *
 * Pure-public auth — the handler builds the per-request `RequestContext`
 * from `c.var.account_id` and enforces visibility per-reference.
 */
export declare const create_serve_cell_fact_route_spec: (options: CreateServeFactRouteSpecOptions) => RouteSpec;
/**
 * Build the admin-only bare-hash `GET /api/facts/:hash` `RouteSpec`.
 *
 * An admin's reach already spans every cell, so serving by bare hash grants
 * no escalation — the union concern that made this route a cross-owner leak
 * for non-admins is vacuous for an admin. Non-admin callers are rejected at
 * the auth phase (403) and never reach the handler. Confidential non-admin
 * reads always go through the cell-scoped route above.
 *
 * Auth is `{account: 'required', actor: 'required', roles: ['admin']}` —
 * the dispatcher's authorization phase resolves the acting actor and the
 * post-authorization guard enforces the admin role before the handler runs.
 * The handler re-checks `has_role(_, admin)` as defense-in-depth so a future
 * mounting/auth-shape regression fails closed rather than serving by bare
 * hash to a non-admin.
 */
export declare const create_serve_fact_route_spec: (options: CreateServeFactRouteSpecOptions) => RouteSpec;
//# sourceMappingURL=serve_fact_route.d.ts.map