The Human Club is Knock's demo site: petitions and fundraisers for humans only, where each human signs a petition once. It's apps/bouncer in getknock/knock, a Next.js app that checks knocks in its own functions with @knock-dev/verify and keeps its records in a libSQL database. A copy of your own is a whole club of its own, with its own petitions, fundraisers and members.
What you need
- A checkout of
getknock/knockand Bun. - A database: a Turso database for Vercel or any host that runs more than one copy of the club, or a SQLite file on a server that runs one.
- An origin for it, such as
https://club.example, served on HTTPS.
Try it on your machine
bun install
cd apps/bouncer
bun run devThe club is at http://localhost:3001 and keeps its records in human-club.db beside it. In development, knocks go through the Door at http://localhost:3000: run the Console and Door's dev server too (bun run dev in apps/door), or set NEXT_PUBLIC_DOOR=https://door.getknock.dev to use Knock's.
On Vercel, with Turso
-
Make the database. With the Turso CLI, signed in:
Shell turso db create human-club turso db show human-club --url # libsql://… turso db tokens create human-club # its tokenThe club makes its tables itself, the first time it opens the database.
-
Make the project. In Vercel, import your copy of
getknock/knockand set the Root Directory toapps/bouncer.apps/bouncer/vercel.jsonsets the rest: the install runs at the repo's root, because the club uses Knock's packages from the same workspace, thenbun run build. -
Set its environment variables, for Production:
TURSO_DATABASE_URLandTURSO_AUTH_TOKENfrom step 1, andCLUB_ORIGINset to the club's address. The rest are in Settings. -
Deploy, then add your domain to the project. If the domain changes, change
CLUB_ORIGINwith it and deploy again.
Preview deployments have addresses of their own, and the club takes knocks only for CLUB_ORIGIN. To try one, give previews a domain in Vercel, set CLUB_ORIGIN to it for the Preview environment, and give them a database of their own, so tests stay out of the real club.
On your own server
bun install
cd apps/bouncer
NEXT_PUBLIC_DOOR=https://door.example bun run build
CLUB_ORIGIN=https://club.example \
TURSO_DATABASE_URL=file:/var/lib/human-club/club.db \
PORT=3001 \
bun run startPut it behind a proxy that serves HTTPS, as for knockd, and keep it running with systemd or the like. A SQLite file works for one copy of the club. If you run more than one, they must share a database: use Turso, with libsql://… and its token.
Settings
NEXT_PUBLIC_ settings are read when the club is built. The rest are read when it runs.
| Setting | Default | What it does |
|---|---|---|
CLUB_ORIGIN | none: needed in production | The club's origin, exactly as browsers send it, such as https://club.example. Wedgies sign for it, and the club takes no knock signed for another. In development, the address the request came to. |
TURSO_DATABASE_URL | file:human-club.db | The database: libsql://… for Turso, or file:… for a SQLite file. Needed on Vercel. |
TURSO_AUTH_TOKEN | none | Turso's token for that database. |
NEXT_PUBLIC_DOOR | Knock's, https://door.getknock.dev; in development, http://localhost:3000 | The Door knocks go through: Knock's, or your own. |
CLUB_ETH_RPC | https://ethereum-rpc.publicnode.com | A mainnet RPC: for ENS, and to check gifts. |
CLUB_WORLD_RPC | https://worldchain-mainnet.g.alchemy.com/public | A World Chain RPC: for bindings. |
CLUB_STRICT | off | Whether a binding must be final first: off, on (safe) or finalized. Strict mode explains. |
CLUB_MAX_BINDING_DAYS | 365 | How old a binding's World ID proof may be, in days. |
CLUB_KNOCK_BINDING | Knock's | The KnockBinding whose bindings count. Only for a network of your own. |
Use your own RPCs for a club people use: the public ones are rate-limited, and the checks believe what they say. Its RPCs says more.
What it keeps
For each human who has knocked: their nullifier, their Wedgie's name, and when they came. Then what they started, signed and gave, with their name beside it only where they chose to show it, and the gifts' transactions. A session is a random token in a cookie, which the club keeps only as a hash, for two weeks.
Nothing else: no email addresses, no IP addresses, no analytics. Whoever holds the database can see which nullifier did what, so keep its token as you'd keep a password.