Marketing and onboarding site for LocalNodes, a hosted "knowledge garden" SaaS. The site sells the product, takes payment via Stripe, then provisions a per-customer instance by dispatching a GitHub Actions workflow in the product repo (LocalNodes/os-knowledge-garden) — surfacing live provisioning progress to the buyer along the way.
Built with Nuxt 4, Nuxt UI v4, and Nuxt Content. Deployed on Vercel.
/onboarding ──▶ check-subdomain ──▶ create-checkout ──▶ Stripe Checkout
(form) (Coolify lookup) (subscription) │
▼
/success ◀── provision-status ◀── Redis state ◀── stripe-webhook
(poll 3s) ▲ (triggerProvisioning)
│ │
provision-callback ▼
(workflow ──▶ status) GitHub Actions
(provision-instance.yml)
- Pick a name — the onboarding form debounce-slugifies the community name and checks subdomain availability against the Coolify API (and a reserved list).
- Checkout — submit creates a Stripe subscription Checkout session (community name / subdomain / email stashed in metadata) and redirects to Stripe.
- Webhook — on
checkout.session.completed, the Stripe webhook triggers provisioning: a RedisSETNXlock for idempotency, then a GitHub Actions workflow dispatch. - Progress — the workflow posts status back to a callback endpoint (
triggered → provisioning → installing → creating_user → sending_email → complete | failed). The success page polls every 3s and shows a 4-minute countdown until done.
This project integrates with four external services. You'll need credentials for each:
- Stripe — subscription checkout + webhooks
- Upstash Redis — provisioning state tracking
- GitHub — a fine-grained PAT to dispatch the provisioning workflow on
LocalNodes/os-knowledge-garden - Coolify — API token, used to check subdomain availability
npm install
cp .env.example .env # then fill in the NUXT_* secretsAll secrets are server-only and read via runtimeConfig from NUXT_* environment variables. See .env.example for the full list and where to obtain each value.
npm run dev # dev server at http://localhost:3000
npm run build # production build
npm run preview # preview the production build locally
npm run generate # static generationTo exercise the Stripe webhook locally, forward events with the Stripe CLI:
stripe listen --forward-to localhost:3000/api/stripe-webhookUnit tests run on Vitest (node environment). Server-side business logic lives as pure, dependency-injected functions in server/utils/, so tests mock Redis / dispatch / config rather than booting Nuxt.
npm run test # run all tests once
npx vitest run tests/unit/slugify.test.ts # a single file
npx vitest run -t "returns alreadyProcessing" # filter by name
npx vitest # watch modeapp/
components/ UI sections + onboarding/provisioning components
composables/ useSubdomain, useProvisioningStatus
pages/ index, onboarding, success, cancel
utils/ slugify, validation schemas
server/
api/ endpoints (check-subdomain, create-checkout,
stripe-webhook, provision-status, provision-callback)
utils/ provisioning logic, redis, stripe, github clients
content/ landing page copy (index.yml)
tests/unit/ Vitest specs
See CLAUDE.md for architecture conventions and deeper detail.