Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Server

The package server can be started with filepack serve. A demo server is available at filepack.com.

The server has a web frontend that can be used to browse packages, as well as an API for uploading and downloading packages.

See API and Web for documentation of the API and web routes.

The server responds to requests for /{fingerprint}, /{number}, and /{revision} which redirect to /package/{package}, and can be used as convenient shortcuts.

Authorization

By default, the Filepack server does not require authorization. Anyone can upload, download, and browse packages. Running filepack serve without any flags listens on port 80 on all interfaces, not just localhost.

Writes can be restricted with --restrict-writes, which forbids unauthorized write access to the API, including the ability to upload and delete packages.

The --admin-key option, along with --domain, can be used to authorize write access by public key. The holder of the admin private key may authorize requests by including a tagged-hex bearer token with their requests in the Authorization header. The token’s audience must match the value of --domain, and the timestamp must be current.

If --restrict-writes is used with no --admin-key, the server will be read-only.

Note that authorization tokens are not scoped to a particular endpoint or request, only by domain and time. They can be used for any purpose while valid. As such, filepack commands with --auth will refuse to run unless the connection to the server is over loopback or HTTPS.

HTTPS

The Filepack server can request a TLS certificate from Let’s Encrypt using the ACME protocol.

To serve HTTPS on port 443, request a TLS certificate for example.com, and redirect requests on port 80 to port 443:

filepack serve --domain example.com --https --redirect-http-to-https

ACME TLS certificates are stored in acme-cache/ in the Data Directory.

Responses served over HTTPS include Strict-Transport-Security: max-age=31536000. If you serve a domain over HTTPS and then later switch to HTTP, browsers that have previously visited using HTTPS will refuse to connect.

Storage

Server state, including the database, ACME cache, uploaded files, and incomplete files, as well as the keychain containing admin keys, is stored in the Data Directory.

The database is stored in database.redb and uses redb.

ACME TLS certificates are cached in acme-cache/.

Uploaded files are stored in files/{hash}, where hash is the tagged-hex BLAKE3 hash of the file contents.

Uploads are written to temporary files in incoming/ and atomically moved to files/{hash} upon completion.

There is no disk quota, so a writable server can be filled by anyone with access.

API

API routes begin with /api. See API for details.

Typed request and response bodies are encoded with deco.

The optional Filepack-API-Version header may be included with requests. If included, the value should be the version of the Filepack API that the client supports. If omitted, requests are treated as having Filepack-API-Version: 0. Requests with an unsupported API version return 400.

The only supported API version is 0.

Caching

All routes that return hashed files include an ETag header whose value is the tagged-hex hash of the file as a strong entity tag. These routes also respect the If-None-Match header for GET and HEAD requests, including range requests, and will return a 304 Not Modified with no body if the If-None-Match header entity tag matches the response ETag header.

Immutable routes return Cache-Control: public, max-age=31536000, immutable with successful responses. Immutable routes are those whose path contains a hash which commits to the response. Note that web routes, such as /package/{fingerprint}, are not immutable, since the HTML served may change, for example if filepack is updated.

Package Numbers and Revisions

When a client uploads a package to the server, it creates and uploads a revision object, similar to a Git commit.

Revision objects have a package field containing a package fingerprint, and a parents field containing a set of parent revision hashes.

Uploaded packages are assigned sequential package numbers starting from zero. Deleted package numbers are not reused.

A package revision with a number is a head.

Newly uploaded packages are assigned numbers and are thus heads. When a new revision of the same package is published, the package number is updated to point at the new revision, whose parents field contains the old head revision.

Server URLs

Commands like filepack upload which take server URLs also accept IP addresses and domains without a scheme. IPs and localhost use http://; all other domains use https://.

Uploading Packages

Packages can be uploaded to the server with filepack upload.

filepack upload actually performs three distinct operations: creation, replacement, and update.

After successfully creating, replacing, or updating a package on the server, the new package head for that server is recorded to .filepack/state.json in the package root.

Package Creation

Package creation assigns a new package number to an uploaded package and is the default when running filepack upload for the first time in a given directory to a particular server.

Running filepack upload from the same directory without modifying the package is idempotent and will return the same package number.

After modifying the package, running filepack upload from the same directory will update the package.

Package Replacement

Package replacement makes an existing package number point at a new package with no parents, making the old package and its parents collectable by filepack gc, unless they are reachable from another head.

A previously uploaded package with NUMBER can be replaced with:

filepack upload --replace <NUMBER>

Package Update

A package number may be updated to point at a new revision whose parent is the current head for that number with:

filepack upload --update <NUMBER>

Or just filepack upload, if .filepack/state.json has an entry for the current server.

If the current package head differs from the recorded head in .filepack/state.json, for example if someone else updates the package, filepack upload will abort. If two clients race to update the same package, the server will accept the first and reject the second with 409 Conflict.

Upload API

Package uploads are content-addressed and bottom up.

Files, directories, and revisions are uploaded using PUT /api/file/{hash}.

Directories are verified using POST /api/directory/{hash}, which requires that all files have been uploaded and all subdirectories have been verified.

The package is verified using POST /api/package/{fingerprint}, which requires that the package root directory has already been verified and package metadata is valid.

Finally, the revision is verified and the package number obtained using POST /api/revision/{revision}, which requires that the package has been verified and the revision has been uploaded and is valid.

Downloading Packages

Packages and files may be downloaded from the server with filepack download.

filepack download verifies the hash of all files it downloads, so servers do not need to be trusted, as long as the client has a known-good package fingerprint or file hash.

Deleting Packages

Packages may be deleted from the server with filepack delete.

Garbage Collection

Deleting a package causes the package number to no longer point to a package, and replacing a package causes the number to point at a different package. These packages are still served by fingerprint and revision, and their files are not immediately removed from the server.

After a package is deleted or replaced, running filepack gc will remove all packages and package files which are not reachable from the current heads or mounted packages.

Previous versions of updated packages are still reachable from the parent revisions of the new head revisions, and will continue to be served by fingerprint and revision.

If a file uploaded with filepack upload --file is not reachable from any package head, it will be deleted by the next run of filepack gc.

Web Packages

Arbitrary web content can be hosted on a Filepack server using mounted web packages.

Web packages contain web content with a static/index.html entry point. They are currently unstable and will be rejected for upload unless the server is started with unstable features enabled.

By default, web packages are not mounted. Uploaded web packages can be viewed and browsed at /package/{package}, but all active content, including HTML and JavaScript, is served with Content-Security-Policy: sandbox, a non-active Content-Type such as text/plain;charset=utf-8 or application/octet-stream, and X-Content-Type-Options: nosniff.

Web packages can be mounted with:

filepack serve --mount PACKAGE

When mounted, /mount/{package}/, where package is a mounted package fingerprint, revision, or number, returns static/index.html of the mounted package, and /mount/{package}/{path} returns the package file at static/{path}.

Mounted files are served unsandboxed from the server origin with unrestricted content types, including text/html and text/javascript, guessed from file extensions. Furthermore, packages mounted by number serve the latest version of the package. On a world-writable server, anyone may update the package, causing arbitrary HTML and JavaScript to be served from the server origin. Make sure you understand the consequences of this and only mount packages you trust, and only mount packages by number on servers with --restrict-writes.

Mounts may optionally be given a name with --mount PACKAGE:NAME, in which case they are also served under /{NAME}/. Built-in routes take precedence over mounts.

This book is served as a web package, mounted at filepack.com/book/.