Go live
Your Claude Code setup that builds, launches, and learns — across the whole product lifecycle. Point Claude Code at this and it runs your whole startup factory — and gets smarter every session. Works with Cursor, Codex & Windsurf too.
npx -y skills add hamza-ali-shahjahan/hamzaish --skill go-liveAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Guided, stateful provisioning of a product's production stack — deep-links, key-format validation, a secrets backend (fnox recommended, or user-touched .env.local), a resumable ledger, CLI automation after signup, and a blocking A1–A10 live gate. Hands off to /security-check → /ship. The walked version of SETUP.md.
SKILL.md
13.4 KB, ~3.6k tokens by cl100k_base, as published. Nobody here has run it
/go-live
Usage: /go-live <slug>
The guided provisioning flow that takes a local-first product (built with /builder-mode, running with zero accounts) to a production-ready one — then hands off to deploy. It operationalizes templates/product-starter-nextjs/SETUP.md (the service list) and factory/playbooks/ai-native-2026/go-live-provisioning.md (the env-var → service map), but walked, validated, and resumable instead of a static checklist.
Where it sits in the chain:
/builder-mode (build local) → /go-live (wire the stack) → /security-check (gate) → /ship (deploy) → /web-launch (verify the launch).
Preconditions
- Resolve the product:
$ARGUMENTSis the slug. If empty, ask (orRead products/_portfolio.md). - Resolve the code path from
products/<slug>/product.config.json→code_path(maps viacode-paths.local.json). All provisioning happens in that product's code repo — that's where secrets get wired. - Pick the secrets backend (see next section). Default recommendation: fnox — no plaintext secrets file on disk. Fallback: user-touched
.env.local. Either way, Claude never creates, reads, or writes a plaintext secrets file itself (hook-enforced), and never writes secrets to anything tracked.
Secrets backend — fnox (recommended) vs .env.local (fallback)
Recommend fnox first (jdx/en.dev — brew install fnox). It removes the root cause of the 2026-07-03 leak: with fnox there is no plaintext .env.local to be harness-watched. fnox.toml holds only age/KMS ciphertext or remote-provider references (1Password, AWS/GCP/Azure secret managers, Vault, Doppler, …) and is safe to commit. The app runs via fnox exec -- <cmd>, which injects secrets into the child process env only at run time.
Agents access secrets only through fnox's MCP server, exec-only. The product repo's .mcp.json runs fnox mcp with [mcp] tools = ["exec"]; its exec tool redacts resolved values from stdout/stderr, so printenv/echo $SECRET return [REDACTED] in the transcript.
Honest threat model (red-teamed 2026-07-04 — see brain/decision-log/2026-07-04-fnox-secrets-backend.md). fnox's MCP redaction is literal string-matching. It closes the accidental-leak class (the watcher-echo / printenv incident) — which is the one that actually bit us — but not adversarial exfiltration (base64/reverse/write-to-file all recover the value). Layer these, weakest to strongest:
- The real boundary — key out of agent reach. This is the only layer robust against a determined/injected agent. Prefer a remote provider (1Password/KMS) whose auth the agent can't complete, so no local key exists. A local age key must live outside the repo and unreadable to the agent (reading it fully decrypts
fnox.toml). - Shell deny-rules (best-effort). The committed
.claude/settings.jsondenies allfnoxat the agent's shell (Bash(fnox),Bash(fnox:*)) — the agent has no legitimate shell use of fnox; it goes through MCP. Note this is best-effort, not a security boundary: Claude Code deny-rules are bypassable (option injection, env-var prefixes,eval, absolute paths — per the permissions docs). It stops the naive path, not a determined one. tools = ["exec"]+ explicitsecretsallowlist infnox.toml— never the permissive default; blocksget_secret/getthrough MCP.guard-secrets-files.shstays as defense-in-depth.- JSON config caveat:
.claude/settings.jsonand.mcp.jsonuse strict validation — no_commentkeys (an unknown key rejects the whole file, silently dropping the deny-rules). Document rationale outside the JSON.
Setting a secret (the user runs this themselves, value never in chat): fnox set STRIPE_SECRET_KEY --provider age (prompts for the value). Verify non-printing: fnox list (names only) or fnox check.
Fallback — .env.local: if the user declines fnox, use the user-touched flow below. Confirm there's an .env.local (test -s .env.local); if missing, the user creates it (cp .env.local.example .env.local) and pastes keys themselves. .env.local is gitignored.
The state ledger (resume)
Maintain .hamzaish-go-live.json in the product's code repo (gitignored — add to .gitignore if absent). Shape:
{ "updated": "<date>", "services": { "supabase": "done", "stripe": "skipped", "resend": "pending", "...": "..." } }
On every run: read it, skip services already done/skipped, resume at the first pending. State values: pending · done · skipped · later. This is what makes /go-live resumable across sessions — never re-ask a settled service.
The guided loop
Group services into required-to-deploy first, optional second. For each not-yet-settled service, do exactly this:
- Explain in one line what it's for and the cheapest skip ("no payments yet? skip Stripe").
- Open the deep-link (print it; the user signs up — you can't create accounts for them).
- Name exactly what to copy from where — the key's VALUE never enters chat and Claude never touches a secrets file. Store it — steps 3–4 depend on the chosen backend:
- fnox (recommended): tell the user to run
fnox set STRIPE_SECRET_KEY --provider age(or their provider) — value omitted, so fnox prompts with hidden input (never in chat, never in shell history). First run:cp fnox.toml.example fnox.tomland set the provider. .env.local(fallback): give the exact line to add, e.g. "in your editor, addSTRIPE_SECRET_KEY=<the sk_test_… key you just copied>to.env.local". First run: the user creates it withcp .env.local.example .env.local.
- fnox (recommended): tell the user to run
- User stores the key themselves (fnox prompt, or paste into
.env.local) and says "done". - Validate presence + format with NON-PRINTING checks only — boolean/count/names output, never the value:
- fnox:
fnox list(names + ciphertext only, no plaintext) andfnox check(health verdict). Neverfnox get/export/exec— those return raw values and are deny-listed for the agent anyway. .env.local:grep -qE '^STRIPE_SECRET_KEY=sk_(live|test)_' .env.local && echo OK(table below). Never Read/Write/Edit orcatit (theguard-secrets-files.shhook blocks it anyway — incident 2026-07-03: a Claude-created.env.localgot harness-watched and the user's pasted keys were echoed into the transcript; both keys rotated).
- fnox:
- Mark the service
donein the ledger. Offerskip/laterat every step (records the choice, moves on).
Service catalog (deep-links · keys · validation)
| Service | For | Signup | Env vars | Key format check |
|---|---|---|---|---|
| Supabase (req. to leave local mode) | DB + auth | supabase.com/dashboard → New project | NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY | URL https://<ref>.supabase.co; keys are JWTs (eyJ…) |
| Vercel + Domain (req. to deploy) | hosting + DNS | vercel.com → import repo; cloudflare.com for the domain | (Vercel project; NEXT_PUBLIC_APP_URL) | APP_URL is a valid https URL |
| Stripe | payments | dashboard.stripe.com (test mode first) | STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | sk_(live|test)_…, whsec_…, pk_(live|test)_… |
| Resend | resend.com → verify domain | RESEND_API_KEY, RESEND_FROM_EMAIL | re_…; FROM is an email | |
| PostHog | analytics | posthog.com → new project | NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_POSTHOG_HOST | phc_… |
| Sentry | errors | sentry.io → new Next.js project | NEXT_PUBLIC_SENTRY_DSN, SENTRY_ORG, SENTRY_PROJECT, SENTRY_AUTH_TOKEN | DSN https://…@…ingest.sentry.io/… |
| GA4 / Plausible | web analytics | analytics.google.com / plausible.io | NEXT_PUBLIC_GA4_MEASUREMENT_ID, NEXT_PUBLIC_PLAUSIBLE_DOMAIN | GA4 G-… |
| GSC / Ahrefs | SEO | search.google.com/search-console / ahrefs.com/webmaster-tools | NEXT_PUBLIC_GSC_VERIFICATION (Ahrefs = DNS verify, no key) | meta content string |
| DataForSEO (optional) | keyword data | dataforseo.com | DATAFORSEO_LOGIN, DATAFORSEO_PASSWORD | non-empty |
Automate after signup (the part that is fast)
Account creation is manual; everything after the key exists, automate — with explicit confirmation before each mutation:
- Vercel:
vercel link, then push the local env up:vercel env add <NAME> production(orvercel env pull/addin a loop).vercel domains add <domain>. - GitHub:
gh secret set <NAME>for CI secrets. - Cloudflare: DNS records via API if a token is provided.
State plainly what you can't do: you can't sign the user up, accept their ToS, or enter their card.
Verify, then hand off
When the required-to-deploy set is done:
- Verify: every required var is present and well-formed (re-run the format checks). On fnox:
fnox check+fnox list(names only). On.env.local: non-printinggrep -qEchecks. ConfirmLOCAL_MODEwill now be off (Supabase set). - Verify the server is actually up before sharing any localhost link (global rule: never hand over a dead localhost link). If the product uses pitchfork,
pitchfork start web(idempotent) thenpitchfork status web— or curl the port for a 2xx — before emitting the URL. pitchfork keeps the server supervised across sessions, so the link stays live. Multiple products: distinct ports (a readiness check confirms "something answers," not "this server" — a collision can false-positive). - Offer the handoff: "Stack wired. Run
/security-check <slug>(gate), then/ship <slug>(deploy)?" —/go-liveprovisions;/shipdeploys. Don't deploy from here.
The live gate — A1–A10 (blocking; go-live is not done at handoff)
Provisioned-and-shipped is still only declared live. /go-live closes when the
product is provably live: after /ship reports deployed, run the assertion
harness from factory/playbooks/ai-native-2026/go-live-provisioning.md against
the real production URL:
bun ${HAMZAISH_ROOT:-$HOME/Claude/Hamzaish}/scripts/verify-live.ts https://<domain> \
--sha <deployed short-sha> [--authed-route </api/…>] [--resend-domain <domain>]
It checks A1–A10 (DNS apex+www, TLS on both, /api/health ok + buildSha + db
probe, auth gate 401s, pk_live_ not dev-mode, cron gated, no server-secret in
the client payload) and emits a scorecard — EVAL: n/N, per-assertion
PASS/FAIL/PENDING/MANUAL with remediation.
A11 — Trackable (MANUAL until verify-live.ts learns it; blocking like the
rest). A product that isn't trackable isn't shareable — if nobody can answer
"which screens do users visit and what do they do there?", user data is being
lost from the first shared link, and it's the founder who ends up noticing
(it happened on every product until this gate existed). Check on the LIVE site:
- Every screen/tab/mode reports its own path — SPAs need virtual
page_views or analytics shows one "/" row forever. - The spec's Trackability events fire on the core flows (dataLayer or GA4 Realtime while you click through).
- Every app in the product carries the tag (grep the measurement id in each
deployed
index.html— main site AND any subpath tool). - Third-party params are registered (GA4 custom dimensions) or the founder has the clickable steps to do it.
- The first-party events table receives the same events (the dual-sink helper —
/user-analyticsRecipe F + Definition of Done #7).
Rules:
- Any FAIL → not live. Fix, re-run; exit 1 blocks. Never close the ledger or tell the user "you're live" over a failing scorecard.
- PENDING/MANUAL are named, never dropped (e.g. "A8 PENDING: Resend DNS propagating, recheck in 1h" — the A7b throwaway-signup stays a human step).
- Record the verdict in the ledger (
"live_gate": "EVAL: 9/9 …"+ date), and append every FAIL tobrain/learnings/with assertion id + cause + fix — recurring misses get promoted into the playbook as pre-checks. - Products scaffolded before the health convention report A3/A4/A6 as PENDING —
the starter now ships
/api/health(ok,buildSha,probes.db); backfill the route when touching an older product.
Honest rules
- Never commit a real key. Only
.env.local(gitignored) or the platform's secret store. The.env.examplestays placeholders-only. - Secrets files are user-touched only. Claude edits only
.exampletemplates; the user copies + pastes their own keys; verification is non-printing (grep -q/-c,test). Key values never enter chat in either direction. Enforced machine-wide by~/.claude/hooks/guard-secrets-files.sh; seebrain/anti-patterns/claude-touched-secrets-file.md(incident 2026-07-03). - Record skips honestly in the ledger; a skipped service is a deliberate choice, not a gap.
- This flow lives at
factory/skills/go-live/SKILL.md— the single source for/go-live(user-invoked;disable-model-invocationkeeps its description out of session context, and CLAUDE.md's command table is the router).
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.