ort 21baeadeff Ignore GitOServr/ and .DS_Store
GitOServr/ holds the deploy SSH keypair for git.oservr.com — must never
be committed.
2026-08-15 13:48:01 -04:00
2026-08-15 13:48:01 -04:00
2026-08-15 13:48:01 -04:00
2026-08-15 13:48:01 -04:00
2026-08-15 13:48:01 -04:00
2026-08-15 13:48:01 -04:00
2026-08-15 13:48:01 -04:00
2026-08-15 13:48:01 -04:00
2026-08-15 13:48:01 -04:00

Kids Calendar

A self-hosted weekly calendar for your family. Parents log in and edit; each child gets a read-only "kiosk" link for a tablet, where they can only tick off tasks that are already there.

  • Parents: sign up, invite your spouse, create a calendar per child, edit freely, print, duplicate a calendar for the next week.
  • Kids: open their kiosk link on a tablet (add it to the home screen). They can see the board and check boxes — nothing else.
  • Everything a child checks off shows up on the parents' devices within a few seconds, no reload needed.

Running it

Requires Node.js 22.5+ (for the built-in node:sqlite module — nothing else to compile, no native build tools needed).

npm install
export SESSION_SECRET=$(node -e "console.log(require('node:crypto').randomBytes(32).toString('hex'))")
node server.js

Plain node doesn't read .env files — export the variables from .env.example yourself (as above), or use a process manager (pm2, systemd) that does it for you. Docker (below) reads .env automatically. Then open http://localhost:3007/signup.html.

cp .env.example .env   # set SESSION_SECRET
docker compose up -d --build

Data lives in a named volume (kids-calendar-data), so it survives container rebuilds. To back it up, back up that volume (or bind-mount ./data:/app/data in docker-compose.yml instead and back up that folder).

Environment variables

Variable Default Notes
SESSION_SECRET (insecure dev default) Required. A long random string — see .env.example for how to generate one.
PORT 3007
DATA_DIR ./data Where the SQLite file lives.
COOKIE_SECURE false Set true once this is behind HTTPS (e.g. a reverse proxy with a real certificate), so cookies are marked secure. Leave false for plain-HTTP LAN access, or login cookies won't be sent.
DISABLE_PUBLIC_SIGNUP false Set true once your household(s) exist, to stop the /signup.html page from creating new ones. Existing invite links still work.

How access works

  • Parents have real accounts (email + password) tied to a household. The first person to sign up creates the household; from the dashboard they can generate an invite link (valid 7 days, one-time use) for a spouse to join the same household with their own login.
  • Kids never get an account. Each child has an unguessable link (/k/<token>) that shows only their active calendar (set from the dashboard) and lets them toggle checkboxes — the server has no route at all for a kiosk link to edit text, add/delete tasks, or see other children's data. If a tablet is lost, regenerate that child's link from the dashboard to invalidate the old one.

Exposing this beyond your home network

This is built for LAN use by default (plain HTTP, cookies not marked secure). If you want access from outside your home:

  • Put it behind a reverse proxy (e.g. Caddy, nginx, Traefik) that terminates HTTPS, then set COOKIE_SECURE=true.
  • Set SESSION_SECRET to a real random value (never the dev default).
  • Consider setting DISABLE_PUBLIC_SIGNUP=true once your family's household(s) are created.
S
Description
No description provided
Readme
182 KiB
Languages
JavaScript 76.3%
HTML 15.4%
CSS 8.1%
Dockerfile 0.2%