server
11 modules
server/app_backend.ts
App backend types and factory — database initialization + auth migrations + deps.
Provides AppBackend, CreateAppBackendOptions, and
create_app_backend().Vocabulary:
- AppDeps — stateless capabilities: injectable, swappable per environment
*Options— static values set at startup, per-factory configuration- Runtime state — mutable values (e.g.,
bootstrap_status) — NOT in deps or options
server/app_server_context.ts
The context threaded to route / RPC / WS endpoint factories.
Lives in its own module — separate from server/app_server.ts — so it can be consumed as a pure type without dragging in the server-assembly machinery. server/app_server.ts value-imports
hono(it builds theHonoapp), so importing anything from it forceshonoto be installed. Contract-only consumers — cross-process test surfaces, Rust-backed servers that reuse the route/RPC spec factories without running the TS server — need AppServerContext but nothono. Keeping the type here (onlyimport typedependencies, none of which value-importhono) lets them import it framework-free.server/app_server.ts
Server assembly factory.
Consumers provide a pre-initialized AppBackend and options (session, origins, routes);
create_app_server()handles middleware, bootstrap status, surface generation, and Hono app assembly.server/env.ts
Base server environment schema and validation.
Provides BaseServerEnv — a shared Zod schema for common server env vars that apps can use directly or extend with app-specific fields.
Generic env loading lives in env/load.ts.
server/fact_write.ts
Shared helper for routing fact bytes between embedded (PG
bytescolumn) and external (filesystem +put_ref) storage tiers based on size.External writes go through atomic temp+rename so the
factsrow never references a partial file; idempotence comes from POSIXrenameoverwrite +INSERT ... ON CONFLICT DO NOTHINGin the fact-store queries layer.server/file_fact_fetcher.ts
Filesystem-backed FactExternalFetcher.
Resolves
file:<shard>/<rest>external URLs against a configured facts directory. The URL shape is the canonical relative-path scheme: 2 hex chars for the shard subdir + 62 hex chars for the rest of the blake3 hash (64 hex chars total). Files live at<facts_dir>/<shard>/<rest>after the writer atomically temp+renames them in.Pre-filter regex:
^file:[0-9a-f]{2}/[0-9a-f]{62}$. A..segment can't match (.isn't in[0-9a-f]); neither can absolute paths, query strings, or any non-hex character. Defense-in-depth in front ofpath.join— the fetcher never trusts the URL came from a fact row, even though it always does in practice.The fetcher does NOT verify hash content —
PgFactStore.getcallsfact_hash_verify(hash, bytes)after the fetch and returns null on mismatch.Runtime: uses
node:fs/promises+node:fscreateReadStreamso the same code works under Deno (via node compat) and vitest.server/serve_fact_route.ts
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.bytesPG column; external facts (filesystem-backedfile:<shard>/<rest>URLs) either return anX-Accel-Redirectheader 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 optionalx_accelfactory 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 isinternal;, 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
factrow. 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 afactrow is invisible to the read check.The cell-scoped route resolves the named cell, requires
can_view_cell(caller, cell), and requirescell.refsto 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 fromc.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 withreq_ctx: nulland are admitted only by a `cell.visibility === 'public'cell. Multi-actor accounts fall through withreq_ctx: null` — there's noacting?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_typeemitted verbatim, inline. To stop a fact stored as (or sniffable to)text/htmlfrom executing as stored XSS for any reader of a referencing cell, every served-fact response carriesX-Content-Type-Options: nosniff+Content-Security-Policy: default-src 'none'; sandbox(theFACT_SECURITY_HEADERSset, applied in all three serve branches). The same pair, byte-identical, is set by the Rust twin (fuz_fact_serving::serve).Content-Disposition: attachmentis deliberately not added — it would force-download and break legitimate inline image rendering.Defense-in-depth
The
external_urlregex is re-validated before issuingX-Accel-Redirecteven thoughPgFactStore.put_refonly writesfile:<shard>/<rest>-shaped URLs. A future row-injection bug upstream would otherwise hand nginx an attacker-controlled path.server/startup.ts
Composable startup summary helpers.
Logs a human-readable summary from an AppSurface.
server/static.ts
Static file serving middleware for SvelteKit static builds.
Multi-step static serving:
- Step 1: Exact path match (handles /, assets, images)
- Step 2:
.htmlfallback for clean URLs (/about→/about.html) - Step 3 (optional): SPA fallback for client-side routes
server/validate_nginx.ts
String-based nginx config validator for fuz_app deploy configs.
Checks consumer
NGINX_CONFIGtemplate strings for required security properties. This is pattern matching on template strings, not a real nginx parser — it catches common security omissions but won't catch all possible misconfigurations.server/x_accel.ts
Fail-loud validation of the X-Accel facts nginx location.
The
X-Accel-Redirectserving path's confidentiality depends on the facts nginx location beinginternal;— only the authz'd handler's redirect (an internal subrequest) may reach it. A *public* facts location would serve any fact's bytes to anyone who guesses the<shard>/<rest>path, bypassing every cell-visibility check. XAccelConfig makes that assertion structural: a consumer can only obtain the redirect prefix by building an XAccelConfig, which runs this check and throws loudly on a missing or non-internal;location — it cannot silently fall closed.This is a best-effort string check (brace-matched
locationblocks), the TS twin of the Rustfuz_fact_servingnginx.rs— not a real nginx parser, but it catches the security-critical omission. Distinct from validate_nginx_config (server/validate_nginx.ts), which validates the/apiAuthorization-strip + security headers and takes no facts-location path.