Source now lives in lukas/calendar-web instead of this flake, matching the website/dashboard pattern: pinix checks it out at /var/www/calendar-web, nginx serves the dist/ output directly, and a new gitpull-calendar-web webhook hook does `git pull && npm ci && npm run build` (esbuild) on push. Frontend/style-only changes no longer need a nixos-rebuild. - webhook.nix: new hook + build script - packages.nix: add nodejs (npm needed for the build script) - forgejo.nix: enable ENABLE_PUSH_CREATE_USER, needed to create the new repo by pushing to it rather than via the web UI - web.nix: nginx root points at /var/www/calendar-web/dist - README.md: document the new deploy path Still needs a Forgejo-side webhook added manually in the calendar-web repo settings (no API token available to script it): target http://127.0.0.1:9000/hooks/gitpull-calendar-web, push events on dev.
2.9 KiB
Calendar setup
The calendar stack runs on pinix:
- Radicale provides CalDAV on the pinix Tailnet address, port
5002. - Nginx serves a small read-only FullCalendar frontend on the pinix Tailnet
address, port
5001. - LLDAP authenticates users who edit calendars through CalDAV.
- Each authenticated LLDAP user owns a private calendar namespace below their username and can create calendars there.
- All authenticated LLDAP users can edit
calendar/public, but cannot delete or overwrite the shared collection itself. Anonymous access remains read-only.
Access
Changed 29.07.2026: cal.0ff.space and Radicale used to share the same
hostname, resolving differently depending on network (frontend externally,
Radicale directly inside the Tailnet) because two separate Traefik routers
matched it. That ambiguity was the likely cause of changes not showing up
consistently (Vikunja #125). Now:
-
cal.0ff.spaceis always the read-only frontend, on both networks. -
Radicale moved to its own internal-only hostname:
https://calendar.0ff.spaceOnly resolvable inside the Tailnet (AdGuardHome rewrite in
hosts/blunix/localServices/adguard.nix); there is no public route to Radicale directly. Point CalDAV clients atcalendar.0ff.spacewhile on the Tailnet.
On the public Traefik entrypoint, cal.0ff.space serves the read-only calendar
frontend. Its only data endpoint is:
https://cal.0ff.space/calendar.ics
Nginx maps that exact path to Radicale's anonymously readable
/calendar/public/ collection. Other public paths are never proxied to
Radicale, and the proxy removes credentials and cookies.
Changed 30.07.2026: the frontend's source moved out of this flake into
its own Forgejo repo, lukas/calendar-web. nginx's root for cal.${domain}
now points at /var/www/calendar-web/dist on pinix, kept current by the
gitpull-calendar-web webhook hook (hosts/pinix/localServices/webhook.nix):
push to the repo's dev branch, the hook git pulls, runs npm ci && npm run build (esbuild bundles FullCalendar + ical.js), done -- no nixos-rebuild
needed for frontend-only changes. This flake still owns the nginx vhost,
firewall ports, and the CSP/proxy config around it.
Network boundaries
Radicale and the frontend listen only on the pinix Tailnet address. The NixOS
firewall permits ports 5001 and 5002 only on tailscale0. Public traffic
passes through Traefik on blunix.
Verification
After deployment:
- Check that Radicale and Nginx are active.
- Confirm that ports
5001and5002listen only on the pinix Tailnet IP. - Verify that
/calendar.icsis anonymously readable through the frontend. - Verify an authenticated LLDAP CalDAV login.
- Create and delete a temporary event in
calendar/publicwith two different authenticated accounts. - Create a personal calendar and verify that another account cannot read it.