scout-website
Public recruiting site for Cub Scout Pack 73 and Scouts BSA Troop 73 (Zieglerville / Green Lane, PA).
Live at https://greenlanescouts73.org (NPM proxy → scout-website container, port 8132 on arrstack).
Replaces the scoutpoc proof of concept (claude-workspace/stacks/scoutpoc).
Updating the calendar (the part you'll actually do)
All dates on the site (home "Next up," both program pages, the Calendar page) come from the scout-calendar feed (claude-workspace/stacks/scout-calendar), which serves the Radicale collection mike/site73 as JSON:
EVENTS_FEED_URL ← Portainer stack env; http://scout-calendar:8000/events.json
/data/events-cache.json ← last good copy, served automatically whenever the feed is down
Edit the calendar in any CalDAV client pointed at Radicale (or ask Claude), and the site follows within ~5 minutes. No file edits, no rebuild, no redeploy. The public ICS families can subscribe to is https://calendar.greenlanescouts73.org/calendar.ics.
Event fields the feed emits (one object per event):
{"date": "2026-10-17", "end": "2026-10-18", "time": "from 4:00 PM",
"title": "Fall Campout", "location": "Green Lane Park",
"unit": "both", "badge": "OVERNIGHT"}
daterequired (YYYY-MM-DD).endonly for multi-day events.unit:"pack","troop", or"both"(default pack). Drives the Pack/Troop calendar filter and each page's "Next up."badge: optional shouty label (FALL OPEN HOUSE,OVERNIGHT,THE BIG ONE).- Past events are hidden from "Next up" automatically; the Calendar page shows the whole program year.
- TeamSnap (team 8198615) stays the source of truth for registered families; Radicale
mike/site73is the source for this public site.
Documents
Files at /srv/scout-website-assets/docs (bind-mounted read-only at /docs), published by
manifest.json in that same folder. Adding a document is a file drop plus a manifest entry: no
rebuild, no redeploy. The app re-reads the manifest whenever its mtime changes.
{"slug": "permission-slip", "file": "2026-permission-slip.pdf",
"title": "Activity permission slip", "description": "One per scout, per outing.",
"category": "forms", "unit": "both", "visibility": "public", "updated": "2026-08-30"}
slugis the permanent URL:greenlanescouts73.org/documents/<slug>. Never change one that has been printed or emailed. To publish a new version, point the same slug at the new filename.categorymatches an id in the manifest'scategories; anything else lands under "Everything else".visibility:public- listed on/documents, open to anyone.unlisted- served at its slug to anyone holding the link, kept off the index, sent withX-Robots-Tag: noindex. For internal papers that need a durable link before login exists. This is obscurity, not access control. An unlisted link is forwardable; assume it will be.members- hidden and unservable, returns 404. This is not a working gate yet, it is the seam login will attach to.
updatedoptional; without it the file's own mtime is shown.
Documents are served through the app (/documents/{slug}), never from a static mount. Anything
under /app/static is public forever, so nothing that will ever need gating goes there. When member
login exists it plugs into documents.visible() and no public URL moves.
Layout
app/app.py— FastAPI app, all pages server-rendered (Home, /cubs, /troop, /calendar?unit=, /join, /documents)./contact301s to/join.app/documents.py— the document shelf: manifest loading, the slug-to-file map, and the one visibility gate.docker-compose.yml— Portainer Repository stack definition (port 8131).- Images: not in the repo. Bind-mounted read-only from
/srv/scout-website-assets/img. Source photos live on the NAS (Scouting-Recruitment-Images,scouting-comms/pack-73/assets/social); web-sized copies inScouting-Recruitment-Images/_web.
Leads
/srv/scout-website/data/leads.jsonl — one JSON line per interest-form submission, plus an ntfy push per lead.
Lead pipeline (self-contained)
/join submissions write directly to the shared Google Sheet (SHEET_ID stack env, creds bind-mounted from /srv/scout-website/secrets/service_account.json) and push to the shared ntfy scouting topic (NTFY_BASE/NTFY_TOPIC/NTFY_TOKEN). The extra comments box lands in a new 8th sheet column, Comments. Every submission also appends to /data/leads.jsonl; if both sheet and push fail, a warning push goes to the fallback NTFY_URL topic. No dependency on the scoutpoc or scouting73-form containers: both can be decommissioned without breaking this site. (The flyer QR domain scouting73.thewichersfamily.com should be repointed here before its container is retired.)