- TypeScript 58.2%
- Rust 21.7%
- CSS 19.4%
- Dockerfile 0.4%
- HTML 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| src | ||
| web | ||
| .dockerignore | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| Dockerfile | ||
| LICENSE | ||
| README.md | ||
PlaatPrikker
Find the date that works for everyone. PlaatPrikker is a friendly, self-hosted datumprikker (scheduling poll): pick a few dates, share one link, and see at a glance when everyone can make it.
Nobody needs an account, not even the organizer.
Features
- Three-step poll wizard: event details, a two-month calendar (tap or drag to select days), then settings. Unfinished drafts survive a page refresh.
- Optional times per date: Morning, Afternoon and Evening presets or a custom time, plus "use these times for all dates".
- One-tap voting: participants enter their name and tap Yes, If need be or No per date (or tap the date to cycle). They can also say "Yes to all" and leave a note.
- Live results: a participant × date matrix with the best options starred, kept up to date automatically.
- Pin the final date: options are ranked from best to worst. Pinning one closes voting and shows everyone a "the date is set" banner with Add to calendar (.ics) and Google Calendar buttons.
- Organizer tools: edit the poll (votes on dates you keep are preserved), close or reopen voting, set a deadline, keep responses private (others only see totals), remove responses, duplicate, export CSV, and delete.
- No accounts: the organizer gets a private organizer link, and participants can change their response from the same device or with a personal link. Polls you created or answered are listed on the home page.
- Easy sharing through copy link, the native share sheet, WhatsApp or email.
- English and Dutch, light and dark mode, mobile first and keyboard accessible.
- Privacy: no trackers, no external requests, and secret tokens travel in the URL fragment so they never end up in server logs.
Deploying with Plaat Deploy
PlaatPrikker is a single Dockerfile project that works without any configuration:
- Push this repository to GitHub.
- In Plaat Deploy, create a project from the repository. It detects the
Dockerfile, which exposes port8080. - The SQLite database lives in the
/datavolume, which Plaat Deploy keeps between deployments.
Every push redeploys automatically.
Running elsewhere
docker build -t plaatprikker .
docker run -d -p 8080:8080 -v plaatprikker-data:/data plaatprikker
Put it behind a reverse proxy that terminates HTTPS. Forwarding headers (X-Real-IP,
X-Forwarded-For) are only trusted from private network peers, and are used for rate limiting.
Configuration
All settings are optional environment variables:
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
HTTP port |
DATA_DIR |
data (/data in Docker) |
Directory for the SQLite database |
POLL_RETENTION_DAYS |
180 |
Delete polls whose last date passed this many days ago (0 disables cleanup) |
Development
You need Rust and Node.js.
# Terminal 1: API server on :8080 (serves web/dist when it exists)
cargo run
# Terminal 2: frontend with hot reload on :5173, proxying /api to :8080
cd web && npm install && npm run dev
Run the tests with cargo test, and type-check the frontend with cd web && npm run typecheck.
How it works
- Backend (src/): Rust with axum and SQLite through rusqlite. The built frontend is embedded in the binary.
- Frontend (web/): Preact and TypeScript, built with Vite, with no UI framework and about 30 KB of gzipped JavaScript.
- Access without accounts: creating a poll returns a random admin token, and responding returns a
random participant token. The browser keeps them in
localStorageand sends them asX-Admin-Token/X-Participant-Tokenheaders.
License
Copyright (c) 2026 Leonard van der Plaat
Licensed under the MIT license.