The SvelteKit 2 + Drizzle + Three.js rewrite of stickersV1.
- Submit projects and earn stickers
- Browse all Hack Club stickers
- Gamble?! - coming soon
- Submit new sticker designs
- Vote on new designs
- Shop, orders, and a profile page
- Sticker JSON API for anyone who wants to build on top of the catalog!
PRs welcome! Poke us in #stickers on Slack with any questions!
git clone https://github.com/hackclub/stickers
cd stickers
# Copy env and fill in credentials
cp .env.example .env
# Spin up Postgres + the dev server (hot-reloaded, http://localhost:5173)
docker compose updocker compose up auto-merges compose.override.yaml, which starts Postgres 17 on localhost:5433 and runs the SvelteKit dev container with your local source bind-mounted in. The container runs bun run db:push --force on boot, so the schema is applied for you.
If you'd rather run the Svelte side on the host:
bun install
docker compose up db -d # just Postgres
bun run db:push
bun run devbun run check # svelte-check + tsc
bun run lint # prettier + eslint
bun run format # prettier --write
bun run db:push # push schema to the configured DATABASE_URL
bun run db:studio # drizzle studioSee .env.example for the full list. The important ones:
# Postgres
DATABASE_URL="postgres://root:mysecretpassword@localhost:5433/local"
POSTGRES_USER=root
POSTGRES_PASSWORD=mysecretpassword
POSTGRES_DB=local
# Hack Club Auth (auth.hackclub.com - unless HQ, you will have to use fewer scopes)
HC_AUTH_CLIENT_ID=
HC_AUTH_CLIENT_SECRET=
HC_AUTH_REDIRECT_URI=
# Encrypts OAuth tokens stored in the DB - must be 32 chars
TOKEN_ENCRYPTION_KEY=
# Airtable - sticker DB (If you ask really nicely, I can give you this)
AIRTABLE_PAT=
AIRTABLE_BASE_ID=
# Airtable - projects submission base (separate PAT, cos PII)
AIRTABLE_PAT_PROJECTS=
AIRTABLE_PROJECTS_BASE_ID=
AIRTABLE_PROJECTS_TABLE_ID=
# Slack bot token (xoxb-...) for avatars / display names
SLACK_BOT_TOKEN=
# Comma-separated hca_id values granted admin access
ADMIN_HCA_IDS=
# Hack Club CDN - proxies project screenshots into Airtable
CDN_API_KEY=The sticker catalog is exposed as JSON. There's a 60 req/min per-IP rate limit.
| Endpoint | Description |
|---|---|
GET /api/stickers |
List every sticker in the catalog |
GET /api/stickers?sticker=random |
Return one random sticker |
GET /api/stickers?sticker=random&limit=N |
Return N random stickers (max 50) |
GET /api/stickers?sticker=NAME |
Look up a sticker by name |
| Endpoint | Description |
|---|---|
POST /api/vote |
Cast/change your vote on a sticker candidate (requires auth + signed vote token) |
The repo ships with a multi-stage Dockerfile. The prod target builds with @sveltejs/adapter-node and runs on port 3000.
- Create new resource → Docker Compose
- Point at this repo
- Set the compose file to
compose.yaml(do not includecompose.override.yaml— that's for local dev only) - Set the environment variables below
- Point your domain at the
webservice - Deploy
docker build --target prod -t stickers .
docker run -d -p 3000:3000 \
-e DATABASE_URL="postgresql://user:pass@host/db" \
-e ORIGIN="https://your-domain.com" \
-e HC_AUTH_CLIENT_ID="..." \
-e HC_AUTH_CLIENT_SECRET="..." \
-e TOKEN_ENCRYPTION_KEY="..." \
stickersMade with <3 by maxstellar, euan and alice + art from kat and help from dani!