back home

changelog · 32 updates

what's changed.

Every update to this site, newest first, straight from the commit history.

November 2026 · 5

  1. Register every slash command globally

    Commands without `global: true`, like /media, were only registered in DISCORD_GUILD_ID, so people in other servers and in DMs couldn't see them. Register everything globally and clear the old guild-only list so the home server doesn't show each command twice.

    Limit /anime-request-update to servers, since Discord doesn't apply member permissions in DMs.

    05d8acf
  2. Edit config.ts

    0266efa
  3. Rebuild better-sqlite3 in update.sh when Node changes

    npm install leaves native modules alone when Node itself is upgraded or swapped, which left better-sqlite3 linked against a missing libnode.so.115 and broke the /anime.db export. update.sh now records the Node binary and version in node_modules/.node-runtime and rebuilds better-sqlite3 whenever they differ.

    fc2d134
  4. Add quotes and list/cancel for scheduled messages

    Right-click a message → Apps → Save quote stores its text in data/quotes.json; /quote random shows one (filterable by text or person) and /quote remove takes one down. The bot now loads message context menus alongside slash commands.

    /schedule-message is split into create, list and cancel, and the staff check moves to utils/staff.ts so both commands share it.

    9f3d5ff
  5. Add a staff-only /schedule-message command

    Staff (Manage Messages, or STAFF_ROLE_ID) give it a unix time and a message, and the bot posts it then. Queued messages live in data/scheduled-messages.json so they survive restarts.

    2e521b0

October 2026 · 27

  1. Redesign the site around a plum, pink and blossom look

    Rework the layout and styles of every page while keeping all content and features. The home page gets a dial hero, an analog clock card, a featured project, a bigger VRChat card, a new quote and a sunset contact section. Subpages share one frame: a full-width header bar, consistent section headings and spacing, and tidier layouts for uses, changelog, colophon, stats, anime requests and the error pages.

    Also scope the home page section padding so nested sections on subpages no longer get huge gaps, and show petals and the side rail on the home page only.

    a37d9ed
  2. Track UTM-tagged visits and show them on /utm-data

    Page visits with utm_* tags are saved to data/utm.json (tags, page, referrer hostname, time; no IPs). Bots and 404s are skipped and only the latest 5,000 are kept.

    d49ff30
  3. feat(reaction-roles): add configurable Discord reaction roles

    91a7ab2
  4. Keep the cat's sprite sheet out of Git LFS

    It was the repo's only LFS file. A server without git-lfs would pull a pointer instead of the image and the /stats cat wouldn't draw. At 2 KB it's fine as a plain file.

    c01c5c4
  5. Show the /stats cat in /status

    /status now attaches a still of the cat in its current mood, with this moment's LEDs and the down container's name on the mug, and ends with the same caption as /stats. The image is drawn on the server from the same grids by services/cat-render.ts; the scene setup moved from the build script into content/cat/parts.ts so both share it.

    e61a787
  6. Add a pixel-art cat to /stats that shows how the homelab is doing

    A grey tabby with a notched ear sleeps on a little server rack with one LED per Docker container. Its mood follows the homelab: content, grumpy (CPU over 85% for 2 minutes), hot, guilty (a container is down, so it knocks a mug labelled with the container's name off the shelf), asleep (at night or when idle) and happy (after an outage). A new mood has to hold for 30 s before the cat switches. The thresholds are CAT_* env vars.

    The art is hand-drawn text grids in src/content/cat, built into one sprite sheet plus frame timings by `pnpm build:cat`. getSiteStats() gains a homelab block (CPU %, temperature, containers via DOCKER_HOST, with an opt-in read-only socket proxy in docker-compose.yml), so the cat rides on the existing /api/stats poll. /stats/cat-preview shows every state, transition and color.

    234b650
  7. Add a /discord-status page

    Explains what each Discord status means, whether to message, and a "no hello" section based on nohello.net.

    efe7dda
  8. Add /avatar and a starboard

    Messages with STARBOARD_THRESHOLD stars (default 3, not counting the author or bots) are reposted to STARBOARD_CHANNEL_ID, with the count kept up to date. Needs the GuildMessageReactions intent and Reaction partials.

    e210397
  9. Add /serverinfo, /userinfo and /status

    /status reuses the status channel site check, plus the public site's /healthz through the tunnel and the bot's gateway ping.

    0a7daab
  10. Add +reload and +announce owner commands

    +reload re-registers the slash commands from Discord; it shares the registration code with deploy-commands, now in services/command-registry. +announce posts an embed to a channel with an optional role, @everyone or @here ping and an attached image.

    ab2f874
  11. Add a +dump command that replies with a JSON dump of the server

    Bot-owner only. Covers the server's settings, roles (position, colour, member count, permissions), channels in sidebar order with their permission overwrites, active threads, emojis and stickers, attached as {server_name}-dump.json.

    758c44e
  12. Add a /suggestion command that DMs the server owner

    Opens a modal for a title and suggestion, then sends it to the guild owner as an embed. Five-minute cooldown per user.

    99ae1f3
  13. Drop the Patreon link and bump the /now date

    9b53641
  14. Add guestbook, stats, changelog, colophon and sitemap pages

    /guestbook: visitors leave a name, message and optional website. Notes are saved to data/guestbook.json as pending and only shown once approved on the unlisted /guestbook/review page (same ANIME_ACCEPT_KEY as /anime-accept). Protected by Turnstile, a honeypot and a 5-per-hour rate limit; GUESTBOOK_DISCORD_WEBHOOK_URL optionally pings Discord. The review-page sign-in helpers move to utils/admin-auth.ts, shared with /anime-accept (existing sessions stay valid).

    /stats: live server, Discord bot, Jellyfin and site numbers, refreshed every 30s from /api/stats. /changelog: site history from the git log, grouped by month with trailers stripped. /colophon: how the site is built and served. /sitemap: every page, from the palette data.

    The pages are added to the palette ("this site" group), nav menu, footer, sitemap.xml and robots.txt. The privacy policy, cookie policy and terms cover the guestbook.

    b78554e
  15. Rebuild the Ctrl+K command palette

    The palette's contents move to content/palette.ts (projects and links come from the existing site content) and render client-side from JSON.

    Search is fuzzy with highlighted matches and also matches keywords, group names and hints, ranked best-first. With no query it shows grouped sections plus the last few pages opened from it. Items show an icon, a one-line hint, "you are here" and external-link badges.

    Adds actions (switch theme, copy email, copy page link, open the support dialog, back to top) with toast feedback, Tab/PageUp/PageDown and Ctrl/Cmd+Enter for a new tab, Esc clearing the query before closing, Ctrl+K toggling, and scroll lock while open. The nav button becomes a search pill showing the shortcut (Cmd on Mac); the palette goes full-screen on phones.

    2ebc74f
  16. Make the site Cloudflare Tunnel aware and harden anime requests

    Trust cloudflared for the visitor IP and https scheme, so canonical and Open Graph URLs are https and rate limits see real IPs. Canonical URLs now always use SITE_URL.

    Caching: pages add stale-while-revalidate and stale-if-error so Cloudflare can keep serving them during an outage, and a Cache-Control a route already set (like no-store on a 503) is no longer overwritten with the 1-hour default. CSS/JS links carry a content hash and are served as immutable.

    Anime requests get optional Cloudflare Turnstile and per-IP rate limits (searches too). Also adds security headers, /healthz, /.well-known/security.txt, optional Cloudflare Web Analytics, graceful SIGTERM shutdown, palette arrow-key navigation, a skip link and home page JSON-LD.

    Adds a README with deploy notes and recommended Cloudflare settings, .env.example, a cloudflared config example and a systemd unit. Compose drops the unused PASSWD, binds to localhost, persists data/ and can run cloudflared under the "tunnel" profile. Policies mention Cloudflare.

    c818c25
  17. Add batch updates to the /anime-accept review page

    Tick several requests, pick one status and an optional note, and update them in a single write. A blank batch note keeps each request's existing note. Update embeds are grouped up to 10 per webhook message.

    481ecf1
  18. Add owner-only +send command that DMs media server info

    +send <user id or mention> sends the target an info embed with the Jellyfin server address, the request/downloads site and the setup guide. Only the bot application's owner (or team members) can use it.

    5961a86
  19. Only use npm ci in update.sh when package-lock.json is committed

    A leftover untracked lockfile from an earlier npm install goes stale once package.json changes, and npm ci then refuses to install.

    7d8c070
  20. Add /anime.csv, /anime.txt and /anime.db library exports

    The Jellyfin anime library is written to data/ as CSV, plain text and SQLite on startup and every 12 hours, and /anime.{ext} serves those files. Adds better-sqlite3 (v12, which still supports Node 20).

    fb7ff89
  21. Make status channel Slow detection less noisy

    Take three samples per check and judge on the median so a cold connection or one dropped request doesn't decide the status, time the site's own response instead of following redirects, and require two slow checks in a row before leaving Online. Sites can set their own slowMs threshold.

    41cc6d4
  22. Add Discord status voice channels for self-hosted sites

    Keeps a locked voice channel per site in the STATUS_CAT category, renamed to Online/Slow/Down as checks change every 5 minutes.

    76ee0b1
  23. add anidl project

    cac2021
  24. Use npm instead of pnpm in update.sh

    3848045
  25. Make update.sh pull over SSH and build before restarting

    - Switch the remote to the SSH URL if it isn't already - Run git and the build as the repo owner so their SSH key is used - cd to the script's directory so it works from anywhere (e.g. cron) - Use pnpm with the frozen lockfile when pnpm-lock.yaml exists - Build before restarting so a failed build doesn't take the site down

    f21dacc
  26. Default update.sh to main branch

    baa7a32
  27. Initial commit

    cd5d498

See how it all fits together in the colophon, or check the live stats.

your corner of discord Law's Corner A cozy corner to chat about software, Linux, projects, games, VRChat, and general internet nonsense. 22 members & counting Come say hi Join the server

thank you for being here

Support my work.

If you'd like to help me keep making things, you can find me here: