2026-08-11 19:10:07 -05:00

Stork

Shareable baby-name boards: suggest names, vote, keep origin / meaning / pronunciation in English, Russian, and Hebrew, and optionally record how a name sounds.

Each board has a unique share link (/b/<uid>). Start a board with no password, add names, share the link. Anyone with the link can edit.

Live read-only demo: stork.levkin.ca demo board.

Quick start (local)

cp .env.example .env   # optional STORK_INVITE_TOKEN bootstraps a "Family" board
make install
make test
make run

Open http://127.0.0.1:8094Start new board → share /b/…. Visitors open the link and enter a display name.

Features

  • Renamable columns (First is fixed; + adds up to 4; ✕ removes extras)
  • Drag a name onto another column to move it (or Move to… in the drawer)
  • Votes / ranking per board
  • Locale notes (en / ru / he) + personal significance (why the name matters) + nickname ideas + voice recordings (re-record overwrites)
  • Suggest form stays simple (baby name + nicknames + why); origin/meaning autofill when known — names arent translated, other scripts are written forms
  • Name generator — draw or remix from a local SQLite world first-name dump (JSON fallback) with filters (region, length, contains letter, …)
  • Site ideas — button emails suggestions to idobkin@gmail.com via Mailcow alerts@levkine.ca SMTP
  • Paper UI at /
  • Optional read-only boards via STORK_READONLY_BOARD_IDS

API sketch

Method Path Notes
GET /api/health Liveness
POST /api/boards Start board {title}{id, url, readonly} (rate-limited)
GET /api/boards/{id} Public board title + url + readonly
GET /api/resolve?invite= Legacy invite → board url
POST /api/session {board_id, display_name} → cookies (rate-limited)
DELETE /api/session Clear cookies
GET /api/columns Columns for session board
POST/PATCH/DELETE /api/columns… Add / rename / remove (not first; blocked if readonly)
GET/POST /api/names List / add
PATCH /api/names/{id} Locale notes, significance, and/or nicknames
POST /api/ideas Email a site idea (SMTP or mailto fallback)
GET /api/nicknames/suggest?spelling= Curated nickname ideas for a spelling
GET /api/names/lookup?spelling= Origin/meaning/script-form autofill hints
POST /api/names/{id}/move Move to another column {kind}
POST /api/names/{id}/vote {value: 1|-1|0}
POST/GET/DELETE /api/names/{id}/recording/{lang} Voice (magic sniff; max ~5 MiB)
GET /api/generator/meta Corpus regions / modes
POST /api/generator/spin Draw or remix with filters
DELETE /api/names/{id} Admin header X-Stork-Admin

Empty unused boards (no names) older than STORK_ORPHAN_BOARD_HOURS (default 48) are removed when someone creates a new board. Readonly + invite Family boards are kept.

Name corpus attribution: data/ATTRIBUTION.md (shipped as stork/data/world-names.sqlite3 when present, else world-names.json). Rebuild: make corpus-db.

Scripts

# Seed starter names onto a board (uses session cookies)
make seed STORK_BOARD_ID=b_…
# Demo set (Mira / Noa / Lior / Elena / Ezra):
STORK_SEED_FILE=data/demo-names.example.json make seed STORK_BOARD_ID=b_…

# Create an empty board and print share URL
.venv/bin/python scripts/create_board.py

Secrets

  • Local: gitignored .env
  • Deploy host: .env next to compose (never commit)

CI gates

Gitea Actions (.gitea/workflows/ci.yml): ruff and pytest are hard gates (no || true). Gitleaks on PRs. Bandit is advisory.

Production

Public HTTPS at stork.levkin.ca. Homelab deploy lives in a separate ansible repo (make deploy-stork).

S
Description
Shareable baby-name boards — votes, en/ru/he notes, voice. Demo: https://stork.levkin.ca/b/b_gS0LlZHm-lk
Readme MIT
7.1 MiB
Languages
Python 68.9%
HTML 28.6%
JavaScript 1%
Shell 0.8%
Makefile 0.6%
Other 0.1%