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
factory option — a validated XAccelConfig (built via
create_x_accel_config in server/x_accel.ts) set in prod, unset in dev.
The handle can only be obtained by proving the facts nginx location is
internal;, so X-Accel serving can't be enabled against a public location.
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.
Untrusted-content hardening
Fact bytes are served with a producer-supplied content_type emitted
verbatim, inline. To stop a fact stored as (or sniffable to) text/html
from executing as stored XSS for any reader of a referencing cell, every
served-fact response carries X-Content-Type-Options: nosniff +
Content-Security-Policy: default-src 'none'; sandbox (the
FACT_SECURITY_HEADERS set, applied in all three serve branches). The same
pair, byte-identical, is set by the Rust twin (fuz_fact_serving::serve).
Content-Disposition: attachment is deliberately not added — it would
force-download and break legitimate inline image rendering.
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.