Skip to content

Getting started

What follows installs the service, points it at a database and a bucket, and publishes a file. It takes about five minutes against services you already have, or against the local ones in Running it.

Install

Nothing is published to PyPI yet, so it installs from a checkout:

git clone https://github.com/abilian/cdn
cd cdn
uv sync

Python 3.12 or later. The service needs PostgreSQL and something that speaks S3; the client half of the CLI needs neither.

Configure

Everything is read from the environment once, at startup, and validated there. A missing value stops the boot rather than failing on the first request that needs it, so a health check never reports green in front of a service that cannot serve.

export DATABASE_URL=postgresql://localhost/cdn
export CDN_BASE_URL=https://cdn.example.com    # the public address, as people will see it
export CDN_SECRET_KEY=$(openssl rand -hex 32)  # signs session cookies and derives S3 secrets
export CDN_CACHE_DIR=/var/lib/cdn/cache        # a persistent, disposable volume

export S3_ENDPOINT_URL=https://s3.example.com
export S3_BUCKET=cdn
export S3_ACCESS_KEY_ID=…
export S3_SECRET_ACCESS_KEY=…

CDN_SECRET_KEY signs session cookies and derives the secret every S3 client authenticates with. Changing it signs everyone out and invalidates every S3 credential at once. The rest of the settings and their defaults are in Configuration.

Create the schema

cdn migrate

Run this before the new code serves traffic, on every deploy. Schema changes go through migrations and never through create_all, so a process that started against an empty database has the same schema as one that has been upgraded for a year.

Provision it

An organisation is a tenant. A zone is a unit of URL and configuration. An owner is the first person who can sign in and invite the rest. One command makes all three:

cdn provision --org acme --org-name "Acme Corp" --zone assets --email you@example.com

It prints the password it generated, once. It is also idempotent: safe to run on every deploy, and it never rewrites an account that already exists. That is what a platform's post-deploy hook needs. See Running it.

The create commands below do the same jobs one at a time, and are deliberately not idempotent: a create that does nothing when the thing exists hides a typo in a slug until someone wonders why their uploads went somewhere else.

cdn create org acme "Acme Corp"
cdn create zone assets --org acme
cdn create user you@example.com "Your Name" --org acme --role owner

Mint a token

cdn create token "laptop" --org acme --scopes read,write,delete

The token is printed once and stored only as a hash. There is no way to read it back.

Publish a file

Point the CLI at the service and hand it the token:

cdn login https://cdn.example.com cdn_a1b2c3d4e5f6_…
cdn put assets ./dist/app-1.2.0.tar.gz releases/

It prints the URL, which is also what cdn url prints:

https://cdn.example.com/assets/releases/app-1.2.0.tar.gz

Fetch it, and ask twice:

curl -I https://cdn.example.com/assets/releases/app-1.2.0.tar.gz
# HTTP/1.1 200 OK
# X-Cache: MISS
curl -I https://cdn.example.com/assets/releases/app-1.2.0.tar.gz
# X-Cache: HIT

The first request fetched the object from S3 and left a copy on the cache volume. The second was answered from disk.

Sign in

The browser UI is at /_/ui, and the owner cdn provision made is who you sign in as. After that, owners invite everyone else from the Members page. The command line is only needed for the first account, because there is nobody to send the invitation.

Where to go next