# Six Sources, One Pipeline

A reader's books are rarely in one place. Some sit in a folder on a laptop. Some sit on a home server reached over WebDAV. Some sit in a bucket on S3-compatible storage. One arrives as a link a friend sent. Another is a file dragged into a browser window. A reader that only accepts uploads asks the reader to reorganize a library before reading a single page. So the shelf has to meet books where they already are — and then do something stricter: once a book is in, the shelf has to stop caring where it came from.

## Where a reader's books already are

The spread is ordinary. People keep books on the machine in front of them, on a drive shared over the local network, in cloud storage they already pay for, and in links passed around between friends. Each of those places has its own way of listing files and handing one over. A folder on disk is listed by the operating system. A WebDAV share answers directory listings over HTTP. An S3-compatible bucket answers paginated object listings. A link is just bytes at the end of an HTTP GET. If the shelf talked to each of these in its own dialect all the way through, every feature built on top — opening, parsing, caching, notes, translation — would have to learn each dialect too.

## Six doors into the same shelf

The shelf takes books in six ways, and each way ends in the same place: a file the server holds, with a row in its book table.

- **Server directory.** Files already on the server's disk are scanned into the shelf. The shelf reads them from its own filesystem.
- **Direct upload.** A file picked in the browser is sent up and stored on the server.
- **URL import.** The server downloads a link into the upload directory, and from there the file is handled like any other upload.
- **Local folder.** The browser scans a folder on the reader's machine with the File System Access API and uploads the files. The server never scans the reader's disk.
- **WebDAV.** The server lists the remote folder and, when a book is opened, downloads that one file.
- **S3-compatible storage.** The server pages through the bucket's object listings and, when a book is opened, downloads that one file.

Six entrances, but note what they share: by the time a book can be read, the server holds the bytes. Nothing downstream ever opens a file still sitting on a NAS or in a bucket.

## The local folder: the browser scans, the server receives

The local folder is the case that most invites a shortcut. The folder is right there, on the reader's machine; reaching into it from the server sounds like the direct thing to do. It is also the one thing the server never does. The provider for local folders returns an empty list from its server-side scan, because the scan happens in the browser: the browser walks the folder, and the results come back to the server as an upload. When the book is later opened, the provider hands back bytes from the copy already stored on the server.

This is the same rule as the remote sources, applied to the case where it looks least necessary. The server holds no live handle to the reader's disk. If the uploaded copy is cleaned up — local-folder files live in temporary storage — opening the book fails and asks for a re-upload. That failure states the rule plainly: the shelf stores files, and when the stored copy is gone, the book cannot be opened.

## Every remote source answers the same three questions

WebDAV and S3-compatible storage look nothing alike on the wire. WebDAV walks a directory tree, handling servers that answer listings differently. S3 pages through object listings, skipping directory markers and filtering to ebook extensions. But both implement the same three operations: list the files, download one file as bytes, test the connection. Both return listings in the same shape — path, name, size, extension, modification time. A registry maps a source type string to the provider that implements those operations, and callers ask the registry for a provider by name. An unregistered type raises an error.

Adding a protocol means writing one provider that answers those three questions and adding one registration line where providers are registered at startup. Nothing else in the system learns the new protocol's name.

## Opening a book forgets where it came from

The convergence point is a single function that turns a book id into bytes. For a book with a local file, it reads the file from disk. For a book in network storage, it looks up the stored source configuration, asks the registry for the right provider, and calls that provider's download. Either way, what comes out is the same pair: bytes and a filename.

The reader is then built from that result. A local book hands the reader a file path, so it can read on demand. A network book hands the reader the downloaded bytes. After that point, parsing, caching, notes, and translation all work on the reader. None of them take a source type as a parameter. A book that arrived over WebDAV and a book that was dragged into the browser go through the same chapter splitting, the same cache keys, the same annotation storage, because by then both are just a book the server holds.

## The cost of leaving books where they are

Reading in place sounds cheaper: skip the transfer, open the file where it lives. The price shows up everywhere else. A parser that reads from a remote share needs retry and partial-read handling that a local-file parser never thinks about. A cache keyed on remote paths needs invalidation when the remote listing changes. An annotation anchored to a remote file needs to survive the file moving. Each downstream feature would carry a branch for each source, and each new source would multiply that work across every feature.

Downloading first pays one transfer and buys one code path. The transfer is the only part that knows about WebDAV or S3 or a browser upload. Everything after it knows about books. That is why the shelf has six entrances and one interior: the number of sources can grow by adding a provider, and the rest of the system stays the same size.
