Code-path breakdowns 2026.08
Twelve traces.
Real files.
The dossier draws the boundaries. This page walks the code. Every node in
every diagram names the file that owns that step, and every claim links to
the source on
main. Where the implementation disagrees with a decision
record, the implementation wins and the disagreement is recorded rather than
smoothed over.
Breakdown 01
Writes re-establish trust at their entry point.
Nooklet exposes three HTTP route files. Most mutations travel
through sixteen
"use server" files across src: fifteen
under src/app, plus the layout sign-out action. The
built-in queue's authenticated POST handler is the
deliberate HTTP exception. Each protected entry point authenticates,
validates its input, and delegates to a focused module operation.
TRACE 01 · SERVER ACTION WRITE PATH
From form submission to durable row
- Primary path
- Short-circuit
- Return edge
A form submission reaches src/proxy.ts, which redirects unauthenticated requests but performs no authorization. The server action then calls auth() and checks the role, parses the FormData with a Zod schema, and consumes a rate limit. Each of those three steps can short-circuit to a typed error state without writing anything. On success the action calls a module workflow, which calls the repository that owns this transaction through Drizzle. The action finishes with revalidatePath, and the server re-renders the affected route.
POST authenticates and delegates its validated control
request to a workflow.
Breakdown 02
Server-rendered reads do not make an HTTP hop.
A server component imports a module query as an ordinary function and calls it during render. There is no fetch, no serialization boundary, and no endpoint in between. The three route files that do exist serve authentication callbacks, the container health probe, and the caller-scoped queue API used for polling and controls.
TRACE 02 · SERVER COMPONENT READ PATH
Render-time query, no endpoint
A server component marked force-dynamic awaits auth(), then calls a module query as an ordinary imported function. The query calls ensureDatabaseReady and runs a Drizzle select in the same process, and the component streams HTML back. Alongside the spine, an aside notes the only three route files in the application: the Auth.js catch-all, the health probe, and the authenticated built-in queue API. Server-rendered page data stays on the direct query path; the queue is the explicit client-polled exception.
ensureDatabaseReady()
directly rather than going through a repository. That is an accepted
variance from the documented layering, not an accident: reads may
shape a result for one screen without a repository round trip, while
most writes use repository-owned transaction seams.
Breakdown 03
A module publishes three folders and hides four.
commands/, queries/, and
workflows/ are the public surface. Repositories,
adapters, schemas, and types are internal, reached only through that
surface. Folders are created when real code lands, so most modules
carry a subset rather than the full template.
TRACE 03 · MODULE ANATOMY
The import allowlist, and where it bends
- Allowed import
- Forbidden import
A caller in src/app imports from a module's public surface: commands, queries, or workflows. Inside the module boundary sit four internal folders that callers must not import directly: repositories, adapters, schemas, and types. A forbidden-import edge is drawn piercing the module boundary straight into repositories. Below the boundary, target modules expose explicit public.ts facades and a repository validator rejects production cross-module imports into repository or adapter internals.
npm run boundaries:check makes that rule executable;
server actions still enqueue durable work through the jobs module's
public API instead of doing filesystem work in the request process.
Breakdown 04
A workflow orchestrator only names the order.
The canonical shape is a folder of phase files behind a thin
index.ts that awaits each one in sequence and branches
nowhere. Indexer search is the cleanest example in the repository:
eight phases, thirty-three lines, no conditionals.
TRACE 04 · WORKFLOW PHASE PATTERN
searchIndexersWorkflow, phase by phase
The searchIndexersWorkflow orchestrator awaits eight phases in order: request-validation, indexer-selection, credential-resolution, indexer-execution, normalization, filtering-scoring, persistence, and audit. Beside it, three examples of how consistently the pattern is applied across the repository: search-indexers with eight phase files behind a thirty-three line index, import-completed-downloads with thirteen phase/helper files in the same shape, and season-fulfillment which is a single file with no phase folder at all.
index.test.ts mocks each phase and asserts only
ordering and propagation. Where a workflow has not been split, that
test cannot exist in the same form.
Breakdown 05
One application container, one supervisor, two long-lived children.
A wedged bind mount blocks in the kernel and cannot be cancelled from JavaScript. Enough blocked calls exhaust the process-wide filesystem thread pool and page rendering stops. The answer is process isolation: filesystem work never runs in the web runtime. The YouTube proof-of-origin provider is a separate internal Compose sidecar and is outside this application-container supervision tree.
TRACE 05 · SUPERVISION TREE
Startup order and exit policy
- Spawns
- Fatal exit
- Recoverable exit
tini is PID 1 and starts container-supervisor.mjs, which forces NODE_ENV to production and then runs the worker bundle once with --migrate-only, awaiting its exit so migrations complete before any long-lived process opens SQLite. It then spawns two long-lived children: the Next.js web server and the background worker. Separate storage-probe children are disposable; each performs one bounded refresh and exits. If the web process exits, the supervisor shuts the whole container down. If the worker exits or its persisted heartbeat stops advancing, it is restarted with exponential backoff starting at one second and capped at thirty seconds. Normal termination first asks the worker to drain its active pass.
SIGTERM drains
healthy in-flight work before the ten-second kill ceiling.
Breakdown 06
Filesystem lanes run alone; network lanes run together.
Every fifteen seconds the worker runs one pass. The three lanes that touch the filesystem execute serially and first, then maintenance runs by itself, and only then do the four network and AI lanes run concurrently.
TRACE 06 · WORKER PASS ORDER
One tick, seven job lanes plus maintenance
A fifteen second timer starts one worker pass. Three filesystem lanes run serially in order: download-import, then media-title-delete, then media-library-scan. Maintenance then runs alone with no other lane overlapping it. Finally four network and AI lanes run concurrently through Promise.allSettled: watch-history-sync, recommendation-run, missing-content-search, and metadata-refresh.
TRACE 06B · CLAIM AND LEASE
Ownership is proved by the write, not a lock
Claiming a job selects due candidates whose lease is free, then issues a conditional UPDATE that re-asserts the same predicate in its WHERE clause. If that update reports more than zero changed rows, this worker owns the job. A heartbeat every thirty seconds then extends the five minute lease for as long as the job runs.
UPDATE matches the predicate, so only one sees a
non-zero change count. A crashed worker needs no cleanup: its lease
simply expires and the row becomes claimable again.
Breakdown 07
The download engine drains one release at a time.
Admission happens in the web process against a persisted capacity snapshot. Everything after that runs in the worker: a single drain loop claims one queued row, fetches every segment over a pool of NNTP connections, and writes decoded payloads straight into the assembling file at their byte offsets.
TRACE 07 · ENGINE RUNNER LOOP
Claim, transfer, and classify the outcome
- Primary path
- Defer and retry
- Loop
The engine runner peeks the next queued row by priority, then checks live capacity with a real statfs for that single release; if there is not enough room it marks the row waiting and defers it so smaller releases can still run. It claims the row by compare and swap into the fetching state, losing the race means deferring to the next row. It clears the work directory for a fresh start, transfers every segment over pooled NNTP connections writing each decoded yEnc part at its byte offset, classifies the outcome with transport failures checked before content failures, and finalizes. The loop then repeats until no candidate remains.
paused rather than failed, because a terminal state
discards the stored NZB and only a paused row can resume.
TRACE 07B · FINALIZATION
Eight steps from raw payload to importable media
Finalization runs eight steps in order: deobfuscate files by magic bytes, verify and repair with par2, inspect archives against an extraction quota before extracting anything, extract into an isolated staging directory, verify and merge the staged tree with path traversal guards, rescue unnamed media, remove par2 artifacts, and finally move the work directory to the output directory in one sequential copy.
Breakdown 08
Seven states are declared; all are reachable.
The engineDownloadStates enum now contains only states
written by the runner, and every transition is a compare-and-swap
that names the states it expects to find.
TRACE 08 · ENGINE STATE MACHINE
Transitions that actually exist
- Transition
- Failure
- Resumable
A row starts queued and moves to fetching. From fetching it branches to either repairing or extracting, and both converge on completed. Fetching can also move to paused, either by user intent or because the transfer was parked, and paused returns to queued. Failure from an active state moves to failed. Assembly itself happens while the row is fetching.
assembling value
has been removed because yEnc parts are assembled in place during
fetching. paused still carries two
distinct meanings: a user pressed pause, or the engine parked a
transfer whose server broke. Both are resumable, which is exactly
why parking is not modelled as a failure.
Breakdown 09
Fifty tables in one SQLite file.
One schema file declares every table. Most entity rows use text identifiers and millisecond timestamps; relationship tables use composite or parent keys, and foreign keys declare explicit delete behavior. Service credentials live in dedicated side tables instead of parent rows.
TRACE 09 · SCHEMA BY DOMAIN
Which module owns which tables
Fifty tables grouped by owning domain: five for identity and
access, nine for the media library, seven for downloads, six for
indexers, six for recommendations, three for watch history,
eight platform tables covering stable instance ownership,
service connections, storage snapshots, jobs and notifications,
one for the download engine, and five for YouTube sources,
selections, videos, membership, and downloads. Auth.js keeps
encrypted JWTs in browser cookies while each token must also
match an unexpired auth_sessions row and the user's
current monotonic authentication generation.
jwt callback requires the
live user, matching authentication generation, and a per-login
auth_sessions row. Sign-out deletes that row before
clearing the cookie, so a late response cannot make an old token
valid again. Disabling an account or writing a password advances the
generation and removes active rows in the same transaction,
including logins still pending issuance. Tokens missing any validity
claim fail closed and require a fresh sign-in. The
engine_downloads row also carries the full NZB payload,
which is what lets a restart re-run a transfer without returning to
the indexer.
Owning source
Breakdown 10
The web process never calls statfs.
A hung mount blocks in the kernel and takes the filesystem thread pool with it. So the request path reads a persisted snapshot instead, refreshed on a schedule by a disposable child process that can be killed at a deadline.
TRACE 10 · CAPACITY POLICY
Who is allowed to touch a bind mount
- Request path
- Snapshot read
In the web process, enqueuing an NZB calls readDownloadCapacity, which reads two rows from the storage_snapshots table rather than probing the filesystem, and refuses the request if the snapshot is older than two minutes. Inside the worker boundary, a disposable probe child runs the only scheduled statfs against both storage roots and writes the result back to storage_snapshots, which the web process then reads. Separately the engine runner performs a real live statfs immediately before claiming a row, asking only whether this one release can run now, so an oversized release is deferred rather than blocking every smaller one.
Breakdown 11
Four boundaries, each enforced at the point of use.
Stored credentials, outbound requests, filesystem paths, and abuse
controls each have a dedicated module in
src/lib/security/. None of them are middleware; every
one is called by the code that is about to do the dangerous thing.
TRACE 11 · SECURITY BOUNDARIES
Where each guard sits
Four security boundaries. Stored credentials derive a key with HKDF-SHA256 from the secret box key, falling back to AUTH_SECRET, and seal values in a versioned AES-256-GCM envelope that supports key rotation. Outbound requests resolve the hostname first and classify the resolved address, then pin those addresses into the dispatcher so the socket cannot re-resolve. Filesystem paths are canonicalized with realpath and compared against approved roots, with an isolated variant that canonicalizes in a child process so a wedged mount times out. Abuse controls use fixed-window counters in the rate_limits table, and forwarding headers are trusted only when explicitly enabled.
Breakdown 12
Every repository opens the database the same way.
There is no exported connection singleton. Each caller invokes
ensureDatabaseReady(), which is cheap after the first
call and carries the migration gate, the pragma set, and a
compatibility assertion with it.
TRACE 12 · DATABASE BOOTSTRAP
From module import to a usable connection
The env module parses its schema eagerly at import, which is why the Dockerfile injects a throwaway AUTH_SECRET at build time. Every repository calls ensureDatabaseReady, which probes cached state stored on globalThis so it survives development hot reload, opens the connection and applies four pragmas including WAL journaling and foreign keys, compares the raw contents of drizzle/meta/_journal.json against the cached signature and runs migrations only when they differ, then applies compatibility repairs for drifted columns and asserts the schema before returning.
Appendix
How the reported drift was resolved.
Each previously reported mismatch was reviewed against the implementation. The rows remain as a decision log: one dead state was corrected in code, while intentional constraints and ownership choices were made explicit in the ADRs.
| Reviewed item | Disposition | Resolution |
|---|---|---|
credential-vault as a physical module |
Accepted architecture |
ADR-0001 now defines it as a responsibility: shared
crypto lives in src/lib/security/, while
each domain owns its secret records and policy.
|
metadata as a physical module |
Accepted architecture |
ADR-0001 now records metadata as a capability exposed by
service-connections, not a placeholder
directory.
|
Durable engine_segments rows |
Accepted constraint | ADR-0002 now specifies in-memory segment scheduling, aggregate persisted progress, and safe whole-transfer restart from the stored NZB. |
assembling engine state |
Rectified in code | The dead enum value and dependent branches were removed; an upgrade migration normalizes any historical row before the narrowed state model is used. |
| Repositories and adapters are module-internal | Not a drift |
ADR-0001 and the structure guide scope this to
production cross-module imports;
boundaries:check enforces exactly that
invariant.
|
| Database access outside repositories | Accepted architecture | ADR-0001 now permits query-owned read projections and focused workflow/configuration/maintenance transactions while keeping reusable persistence and ordinary writes in repositories. |
| Multiple prioritized Usenet servers | Accepted constraint | ADR-0002 now fixes the current topology at one canonical instance-wide server; primary/block-account failover remains a possible future decision. |