Documentation
¶
Overview ¶
Package waitlist registers a viral, points-based waiting-list plugin on a Base app.
The mechanic is one number: an entry's POINTS. Position on the list is the entry's competition rank by points (ORDER BY points DESC, earlier joiners break ties). Points are earned from events — referrals, shares, invites, verified social follows/joins, running hanzod, admin/service boosts — and every award is an append-only row in `waitlist_events`, so a UNIQUE(entry, dedupKey) index is the anti-fraud spine (one follow = one award). There is exactly one place points change: award().
Access is a separate, orthogonal gate: an entry has access when the list is Open, when it was granted access (sticky), or when its points-derived rank falls within AccessCapacity.
Endpoints under /v1/waitlist:
POST /join register an entry, credit a referrer atomically
GET /status one entry's rank, points, per-source breakdown, access
GET /neighborhood the rank +/- window around an entry (the scalable view)
GET /list leaderboard page (top-N; masked emails)
GET /activity recent event feed
POST /track-share award share points (deduped per platform per day)
POST /invite award invite points; credit conversions on join
POST /boost service-authed points boost (superuser/AdminSecret,
e.g. hanzod), a caller-amount award through the seam
POST /award server-to-server award for a VERIFIED event
(social/hanzod), gated by AwardSecret — the seam the
cloud automations connectors call after verifying
GET /export admin-only CSV
Backing storage is Base SQLite collections auto-created on bootstrap. No Redis, no external store: SQL transactions provide atomicity, an index on points provides O(log n) neighborhood seeks.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func MustRegister ¶
MustRegister installs the waitlist plugin on the given app and panics on error. Suitable for a Base process's startup wiring.
func Register ¶
Register installs the points-based waitlist plugin.
On OnBootstrap it auto-creates/upgrades three collections (`waitlists`, `waitlist_entries`, `waitlist_events`) and seeds any configured default waitlists. On OnServe it mounts the REST surface under /v1/waitlist. The plugin owns its collections (no public CRUD); the dashboard remains available to superusers.
Types ¶
type Config ¶
type Config struct {
// Enabled toggles the whole plugin. A zero-value Config is disabled;
// callers must set Enabled:true explicitly to opt in.
Enabled bool
// CollectionPrefix lets multiple waitlist plugins coexist on one Base.
// Default empty -> `waitlists`, `waitlist_entries`, `waitlist_events`.
CollectionPrefix string
// TurnstileSecret enables Cloudflare Turnstile verification on /join.
// Resolved at boot from TURNSTILE_SECRET_KEY if empty.
TurnstileSecret string
// JoinRateLimit caps /join (and other public writes) by source IP. Zero ->
// default 5/window. Negative disables.
JoinRateLimit int
// JoinRateWindow is the sliding window for JoinRateLimit. Zero -> 1h.
JoinRateWindow time.Duration
// AdminSecret guards /export and the service-authed /boost. Resolved from
// WAITLIST_ADMIN_SECRET if empty. If still empty, those endpoints require a
// superuser session (else 404).
AdminSecret string
// AwardSecret guards the server-to-server POST /award. Resolved from
// WAITLIST_AWARD_SECRET if empty. If still empty, /award is disabled (404)
// — a verified-event award can never be forged by a public client.
AwardSecret string
// Points is the award schedule. Zero values resolve to sane defaults /
// the POINTS_* environment.
Points PointValues
// InviteMaxBatch caps emails per /invite call. Zero -> 50.
InviteMaxBatch int
// DisposableDomains, if non-nil, replaces the built-in disposable
// e-mail blocklist. Empty slice disables blocking.
DisposableDomains []string
// DefaultSlugs are waitlist slugs seeded on bootstrap. Each becomes a
// waitlist row (name = slug with its first letter upper-cased) if absent.
// Resolved at boot from WAITLIST_DEFAULT_SLUGS (comma-separated). Empty
// seeds nothing.
DefaultSlugs []string
// AccessCapacity auto-grants product access to the top-N entries by rank.
// Resolved at boot from WAITLIST_ACCESS_CAPACITY. Zero (default) grants
// none automatically.
AccessCapacity int
// Open is the master switch that ends the waitlist: when true EVERYONE
// has access. Resolved at boot from WAITLIST_OPEN (1/true/yes,
// case-insensitive). Default false.
Open bool
}
Config controls plugin registration.
type PointValues ¶
type PointValues struct {
Referral int // someone joins via your refCode
InviteSent int // per valid email you invite
InviteConverted int // an invited email actually joins
Social int // a verified social follow/join (X, Discord, ...)
Hanzod int // running a hanzod node (verified)
Signup int // a plain signup (default 0)
}
PointValues is the award schedule — the currency the business controls. Every value is overridable from the environment so a consumer can tune the economy without a redeploy of the plugin.