Files
canon/README.md
T
cdnutterandClaude Opus 5.5 6d92b46130 Add Crimson theme: red on OLED black
Pure #000 background (OLED pixels off), near-black panels with dark red
borders, and a red accent (#e0263a) that passes 4.5:1 as text on black and
behind white button labels. Badges use a brighter red to stay legible on
their tinted background.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 10:10:15 -07:00

9.1 KiB
Raw Blame History

Canon

A self-hosted page for your ranked favorites: movies, TV shows, albums, songs, video games, theater, and anything else you care to rank in a custom list. One owner, as many lists as you like, each with its own size (Top 5, Top 100, … up to 65,535), with proper posters and cover art pulled from the best free metadata sources.

Your page is titled "Your name's Canon" by default. Set your name under Settings → Profile, or give the site any title you like there.

Visitors just see your lists. There's no sign-up, no feed and no login button. You manage everything from /login.

  • Ranked lists per category: Movies, TV Shows, Albums, Songs, Video Games and Theater. Each list sets its own size, title, description and layout (artwork grid or ranked list).
  • Search-to-add from TMDB, TheTVDB, IGDB, Deezer/MusicBrainz and Wikipedia, with add-manually as a fallback for anything they miss.
  • Custom lists for anything else, like sports teams, drinks or restaurants. You name the list and add entries by hand (with your own photo or logo), or pull a name and picture from Wikipedia.
  • Theater by production: record which staging you mean (Broadway, West End, North American Tour, a revival…) plus venue and cast, and use that production's own poster.
  • Drag to rank, or use the arrow buttons or type a new number. Paginated editing keeps huge lists usable.
  • Art is cached on your server, so pages never hotlink and keep working if a source changes. You can also paste an image URL or upload your own.
  • Export any list as a titles-only .csv or .txt.
  • Descriptions and your reasons: each entry keeps the source's description (TMDB overview, IGDB summary, Wikipedia intro), and you can add a "Why it's #N" note explaining its spot. Both show in the detail view.
  • 12 theme presets plus a custom CSS box. Every color is a CSS variable.
  • Single binary + SQLite, no external database. Around 15 MB, and it runs anywhere Docker does.

Quick start (Docker Compose)

git clone https://git.iamchrisama.com/cdnutter/canon && cd canon
cp .env.example .env        # optional: admin account + API keys
docker compose up -d
docker compose logs canon # shows the one-time setup code if you didn't set an admin in .env

Open http://localhost:4242/setup, enter the setup code and create your account. Then go to Settings → Integrations to add API keys.

To skip the setup page, set CANON_ADMIN_USERNAME and CANON_ADMIN_PASSWORD in .env before the first start. They're only used when no account exists yet.

Opening it from other devices

Canon listens on all network interfaces (0.0.0.0) by default, so phones and other computers on your network can open http://<this computer's IP>:4242. When run outside Docker, the startup log prints the exact addresses. If another device can't connect, the host's firewall is probably blocking the port (for example sudo ufw allow 4242/tcp). To keep Canon private to one machine, set CANON_HOST=127.0.0.1, or publish 127.0.0.1:4242:4242 in docker-compose.yml.

Without Compose

docker build -t canon .
docker run -d --name canon -p 4242:4242 -v canon-data:/data canon

Or from source (Go 1.26+): go run ./cmd/canon stores data in ./data.

Data sources and API keys

Category Source Key? Get one
Movies TMDB Yes (free) themoviedb.org/settings/api. The v3 key or the v4 Read Access Token both work.
TV Shows TheTVDB (default) or TMDB Yes thetvdb.com/api-information. Switch TV to TMDB in Settings to reuse your TMDB key.
Video Games IGDB Yes (free, via Twitch) Register an app at dev.twitch.tv/console and use its Client ID + Secret (guide).
Albums & Songs Deezer (default) or MusicBrainz + Cover Art Archive No Nothing to set up.
Theater Wikipedia No Nothing to set up.
Custom Manual entry, or Wikipedia No Nothing to set up.

Why these for music and theater? Neither needs a key.

  • Music: Deezer's public catalogue API has no key, generous limits (about 50 requests per 5 seconds) and 1000×1000 covers for both tracks and albums. MusicBrainz is the fully open alternative, but it's limited to one request per second and art coverage is patchier. Spotify, Apple Music and Last.fm all need keys or OAuth and have stricter terms.
  • Theater: there's no open theater database with an API (IBDB and Playbill don't offer one). Wikipedia covers nearly every musical, play and opera, usually with poster or logo art. Canon filters search to pages that use the musical, play or opera infobox. Wikipedia describes the work, so you pick the production yourself and can swap in that production's poster.

Keys entered in the UI are stored in the SQLite database on your server. Keys set as environment variables take precedence and show as locked in the UI.

Configuration

Variable Default
CANON_HOST 0.0.0.0 Interface to listen on. 0.0.0.0 makes it reachable from other devices on your network; 127.0.0.1 keeps it on this machine
CANON_PORT 4242 Listen port
CANON_DATA_DIR /data (Docker), ./data Database + cached images
CANON_ADMIN_USERNAME / _PASSWORD Create the owner account on first start
CANON_TMDB_API_KEY
CANON_TVDB_API_KEY / CANON_TVDB_PIN The PIN is only for user-supported keys
CANON_IGDB_CLIENT_ID / _SECRET Twitch app credentials
CANON_TRUST_PROXY false Trust X-Forwarded-* headers (set behind a reverse proxy)
CANON_SECURE_COOKIES false Force Secure cookies (set if served over HTTPS)
CANON_LOG_LEVEL info debug logs every request

Reverse proxy

Canon serves plain HTTP. Put it behind Caddy, Traefik or nginx for HTTPS, and set CANON_TRUST_PROXY=true so logins are rate-limited per real client and cookies are marked secure. Caddy example:

favorites.example.com {
    reverse_proxy canon:4242
}

Theming

Pick a preset under Settings → Appearance: Midnight, Obsidian, Nord, Dracula, Mocha, Rosé Pine, Gruvbox, Forest, Synthwave, Crimson (red on OLED black), Daylight, Paper or Latte. Then add your own CSS if you like. It loads after the theme on every page, so overriding variables is enough for a whole new look:

:root, [data-theme] {
  --bg: #0d0b12;
  --surface: #16131d;
  --accent: #ff5a7a;
  --font-display: "Georgia", serif;
  --radius-art: 14px;
}

See web/static/css/themes.css for every variable.

List sizes

A list can hold 1 to 65,535 entries. The editor recommends 100 or fewer and warns past 1,000. Long lists work (public pages and the editor paginate at 100), but they're hard for visitors to take in.

Exporting a list

Every list can be downloaded as its titles only, in rank order, one per line. There are no ranks, header row or other fields.

  • Export → Titles as CSV (/l/<list>/export?format=csv): a single column. It starts with a UTF-8 BOM so Excel shows accented titles correctly.
  • Export → Titles as text (/l/<list>/export?format=txt): plain lines.

The Export menu appears in the list editor and, when you're signed in, on the list's public page. The links follow the list's visibility: anyone can download a public list, and only you can download a hidden one.

Upgrading from TopDeck

Canon was called TopDeck, and upgrading keeps all your data:

  • The database is renamed from topdeck.db to canon.db automatically on first start.

  • Environment variables changed prefix from TOPDECK_ to CANON_. Rename them in your .env.

  • You'll be signed out once (the session cookie was renamed).

  • Docker Compose now always uses the project name canon. If you ran the old compose file, your data is in the topdeck_topdeck-data volume. Copy it into the new one before starting:

    docker volume create canon_canon-data
    docker run --rm -v topdeck_topdeck-data:/from -v canon_canon-data:/to alpine cp -a /from/. /to/
    

Backups

Everything lives in the data volume: canon.db (SQLite) and images/. Copy the volume, or while running:

docker compose exec canon sh -c 'cd /data && tar c canon.db* images' > canon-backup.tar

Development

go run ./cmd/canon        # http://localhost:4242
go test ./...

Templates and static assets in web/ are embedded into the binary, so rebuild after editing them. The front end is plain ES modules with no build step.

cmd/canon         entry point, first-run bootstrap
internal/store      SQLite schema, lists, items, ranking, settings, sessions
internal/providers  TMDB, TheTVDB, IGDB, Deezer, MusicBrainz, Wikipedia
internal/imgcache   downloads and stores artwork
internal/server     HTTP routes, auth, JSON API, page rendering
web/                templates, CSS themes, JS, icons