Web App · version 1.0
Waiting Lists
A self-hosted waiting-list web app with an included command-line client. Run it from your terminal or let a coding agent create lists, connect forms, inspect subscribers, and export data through the private API.
Why I built this
A waiting list should be one of the smallest pieces of a product launch, but it often turns into a pile of forms, email tools, spreadsheets, consent questions, and glue code. I wanted one focused service that could collect an email safely, prove consent, and stay easy to operate.
Waiting Lists is the real, working result: a public collection endpoint, deliberate double opt-in, a private dashboard, export formats, delivery status, cleanup rules, and a private interface that AI agents can use without putting keys in a project repository.
From form to confirmed subscriber
The whole waiting-list lifecycle is already connected.
- Create a list in the dashboard or included CLI.
- Drop the generated form into any site.
- Confirm ownership through double opt-in.
- Review or export subscribers in the dashboard or terminal.
Features
Double opt-in
Keep new requests pending until the address owner reviews and confirms through a single-use 24-hour link.
Private dashboard
Create lists, inspect confirmed and pending subscribers, review delivery problems, and manage settings.
Drop-in forms
Post email and explicit consent from any approved website using generated HTML or JavaScript.
Included CLI and private API
Create, inspect, update, integrate, and export lists from a terminal or coding agent. The CLI source and its complete guide are in the download.
Useful exports
Download consent archives, universal email files, and Sendy, Kit, or Mailchimp-ready CSVs.
Privacy-minded lifecycle
Minimize collected data, avoid storing IP addresses, support signed removal links, and clean up expired records.
Use cases
Product launches
Give each upcoming product its own endpoint, consent wording, origin rules, and export.
Client projects
Run one private service for several sites while keeping every list separately manageable.
Agent-built sites
Let a coding agent create the list and connect the form through a documented command-line contract.
Self-hosted lead capture
Own the source, deployment, subscriber database, retention policy, and email delivery path.
Email-provider staging
Collect and confirm interest before moving approved addresses into the newsletter system you prefer.
SaaS starting point
Add accounts, organizations, billing, or hosted onboarding to turn the single-owner tool into a service.
Tech stack
Application
Astro
Renders the public site, private dashboard, confirmation flow, and server endpoints.
Runtime
Cloudflare Workers
Runs HTTP requests, the Queue consumer, scheduled cleanup, and static asset delivery.
Persistence
D1
Stores lists, consent records, subscriber state, sessions, API keys, settings, and delivery events.
Transactional email
Email Service
Sends confirmation and removal links from a restricted sender binding.
Interaction
HTMX and Alpine.js
Add focused form and dashboard behavior without a separate client application.
Verification
Vitest
Covers authentication, tokens, API behavior, validation, exports, Turnstile, and email events.
Command line
Node.js CLI
Uses the private API to create, inspect, update, integrate, and export lists without putting credentials in a project repository.
CLI
The ZIP contains the CLI source, package scripts, the authenticated API it calls, and a dedicated CLI.md guide. It runs directly from the downloaded repository. Nothing depends on a separately published CLI package.
npm run waitinglists -- initCreate a list and link it to the current project.
npm run waitinglists -- listsList waiting lists and confirmed subscriber counts.
npm run waitinglists -- integrationPrint the exact endpoint and form markup for a linked list.
npm run waitinglists -- exportSave a private CSV export with restricted file permissions.
Screenshots and demos




Usage manual
Run it and make it yours.
The setup and usage guide from the ZIP. Read it before downloading so you know what the software needs.
Waiting Lists is a self-hosted, email-only waiting-list service with double opt-in, a private dashboard, an agent-ready API and CLI, CSV exports, and privacy-minded retention controls.
Current release: 1.0 (1.0.0 in package metadata). See CHANGELOG.md for release notes.
This download package was created from commit 71bc1c9e86ba04f5fe73d372d8f2f23bc56ed79d dated 2026-07-31. Live credentials, account identifiers, analytics, deployment history, dependencies, and build output are intentionally excluded.
What is included
- Complete Astro and TypeScript source in
code/ - Cloudflare Worker, D1 migrations, Queue consumer, Cron cleanup, rate limits, Turnstile, and Email Service integration
- Admin authentication, waiting-list management, delivery status, and CSV exports
- Public HTML/JSON subscription endpoint with per-list origin controls
- Private JSON API and complete command-line client for terminal use, AI agents, and automation
- Dedicated
CLI.mdguide with setup, command reference, common workflows, and credential handling - Automated tests for authentication, API keys, consent tokens, CSV, email events, country data, Turnstile, and validation
- Human setup instructions here and dedicated instructions for AI coding agents in
AGENTS.md
Human quick start
1. Prerequisites
Install Node.js 22 or newer. Create a Cloudflare account if you want to use the included deployment path.
2. Install and verify
cd code
npm ci
npm run check
npm test
3. Create local settings
cp .dev.vars.example .dev.vars
Replace every placeholder in .dev.vars. Generate separate random values for SESSION_SECRET and SUBSCRIBER_TOKEN_SECRET. Generate ADMIN_PASSWORD_HASH from a strong password:
printf %s 'your-high-entropy-password' | openssl dgst -sha256
4. Prepare the local database
npm run db:migrate:local
npm run dev
Open the local URL shown by Astro, then visit /login and sign in with the administrator credentials from .dev.vars.
5. Create and connect a list
Create a waiting list in the dashboard. Add the exact browser origins that may submit to it. The list page generates ready-to-copy HTML, JavaScript, and an AI-agent integration brief.
6. Use the included CLI
Create an API key at /admin/agent, then follow CLI.md to configure the command-line client without putting the key in shell history.
npm run waitinglists -- help
npm run waitinglists -- init --name "My product" --origin "https://example.com"
The CLI can create, list, inspect, update, integrate, and export waiting lists. It runs from code/cli/waitinglists.mjs and uses the private API included in this package.
Before deploying, continue with CONFIGURATION.md, SECURITY.md, and DEPLOYMENT.md.
Project map
code/src/pages/— public, admin, and API routescode/src/lib/— auth, database, consent, email, API keys, validation, and exportscode/src/worker.ts— Worker entry, Queue consumer, and scheduled cleanupcode/migrations/— D1 schema historycode/cli/— complete command-line client for people, coding agents, and automationcode/wrangler.jsonc— portable Cloudflare configuration with placeholders
Change the stack
The included Astro and Cloudflare implementation is a working reference, not a lock-in contract. The behavior, data model, edge cases, and tests are the blueprint. You can ask an AI coding agent to move the UI, database, email provider, framework, or deployment platform while preserving the product contract described in these documents.
Read AGENTS.md first when handing the package to an AI coding agent.
CLI reference
Commands for terminals, scripts, and coding agents.
The command-line workflows and options included with the software.
The download includes a working Waiting Lists CLI in code/cli/waitinglists.mjs. It uses the included private JSON API, so you can manage waiting lists from a terminal or let a coding agent do it through a small, predictable command set.
The CLI runs directly from this repository. It is not a separately published npm package and does not require a global install.
Before you start
- Install the project and run the database migrations as described in
README.md. - Start the app locally or deploy it to infrastructure you control.
- Sign in to the private dashboard and open
/admin/agent. - Create an API key. Copy it when shown because the app stores only its hash.
Configure the CLI
From code/, pass the key through standard input so it does not appear in shell history:
read -s WAITINGLISTS_API_KEY
printf %s "$WAITINGLISTS_API_KEY" | npm run waitinglists -- configure \
--key-stdin \
--api-url https://waiting-lists.example/api/v1
unset WAITINGLISTS_API_KEY
For local development, omit --api-url. The CLI defaults to http://localhost:4321/api/v1.
The CLI saves credentials to ~/.config/waitinglists/credentials.json with owner-only permissions. You can instead provide WAITINGLISTS_API_KEY and WAITINGLISTS_API_URL as environment variables.
Create and link a list
Run this from the website project that will use the waiting list:
node /path/to/waiting-lists/code/cli/waitinglists.mjs init \
--name "My product" \
--origin "https://example.com"
init creates the list and writes .waitinglists.json in the current directory. This file contains the non-secret list ID, slug, and public collection endpoint. It is safe to commit if your project benefits from sharing that link.
If you run the command from inside the downloaded code/ directory, use the shorter form:
npm run waitinglists -- init --name "My product" --origin "https://example.com"
Commands
waitinglists configure --key-stdin [--api-url URL]
waitinglists init --name NAME --origin URL [--slug SLUG] [--json]
waitinglists create --name NAME --origin URL [--slug SLUG] [--json]
waitinglists lists [--json]
waitinglists show [LIST_ID] [--json]
waitinglists update [LIST_ID] [--name NAME] [--slug SLUG] [--origin URL] [--json]
waitinglists integration [LIST_ID] [--json]
waitinglists export [LIST_ID] [--format archive|universal|sendy|kit|mailchimp] --output FILE
Use npm run waitinglists -- help to print the same reference in the terminal.
Most commands accept a list ID. After init, commands run in the linked project can read the ID from .waitinglists.json instead.
Common workflows
List every waiting list:
npm run waitinglists -- lists
Print the linked list and its subscriber summary as JSON:
npm run waitinglists -- show --json
Get the public endpoint and generated form markup:
npm run waitinglists -- integration
Change the allowed browser origin:
npm run waitinglists -- update --origin "https://www.example.com"
Export confirmed subscribers without printing personal data to the terminal:
npm run waitinglists -- export \
--format universal \
--output confirmed-subscribers.csv
The export command refuses to overwrite an existing file and creates the new file with owner-only permissions.
Coding-agent use
Give your coding agent this package and ask it to read AGENTS.md and CLI.md. The agent can then use --json for structured output, create or inspect a list, fetch its integration markup, and connect the public form.
Do not put an API key in AGENTS.md, .waitinglists.json, prompts, source files, or Git. Configure it once through standard input or expose it to a trusted process through WAITINGLISTS_API_KEY.
Troubleshooting
No API key configuredmeans the credentials file andWAITINGLISTS_API_KEYare both missing.401 Unauthorizedmeans the key is invalid or has been revoked in/admin/agent.No list ID suppliedmeans the current directory has no.waitinglists.json; pass a list ID or runinit.- Origin errors mean the collecting site's exact browser origin is missing from the list configuration.
Architecture
See how the software is put together.
Review the system flow, boundaries, integrations, and replaceable parts before you download it.
Overview
Waiting Lists is an Astro server-rendered application deployed through a Cloudflare Worker. D1 stores lists, subscribers, consent evidence, admin sessions, API keys, settings, and email delivery events. Cloudflare Email Service sends confirmation messages, a Queue reports delivery outcomes, and a Cron Trigger removes expired or over-retained records.
Request flow
- An administrator creates a list and configures allowed browser origins.
- A public form posts exactly
emailandconsentto/api/subscribe/[slug]. - Validation, origin rules, and rate limits run before D1 changes.
- A pending subscriber and hashed one-time confirmation token are stored.
- Email Service sends a confirmation link and a separate removal link.
- The person reviews and confirms; the token is consumed and the subscriber becomes confirmed.
- The dashboard and exports expose confirmed subscribers to the authenticated administrator.
- Queue events update delivery status. Scheduled cleanup removes expired pending requests and old confirmed records.
Main boundaries
src/pages/api/subscribe/[slug].ts— public collection boundarysrc/pages/admin/— private human interfacesrc/pages/api/v1/— private agent/API boundarycli/waitinglists.mjs— included command-line client for people, coding agents, and automationsrc/lib/auth.ts— password, session, cookie, and CSRF rulessrc/lib/subscriber-tokens.ts— confirmation and removal token rulessrc/lib/db.ts— database queries and lifecycle operationssrc/lib/email.tsandemail-events.ts— outbound mail and delivery eventssrc/lib/integration.ts— machine-readable integration contractsrc/worker.ts— HTTP entry, scheduled cleanup, and Queue consumermigrations/— source of truth for the D1 schema
Replaceable infrastructure
Cloudflare is the reference implementation. A port can replace D1 with PostgreSQL, Email Service with another transactional provider, Queues with a job runner, Turnstile with another abuse-control layer, and Workers with any server runtime. Preserve the boundaries and acceptance behavior above rather than translating files line by line.
What’s included
- Waiting Lists version 1.0 with the complete CLI in code/cli/, its private JSON API, and a dedicated CLI guide
- Sanitized source snapshot with production resources, subscriber data, credentials, and analytics removed
- D1 migration history and Cloudflare Worker configuration with portable placeholders
- Public collection endpoint, confirmation and removal flows, private dashboard, API, and CLI
- Cloudflare Email Service, Queue delivery events, Turnstile, rate limits, and scheduled cleanup
- Automated unit tests and production build configuration
- A plain-language human quick start, versioned changelog, and dedicated AI-agent instructions
- Architecture, decisions, customization, configuration, security, deployment, and build guides
- Audited source ZIP included in the free ZIP download
Documentation
The ZIP includes project context for you and your coding agents.
README.md
The starting point: what the software does, prerequisites, local setup, commands, and the shortest path to a working copy.
CHANGELOG.md
The release history and the public changes included in each version.
BUILDING.md
The build story: how the software was made, the difficult parts, how it was verified, and what could come next.
AGENTS.md
Project context and operating rules for Codex, Claude Code, Cursor, and other AI coding agents.
ARCHITECTURE.md
How the major parts fit together, where data flows, and where to make structural changes.
DECISIONS.md
The main technical and product choices, including tradeoffs worth preserving or revisiting.
CUSTOMIZATION.md
A practical map for changing the brand, interface, features, data model, and integrations.
DEPLOYMENT.md
A production checklist covering resources, environment setup, builds, and deployment verification.
CONFIGURATION.md
Every setting and environment variable, where it is used, and how to configure local and production environments.
SECURITY.md
Credential handling, trust boundaries, sensitive data, and checks to run before publishing your version.
CLI.md
Command-line usage, options, common workflows, and automation examples.
Why download this
- Skip the consent, token, session, export, delivery, and cleanup groundwork
- Start with a complete working product instead of a signup-form demo
- Give an AI agent enough context to set it up or rewrite it safely
- Own the subscriber database and deployment decisions
- Keep the public integration tiny while the difficult lifecycle rules stay server-side
- Replace the framework or platform without starting from a blank repository
Customize it
Use the working source as a foundation. Keep it small, change it for your own workflow, or turn it into a different product.
- Grow it into the waitlist product you run for every launch—or white-label for agencies that need double opt-in done right.
- Rebrand pages, emails, and consent copy until it feels like part of the product you are announcing.
- Add teams, billing, webhooks, and newsletter sync when a single list turns into a small SaaS.
- Swap email and database providers behind the existing boundaries without rewriting the core flow.
- Port Astro to the stack your team already ships, keeping the product contract intact.
- Deploy on Workers today, or move to a VPS/container later when your traffic story changes.
MIT license
Every source ZIP includes an MIT LICENSE file. You can use, copy, change, publish, distribute, sublicense, or sell the code, including as part of a commercial product.
Keep the copyright and license notice with copies or substantial portions of the code. Third-party dependencies and assets keep their own licenses.
Keep learning
Solo Lab
Connect a self-hosted waiting list to the work of finding customers and launching your own products.
