5.6 KiB
Le Daily Squirrel — Project Status
Date: 2026-07-04
Repo: AutomationWise/leDailySquirrel on Forgejo (gf.automationwise.com)
Production: https://news.opelousas.org (Cloudflare Pages, project news-opelousas-org)
A satirical daily newspaper for Opelousas — vintage faded-newsprint aesthetic, edition-based publishing, self-hosted GitOps pipeline, with AI journalist agents planned as the content authors.
Architecture
| Piece | Where | Notes |
|---|---|---|
| Git forge (Forgejo 9) | kiln (Fedora), rootless Podman Quadlet |
HTTP 127.0.0.1:3000 behind Caddy at gf.automationwise.com; SSH on host port 3022 (LAN only, blocked at edge) |
| Reverse proxy | Caddy on kiln |
Owns 80/443, auto TLS; also fronts Vaultwarden and Tuwunel |
| CI runner | kiln, Podman Quadlet (forgejo-runner 3.4.1) |
Org-level runner kiln-worker, label ubuntu-latest:docker://node:20-bookworm, uses host Podman socket (UserNS=keep-id, SecurityLabelDisable=true, socket path identical inside/outside container) |
| Static site generator | Eleventy v3.1.6 | Input content/, includes ../_includes, output dist/ |
| Hosting | Cloudflare Pages, Direct Upload mode | Deployed via wrangler pages deploy dist --project-name=news-opelousas-org --branch main from .forgejo/workflows/deploy.yml on push to main |
| Dev box | anvil |
Git remote over SSH: ssh://git@gf.automationwise.com:3022/... with ~/.ssh/config host override to kiln's LAN IP (NAT hairpin workaround) |
Secrets: CF_ACCOUNT_ID + CF_API_TOKEN in Forgejo repo Actions secrets. Token permissions: Account→Cloudflare Pages→Edit, Account→Workers Scripts→Edit, User→User Details→Read, scoped to the account.
Site mechanics
- Editions live at
content/editions/YYYY-MM-DD/index.md; URLs are/editions/YYYY-MM-DD/. - Front matter:
layout: edition,edition_number(int),volume(int — must be a number, theromanfilter converts; storing "I" breaks it),date,status: draft|published,title. - Collections (
eleventy.config.js):publishedEditions(all published, newest first),currentEdition(newest one only). - Homepage (
content/index.njk): paginates overcurrentEdition(size 1) and renders it at/— render, not redirect.content/index.11tydata.jsmapstitle,date,edition_number,volumeup from the paginated item. - Filters:
newsDate(UTC-forced long date — fixes the CST off-by-one-day),roman(int → Roman numeral). - 404:
content/404.md→dist/404.html. Required — without it Cloudflare Pages SPA-fallbacks every unknown path to/with a 200 (this was the "every URL shows the current edition" bug). Todo → more Squirreliness. - Masthead: inline SVG (Inkscape export, single path, mm-scale coordinates,
viewBox="0 0 381 170"). Fill flows fromfill="var(--ink)"on the<g>(path's inlinefillstyle removed). CSSsquirrel-flipanimation rotates it 180° on a 50/50 duty cycle (~24s period), with aprefers-reduced-motionopt-out. - Lifecycle tooling:
make publication(scaffolds next edition, enforces one open draft, createsedition/<date>/openbranch),make publish(sedsstatus: draft→published),make build/make serve.
Hard-won gotchas (do not relearn these)
- Quadlet units can't be
systemctl enabled — the[Install]block in the.containerfile handles boot; justdaemon-reload+start. Lingering is enabled for the user on kiln. - Rootless Podman + SELinux: never
:Za socket mount; socket path must match host↔container when the runner asks the host to mount it into job containers. - Wrangler CI deploys need
--branch mainor they land as Preview, not Production. - Eleventy config must be a single
module.exports; run builds from repo root only. - Front matter beats directory data files (
editions.json) — data bugs hide there.
Current status
Working end-to-end: push to main → runner builds → Cloudflare production deploy. Volume renders as Roman numerals, archive URLs serve the correct historical edition, fake URLs 404, custom domain live, SVG masthead in place with periodic flip.
In development / not yet done:
- Edition immutability (freeze-at-publish) — designed, not implemented. Plan:
make publishbuilds once, snapshots rendered HTML to committedfrozen/editions/<date>/, marks sourcefrozen: true;editions.11tydata.jsreturnspermalink: falsefor frozen editions;addPassthroughCopy({ "frozen/editions": "editions" })serves snapshots verbatim. Homepage stays dynamic; only/editions/<date>/freezes. Past editions from the pre-freeze era are expendable (still in dev). - Archive ledger — no
/archive/(or/editions/) index page listing past editions yet. - AI journalist pipeline — the actual point of the project. Agents ingest daily material, write articles in the three-column structure, and open PRs against the open
edition/<date>/opendraft branch. Likelyimp(OpenClaw on liminal) via NadirClaw routing, following the spec-first handoff pattern. Not started. - Maintenance-mode toggle — deferred; plan is a Cloudflare Page Rule redirect to a
/maintenance/page. - Masthead spacing polish — minor viewBox/padding nudges deferred.
Suggested next steps, in order
- Implement the freeze-at-publish flow while there's only throwaway content to test against (touching
publish.sh,eleventy.config.js, newcontent/editions/editions.11tydata.js). - Build the archive index page off
collections.publishedEditions. - Define the agent contract: article markdown format, column assignment, front-matter rules, PR-to-draft-branch workflow — then wire the first agent.