- JavaScript 35.9%
- Go 30.1%
- CSS 18.7%
- HTML 14.7%
- Dockerfile 0.6%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| docs | ||
| scripts | ||
| web | ||
| .dockerignore | ||
| .gitignore | ||
| .woodpecker.yml | ||
| bun.lock | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| main.go | ||
| main_test.go | ||
| package.json | ||
| README.md | ||
| server.go | ||
| store.go | ||
| store_test.go | ||
Little Days
A quiet, mobile-first shared notebook for labour and the first days with your baby. One Go binary, SQLite, and an embedded Tailscale node. No public listener or external fonts, scripts, analytics, accounts, or cloud data storage.
Labour preview · Baby preview · Nappy breakdown · Today · Edit record
What it does
- Start/stop a shared contraction timer; it survives refreshes and restarts.
- Add earlier contractions, feeds, and nappies. Tap any record to edit; removal is inside the dialog and requires confirmation.
- Last-hour contraction count, average completed duration, and start-to-start interval.
- Bottle feeds in ml or US fluid ounces, breast feeds in minutes.
- Wet, soiled, or both nappies; switch between rolling 24 hours and Today. Tap the total nappy count for an exclusive wet-only / soiled-only / both breakdown.
- Choose Labour or Baby as this browser's homepage and a default bottle unit.
- Download records from Preferences as JSON. Times display in the browser's local timezone.
Counts include entries whose start time falls within the inclusive rolling window. The running contraction counts toward the hourly total but not average duration. Intervals use consecutive starts within that hour; fewer than two shows a dash. The expanded nappy breakdown uses mutually exclusive categories that add up to the total. Today starts at midnight in the browser's timezone, including daylight saving changes; the server stores absolute Unix timestamps and forces no locale. This is a timing notebook; it provides no clinical assessments or thresholds.
Development
Go 1.27.1 (required by the pinned current Tailscale dependency):
go test -race ./...
go vet ./...
DATA_DIR=./data go run . --dev
Open http://127.0.0.1:8080. The explicit --dev flag binds only loopback and
bypasses Tailscale. The production default exposes only tailnet HTTPS on port 443.
All frontend assets are embedded; there is no Node build step. SQLite is pure Go.
Hosting on your tailnet
- Enable MagicDNS and HTTPS certificates in your Tailscale admin console.
- Create an auth key for a non-ephemeral service node, preferably preauthorized with an appropriate service tag. Restrict tailnet grants/ACLs so only intended family members can reach this node's TCP 443. This app trusts those grants; everyone permitted to connect can read, edit, export, and delete every record.
- Enable this repository in Woodpecker. Add secrets
forgejo_registry_usernameandforgejo_registry_token(package-write access). Permit them for main-branch push events only, never pull requests. The agent must support the Docker Buildx plugin (its daemon/privileged allow-list is host configuration). Tests, vet, formatting and binary build run first; PRs and manual main runs build an image without pushing; successful main pushes publishlatestand a full commit-SHA tag to the Forgejo registry. Manual runs intentionally do not publish or need registry secrets. - Copy docs/compose.yml into a deployment directory. Put the
auth key in
secrets/tailscale-authkey, readable by container UID 65532, with restrictive host permissions. Keep this file outside version control. The published image supports anonymous pulls; then:
docker compose up -d
The named volume must be writable by UID/GID 65532. If your volume driver creates
it as root, provision its ownership once before starting the app. Mounting a
pre-created directory owned by 65532 at /data is another option. No host ports,
TUN device, root container, or host network are needed.
Visit https://little-days.<your-tailnet>.ts.net from a device on the permitted
tailnet. The exact node hostname is available in the Tailscale admin console.
Do not enable Funnel. The app does not request it.
Persist all of /data: it contains both SQLite records and the tsnet node
identity. Once enrolled, you can remove the auth-key environment setting and
secret mount from compose; the persisted node identity handles subsequent starts.
TS_AUTHKEY is also supported, but a mounted TS_AUTHKEY_FILE keeps it out of
container environment inspection. A new hostname is configured with TS_HOSTNAME.
Set TS_ADVERTISE_TAGS to comma-separated node tags, for example tag:baby
or tag:baby,tag:server. Whitespace/empty entries are ignored. Tags are passed
to tsnet as AdvertiseTags, following tsidp.
Declare tags in the tailnet policy and ensure the enrollment key is authorized
for them. With no value, the app does not explicitly request additional tags.
For a consistent backup, stop the container and copy the entire volume before restarting it. Restore into an empty volume with the same ownership, ensuring the original node is stopped. JSON export is a readable record copy, not an import/restore feature. Prefer a commit-SHA image tag for predictable upgrades. The initial version only targets linux/amd64 in CI; adjust platforms if needed.
Design and operation
No separate login screen: access is enforced by your tailnet policy. Browser requests require a same-origin custom header; Go's cross-origin protection rejects cross-site mutations. Notes render as text, never HTML. All mutations commit to SQLite before success is shown. Only one active contraction is permitted; stale stop requests cannot stop a newer contraction. Overlapping contractions and future timestamps are rejected. All clients refresh every five seconds. There is no offline queue: a failed save stays in the form for retry.
This repository includes the pipeline and hosting template. CI activation, registry credentials, Docker image publication, and live tailnet enrollment need your infrastructure; none are claimed complete merely by pushing the code.
References: tsnet server API, Woodpecker Docker Buildx plugin.
Browser smoke test
Optional development-only dependencies (Bun and Playwright); the app itself does
not use them. Start --dev with a new disposable DATA_DIR, then in another
terminal run:
bun install
bunx playwright install chromium
bun run test:ui
The test writes synthetic records and checks phone/desktop overflow, shared timer
and reload, feeds, nappies, validation, edits/deletion, failed-save recovery,
export and browser preferences; calendar-day boundaries and UTC round-trips in
Tokyo, Los Angeles and New York, including 23/25-hour DST days. Screenshots go to ignored artifacts/.
Playwright's browser requires its documented Linux shared libraries. The test
expects localhost:8080 and an empty database; never aim it at live family records.
The row/dialog interaction and disclosure design rationale is documented in docs/ux.md, with the accessibility references used.
License
Little Days is licensed under Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0). See LICENSE for the full terms. For attribution, credit the Little Days contributors and link to this repository. Third-party dependencies retain their respective licenses.