No description
  • TypeScript 58.2%
  • Rust 21.7%
  • CSS 19.4%
  • Dockerfile 0.4%
  • HTML 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
lplaat 234a5f7087 Initial commit: PlaatPrikker, a self-hosted datumprikker
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:36:29 +02:00
src Initial commit: PlaatPrikker, a self-hosted datumprikker 2026-10-02 16:36:29 +02:00
web Initial commit: PlaatPrikker, a self-hosted datumprikker 2026-10-02 16:36:29 +02:00
.dockerignore Initial commit: PlaatPrikker, a self-hosted datumprikker 2026-10-02 16:36:29 +02:00
.gitignore Initial commit: PlaatPrikker, a self-hosted datumprikker 2026-10-02 16:36:29 +02:00
Cargo.lock Initial commit: PlaatPrikker, a self-hosted datumprikker 2026-10-02 16:36:29 +02:00
Cargo.toml Initial commit: PlaatPrikker, a self-hosted datumprikker 2026-10-02 16:36:29 +02:00
Dockerfile Initial commit: PlaatPrikker, a self-hosted datumprikker 2026-10-02 16:36:29 +02:00
LICENSE Initial commit: PlaatPrikker, a self-hosted datumprikker 2026-10-02 16:36:29 +02:00
README.md Initial commit: PlaatPrikker, a self-hosted datumprikker 2026-10-02 16:36:29 +02:00

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:

  1. Push this repository to GitHub.
  2. In Plaat Deploy, create a project from the repository. It detects the Dockerfile, which exposes port 8080.
  3. The SQLite database lives in the /data volume, 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 localStorage and sends them as X-Admin-Token / X-Participant-Token headers.

License

Copyright (c) 2026 Leonard van der Plaat

Licensed under the MIT license.