Code
Outside of work I build self-hosted tools, personal websites, and whatever else catches my interest. Side projects are where I experiment with new technology and stay sharp since at work I stick to stable, mature frameworks. There is plenty more to see on my GitHub account.
Taproot
Dotfiles, containers, and the configs that make a machine mine.
{
"sha": "44a90a4",
"message": "crontab: keep stock 02:00 UTC daily run, document the ~22:00 ET landing",
"date": "2026-07-21T23:49:05Z",
"author": "Isaac Bythewood"
}GitHubdarkfurrow.com
A living almanac of seasons, soil, and the quiet knowledge that used to be common.
{
"sha": "0878cc9",
"message": "fix december countdown, retire service worker, honest nav links\n\n- days_until_next_season skips same-name seasons when wrapping into next\n year, so December no longer counts down the days until winter mid-winter\n- delete sw.js and unregister existing installs: it cached unhashed asset\n paths that no longer exist and pinned returning visitors on stale pages\n- reload content on day rollover, not just season rollover (sun, moon, and\n the day-seeded picks all change at midnight)\n- season nav gets real hrefs (works without JS) and aria-current=\"page\"\n- winter ends 2/29 so leap day belongs to a season explicitly\n- format_hm carries 60 minutes into the hour (\"5h 60m\" -> \"6h 0m\")\n- 500 responses log the detail and serve a generic body",
"date": "2026-07-21T23:01:58Z",
"author": "Isaac Bythewood"
}GitHubRepos
A minimal self-hosted git browser. Renders bare repos as a website with commits, diffs, syntax-highlighted blobs, and atom feeds, plus clone over HTTPS.
{
"sha": "7634889",
"message": "harden raw serving and diff/highlight limits, fix syntax detection\n\n- serve html/svg/xml blobs on /raw as text/plain with nosniff: rendered\n inline they would run scripts on this origin (stored XSS)\n- tree listings read blob sizes via find_header instead of loading every\n blob's full contents\n- cap parsed diff output at 2MB (a vendored-tree commit could render\n hundreds of MB) and skip syntax highlighting past 1MB with a \"view raw\"\n fallback; cap READMEs at 1MB\n- syntax detection no longer calls find_syntax_for_file (it opened the\n request-supplied basename on the server fs); match extension, whole\n name, then first-line sniff from the blob\n- percent-encode rev and path segments in tree/blob hrefs (new\n urlencode_path filter keeps separators)\n- atom 404s serve a generic body instead of the error string\n- kill_on_drop on git http-backend so dropped clone streams don't orphan\n upload-pack",
"date": "2026-07-21T23:08:13Z",
"author": "Isaac Bythewood"
}GitHubAnalytics
A self-hostable analytics service with a straightforward API to track events from any source.
{
"sha": "b9ed9c7",
"message": "harden collector and auth, fix historical graph anchor and report filenames\n\n- fail startup when ANALYTICS_PASSWORD is unset instead of defaulting to admin\n- constant-time password compare with a flat 500ms delay on failed logins\n- escape / in typst_md so collector text cannot start Typst line comments\n- anchor the events graph to the requested end date, not today\n- ASCII-sanitize report Content-Disposition filenames (non-ASCII panicked)\n- cap collector bodies at 16KB, event names at 200 chars, hot fields at 2048\n- read client IP from the proxy-appended last X-Forwarded-For entry\n- validate and atomically rename GeoIP downloads so a corrupt file cannot\n wedge enrichment for 30 days\n- collector.js: keepalive so page_leave survives unload, cap click text",
"date": "2026-07-21T00:31:52Z",
"author": "Isaac Bythewood"
}GitHubStatus
A self-hosted uptime monitor and status page builder, with Lighthouse audits and PDF reports baked in.
{
"sha": "a6e6b21",
"message": "harden auth, crawler, and report endpoints\n\n- fail startup when STATUS_PASSWORD is unset instead of defaulting to admin\n- constant-time password compare with a flat 500ms delay on failed logins\n- require auth for ?report=pdf|md so public pages are not a free\n CPU-bound PDF-generation endpoint\n- ASCII-sanitize report Content-Disposition filenames (non-ASCII panicked)\n- kill_on_drop on the lighthouse subprocess so timeouts don't orphan chromium\n- crawler: respect the deadline during external-link probes and cap the\n probe list at 500 links\n- crawler: stream HTML bodies with a 5MB cap instead of buffering unbounded\n- record real elapsed time on probe failure, not a flat 10s timeout\n- require https:// on property create (checker is HTTP/2-over-TLS only)\n- toggle_public returns an honest 500 on db error instead of success:true",
"date": "2026-07-21T22:28:21Z",
"author": "Isaac Bythewood"
}GitHubFinance
A self-hosted market watcher for stocks, ETFs, indexes, and futures, with live charts, key stats, fundamentals, and SEC filings.
{
"sha": "8a927c5",
"message": "close guard pacing race, prune dead watchlists, tidy small findings\n\n- serialize EndpointGuard::acquire per endpoint with an async mutex: the\n read-decide-sleep-commit sequence was racy, letting concurrent tasks\n violate pacing or double-claim the half-open probe slot\n- robots.txt: Disallow /api/ and /stream\n- prune watchlist sids whose newest row is >13 months old (the fin_sid\n cookie lives 12 months and is never refreshed, so they can never return)\n- country_bucket returns String instead of leaking the long-tail codes\n- drop unused zip dependency",
"date": "2026-07-21T22:52:14Z",
"author": "Isaac Bythewood"
}GitHubblog.bythewood.me
A self-hostable markdown blog for developers, with code blocks, syntax highlighting, live search, great SEO, and a clean customizable UI.
{
"sha": "7681299",
"message": "cache PDFs, sanitize search rendering, tighten headers and errors\n\n- cache rendered PDF bytes per slug (posts are immutable per process, so\n every hit paid the CPU-bound Typst compile; now 6ms warm vs 1.2s cold)\n- 500 responses log the detail and serve a generic body instead of leaking\n template/IO error strings\n- add `inline;` disposition-type to PDF and Markdown Content-Disposition\n- search.js: escape regex metacharacters in the query and build result rows\n from text nodes instead of innerHTML (also preserves match case)\n- template `debug` flag follows cfg!(debug_assertions) instead of false\n- /content/images/ caches 1 day instead of 1 year (unhashed filenames)\n- frontmatter close delimiter must start a line (find \"\\n---\"), so a ---\n inside a value cannot truncate the metadata block",
"date": "2026-07-21T22:58:19Z",
"author": "Isaac Bythewood"
}GitHubTimelite
A simple time tracker that keeps everything local in your browser. No accounts, no sync, no server-side state.
{
"sha": "1e9cf55",
"message": "harden persistence and hotkeys, DST-safe days, next 16.2.11\n\n- persist() helper with .catch around every localForage.setItem: a quota\n or private-mode failure was an invisible unhandled rejection\n- LOCALDATA_READY merges over initialState (stored state can predate new\n fields), coerces entry start/end and timer to Dates, defaults tags to []\n- import assigns fresh ids for duplicates within the imported file, not\n just collisions with the existing log; delete dead mergeEntries\n- hotkeys ignore ArrowUp/Down inside inputs (they were stealing the caret)\n- DST-safe day stepping in log (setDate) and summary (setDate + round)\n- unsafe-eval only in dev CSP\n- bun --bun in package scripts, pin Dockerfile to oven/bun:1-alpine\n- bump next to ^16.2.11",
"date": "2026-07-21T23:15:42Z",
"author": "Isaac Bythewood"
}GitHubisaacbythewood.com
The personal website you are looking at right now.
{
"sha": "0d765b7",
"message": "security headers, next 16.2.11, fix canvas and loader bugs\n\n- add security headers in next.config.js (nosniff, frame options,\n referrer policy, permissions policy, CSP allowing the analytics\n collector in script-src/connect-src; unsafe-eval only in dev)\n- bump next to ^16.2.11\n- _document.js collector snippet: assign collectorScript to window\n explicitly instead of via implicit global\n- synthwave.js: close the rgba() in the gradient stop\n- retrostars.js: refs instead of state (state re-rendered the component\n on every mousemove and twinkle tick; the canvas loop reads refs)\n- index.js: release the loader on hero image error, not just load\n- add prop-types as a real dependency (was riding on a transitive)",
"date": "2026-07-21T23:12:30Z",
"author": "Isaac Bythewood"
}GitHub