- TypeScript 71.6%
- CSS 24.9%
- JavaScript 2.5%
- Dockerfile 0.8%
- Shell 0.2%
| .cursor | ||
| .github/workflows | ||
| .vscode | ||
| docker | ||
| prisma | ||
| public | ||
| scripts | ||
| src | ||
| tests | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| eslint.config.mjs | ||
| next.config.ts | ||
| package-lock.json | ||
| package.json | ||
| playwright.config.ts | ||
| postcss.config.mjs | ||
| README.md | ||
| tsconfig.json | ||
| vitest-env.d.ts | ||
| vitest.config.ts | ||
Piccolus
A small party game for your phone: pick a deck, add the players,
pass the phone around. Cards can include $P / $P1, $P2, … placeholders that get
filled in with a random player on every draw.
You can also create your own categories, decks, and cards from the built-in admin UI.
Run it with Docker
The easiest way to host Piccolus is with Docker Compose. You only need Docker (with the Compose plugin) installed.
# 1. Get the code
git clone <repo-url> piccolus
cd piccolus
# 2. Configure two secrets
cp .env.example .env
# then edit .env and set:
# ADMIN_PASSWORD=<your admin password>
# AUTH_SECRET=<at least 32 random characters>
# Tip: openssl rand -base64 32
# 3. Start it
docker compose up -d --build
Open http://localhost:3000.
The admin UI is at http://localhost:3000/admin. Log in with the
password from ADMIN_PASSWORD.
A fresh install starts with an empty database. The home screen detects
the empty state and shows a single "Open admin" button so new operators
know exactly where to go. Sign in with the password from
ADMIN_PASSWORD and create your first categories, decks, and cards.
The example seed (prisma/seed.ts) is a dev/E2E-only helper and is not
shipped in the Docker image, so production deployments can never be
accidentally populated with demo content.
Updating
git pull
docker compose up -d --build
The database schema is applied automatically on startup. Your data
and uploaded card images are kept in Docker named volumes (db and
uploads) and survive rebuilds.
Backups
Both the database and the uploaded images live on volumes:
# back up everything
docker run --rm \
-v piccolus_db:/db -v piccolus_uploads:/uploads \
-v "$PWD":/out alpine \
tar -C / -czf /out/piccolus-backup.tgz db uploads
Restore by extracting the archive back into the same volumes.
Configuration
Set these in .env:
| Variable | Required | Notes |
|---|---|---|
ADMIN_PASSWORD |
yes | Password for /admin/login. Pick a strong one. |
AUTH_SECRET |
yes | Secret used to sign the admin session cookie. Use at least 32 random characters. |
PORT |
no | Host port to expose the app on. Defaults to 3000. |
Reverse proxy / TLS
Piccolus listens on plain HTTP inside the container. Put it
behind a reverse proxy (Caddy, Traefik, nginx, etc.) to terminate TLS.
A Strict-Transport-Security header is set automatically when the
app sees NODE_ENV=production (which the Docker image does).
Using the app
Hosting a game
- Open the home page on the phone you'll pass around.
- Pick a category, then a deck. (Or pick Everything to draw from every deck.)
- Type in the player names (at least two). Add more with Add.
- Hit Start. Each card may contain
$P, which is replaced with a randomly chosen player for that draw. Use$P1,$P2, … when the same player must appear more than once — the same number always refers to the same person. - Tap Next to pull the next card.
If a card needs more players than you've seated, it's skipped and you're told how many were excluded.
Managing content
Sign in at /admin/login.
- Categories: top-level groups shown on the home screen.
- Decks: collections of cards inside a category.
- Cards: text + rarity tier + optional image. Use
$Pin the card text for any spot that should become a player name. Multiple$Ps in one card pick different players. Use$P1,$P2, … for stable references — the same number always maps to the same player within a single draw (e.g.$P1 dares $P2 … $P1 drinks too). - Drop rate:
1 in 5is the most common tier;1 in 100is the rarest. Rarity controls how often the card surfaces during a shuffled run.
That's it. There are no user accounts to manage; the password in
ADMIN_PASSWORD is the whole auth surface.
Local development
If you'd rather run it without Docker:
npm install
cp .env.example .env # then fill in ADMIN_PASSWORD + AUTH_SECRET
npm run dev # applies the schema + seeds example data, then boots
npm run dev runs an idempotent pre-step that applies the Prisma
schema and seeds an example library (two categories, three decks, a
handful of cards). The seed is non-destructive: it only adds missing
example rows and never overwrites your edits, so it's safe on every
boot. Use npm run db:reset to wipe and re-seed when you want a clean
slate.
Useful scripts: npm test, npm run test:e2e, npm run lint,
npm run db:studio, npm run db:seed. Architecture notes for
contributors live in AGENTS.md.