Document 02 of 14 · SETUP.md
Everything here needs the owner's hands, because it is an account, a key, a card or a delete. About an hour. Do it in this order. The domain moved to Vercel and the Lovable project was deleted in the separation session on 2026-09-16; section 7 records what is done and what waits for the end of Phase 1.
The plan, the mockups and every document are a small static site under plan/ in the repo, on main since the merge on 2026-09-16, meant to live at plan.artisanpdfstudio.com while the build is worked through. The live site at www is not touched. Three things make it appear:
artisan-pdf-studio exists, linked to the repo, and builds every push of main as static output with no framework. Framework preset should read Other with no build command; check under Settings, General if a deploy ever tries to run a build.plan.artisanpdfstudio.com is added. Vercel hands out a CNAME target of its own for it, eb700aefa7ca20e2.vercel-dns-016.com; that is the value to use, not the generic cname.vercel-dns.com an older guide would name.plan is a CNAME to that target, grey cloud, and resolves to Vercel.plan while the rebuild lived there, and is main since the merge the same day. ⚠ In the current Vercel dashboard that setting is not on the Git page; it is under Settings, Environments, Production, Branch Tracking. The alternative, had it been wanted, was to leave production on main and set the domain's own Git Branch to plan under Domains. Until one of them is set, the domain serves the project's first-ever deployment (commit 8802533, older than every fix since), because production is main, main has not moved since the project was linked, and every later push to plan builds as a preview that never reaches the domain. After changing the setting, press Redeploy on the latest plan deployment, or push once; the domain then follows the new production deployment. One more thing to look at on the same Domains page: if the domain reads Pending Verification rather than Valid Configuration, Vercel wants a TXT record too, name _vercel, with the value it shows, added in the Cloudflare zone next to the CNAME. That happens when the apex domain was once on a different Vercel account, which the earlier PDF farm attempts make plausible. There was no _vercel record in DNS at the last check, so if the page asks for one it has not been added.vercel.json redirects the plan host to /plan/ and marks every plan page noindex, so nothing here reaches search. A rewrite cannot do this job: Vercel serves a matching file before it looks at rewrites, so / on the subdomain served the root index.html instead of the plan, and short paths like /mockups/ returned 404 (found 2026-09-16). Redirects run before the file check. When the real build goes live, the plan subdomain can stay or go.
To rebuild the plan pages after editing a document: node plan-src/build.mjs, then commit plan/.
brentspore/artisan-pdf-studio. main is the rebuild since 2026-09-16: the plan, the filled memory scaffold, and none of the Lovable app. It got there as a fast-forward (6de0dc6 to 4ef8d36) from the plan branch, which was deleted after the merge along with claude/dazzling-hawking-80yvtz, the branch the plan was written on. The old app is in history before 4ef8d36.91289ef8-5892-4af3-be90-0f766b4d955d) is deleted (owner, 2026-09-16, after www was verified on Vercel). Its Lovable-managed Supabase went with it.ahjsvadwxacschqgdahj) in the Synergy org is an empty 2025 attempt, already paid for; it is kept and becomes the new site's database, reset in step 6 below..env. It held only the publishable Supabase URL and key for the Lovable project, not a secret, and it is gone on main. Never commit one again; the new repo carries .env.example with names only.main, or Lovable would have kept syncing it. The project was deleted the same day, once www served from Vercel (section 7).main (fast-forward, 6de0dc6 to 4ef8d36). Lovable's last synced commit stayed at 6de0dc6 after the push, which is the proof the disconnect held. The old app remains in history if anyone ever wants to look.artisanpdfstudio.com (managed, invisible mode is fine). Note the site key (public) and the secret key.cleanup-expired-templates, create-payment, track-analytics, verify-payment, purchase-template, verify-template-purchase, purchase-any-template, generate-thumbnail, generate-logo, export-to-google-docs, google-auth-init, google-auth-status); under Settings, API create a new secret key for the Vercel functions and note it with the Project URL. The two migrations in supabase/migrations/ (reset, then the orders schema and the private orders bucket) are applied in session one through the connector or the CLI. Once the new site is live and nothing else references the project, disable the legacy anon and service_role keys, because earlier attempts may still hold them. The one auth user can go or stay.artisanpdfstudio.com as a domain and put its three records in Cloudflare (a DKIM TXT at resend._domainkey, an MX and an SPF TXT on the send subdomain; the dashboard shows the values). These records do not affect the website, so they can go in now. Create an API key with sending and contacts permission and a contact list named "Studio"; note its ID. Sending address hello@artisanpdfstudio.com, reply-to a real inbox.| Name | From | Used by |
|---|---|---|
SITE_ORIGIN |
https://www.artisanpdfstudio.com (the domain is on Vercel already) |
every function, for links and <base href> |
SUPABASE_URL |
step 6 (PDF Farm's URL) | checkout, webhook, render, order, draft, subscribe |
SUPABASE_SERVICE_ROLE_KEY |
step 6, the new secret key | same. Never in the HTML. |
STRIPE_SECRET_KEY |
step 7 | checkout, webhook, order |
STRIPE_WEBHOOK_SECRET |
section 7, after the endpoint exists | webhook |
CF_ACCOUNT_ID |
step 4 | render |
CF_BROWSER_RUN_TOKEN |
step 4 | render |
TURNSTILE_SECRET_KEY |
step 5 | draft, subscribe |
RESEND_API_KEY |
step 8 | webhook (receipt), subscribe |
RESEND_LIST_ID |
step 8 | subscribe |
MAIL_FROM |
Artisan PDF Studio <hello@artisanpdfstudio.com> |
webhook, subscribe |
ANTHROPIC_API_KEY |
step 9 | draft |
ASSIST_MONTHLY_CAP |
5000 |
draft |
INTERNAL_KEY |
32 random bytes (openssl rand -base64 32) |
render, and any admin route |
Public values that go in the HTML, not in env: the Turnstile site key, the GA4 measurement ID (in site.js behind the host guard, as on measureandbuy).
Add them for Production and Preview both. Preview deployments use the test Stripe key forever; only Production gets the live one in Phase 2.
These are SPEC.md section 11. The recommendation is in each line; a one-word answer per item is enough.
Node 22 or newer. Chrome (for the shot scripts). poppler (brew install poppler) for pdffonts and pdftotext. The Stripe CLI for replaying webhooks locally. The Supabase CLI is optional if the MCP connector is connected in the session. A home printer with paper in it, because the Studio Standard says every template is printed at 100% before it ships, and that is not a figure of speech.
The separation prompt, run 2026-09-16. Kept as the record of what was pasted; it names the plan branch, which no longer exists, so do not paste it again. It walks through the Lovable disconnect, the merge to main, the domain move (amended 2026-09-16: the owner chose to put the domain on Vercel at separation, not at the end of Phase 1; see DECISIONS.md), the machine tools, section 2 and section 4, and stops before Phase 0.
Pull. This is artisan-pdf-studio on branch `plan`, which Vercel serves at plan.artisanpdfstudio.com. Before doing anything, read `CLAUDE.md`, `SPEC.md` section 0, `SETUP.md` sections 1 to 5 and 7, and `.ai/memory/HANDOFF.md`.
The job for this session: separate this project from Lovable completely, move the build onto `main`, put the domain on Vercel now rather than at the end of Phase 1, and get the machine and the accounts ready for Phase 0. Do not start Phase 0 itself; that is the next prompt in `SETUP.md` section 6, and I will paste it when this is done.
Work in this order. Steps marked (me) are things only I can do: give me the exact click path, then wait for me to say done before going on.
1. Confirm the checkout: `plan` is current, the working tree is clean, `main` still holds the old Lovable app. Search the branch for Lovable, lovable-tagger or gpt-engineer remnants outside the plan documents, which mention Lovable as history and stay. Remove anything you find and tell me.
2. (me) Lovable: in project 91289ef8-5892-4af3-be90-0f766b4d955d, Settings, GitHub, disconnect the repo. Then check whether any Lovable project is attached to the "PDF Farm" Supabase project and disconnect that too. If you have the Lovable connector, confirm the state from it after I say done. Leave the Lovable project published for now; it comes down in step 5, once www serves from Vercel.
3. (me) GitHub: repo Settings, GitHub Apps, remove Lovable's access to this repo.
4. After 2 and 3, make `main` the real branch: `git checkout main`, `git merge --ff-only plan`, `git push origin main`. If it is not a fast-forward, merge with a merge commit. Never force-push `main`. Then (me) in Vercel: Settings, Environments, Production, Branch Tracking, back to `main`. Then delete the `plan` and `claude/dazzling-hawking-80yvtz` branches on origin, confirm through the Vercel connector that the next push to `main` builds with target production, and confirm plan.artisanpdfstudio.com still serves the plan through the host rewrite in `vercel.json`. Change every mention of the `plan` branch in `SETUP.md`, `README.md` and `.ai/memory/` to `main`.
5. The domain, now. (me) In Vercel, add artisanpdfstudio.com and www.artisanpdfstudio.com to the project, www as primary so the apex redirects to it. (me) In Cloudflare, change the apex A record and the www CNAME from Lovable's 185.158.133.1 to the values Vercel shows, grey cloud, DNS only. Then you verify: DNS resolves to Vercel, https://www.artisanpdfstudio.com serves the holding page from `index.html` with its link to the plan, the apex redirects to www, and plan.artisanpdfstudio.com still works. Then (me) delete the Lovable project, which takes its own Supabase with it. Move the finished items out of `SETUP.md` section 7 and leave the Stripe webhook, the legacy Supabase keys, the old Stripe products and Search Console there for the end of Phase 1.
6. Machine: check for Node 22 or newer, Chrome, poppler (`pdffonts`, `pdftotext`), the Stripe CLI and Homebrew. List what is missing with its install command, and install after I say yes.
7. Accounts: `SETUP.md` section 2, items 4 to 10. Where you have a connector (Supabase, Stripe, Resend, Cloudflare), do what it can do and show me what it did; hand me the rest one item at a time and wait. For Supabase, apply the two migrations in `supabase/migrations/` to project ahjsvadwxacschqgdahj only after showing me what the reset drops and getting a yes; every table is empty, but ask anyway. Never print a key into the chat, a file or a commit. Keys go into Vercel environment variables under the names in `.env.example`; you tell me which name each value belongs to.
8. Decisions: ask me the open items in `SPEC.md` section 11 in one batch, with your recommendation on each. Record the answers in `SPEC.md` section 11 and `.ai/memory/DECISIONS.md`.
9. Finish: make sure the project has an entry in `~/.ai/memory/PULSE.md`, update `.ai/memory/HANDOFF.md` with what is done and what is still on me, commit, push, and tell me the state in plain words. Then stop.
Rules: human voice on anything a visitor could read, no em dashes. DNS changes only in step 5, and only to the values Vercel shows. The Lovable project is deleted only in step 5, after www serves from Vercel. Confirm before anything destructive.
Then Phase 0. Paste this into Claude Code in the repo, on main after the merge, once section 2 is done and the env vars are in Vercel:
The memory scaffold in
.ai/memory/is already filled fromSPEC.md; read it andCLAUDE.md, then do Phase 0 fromSPEC.mdsection 9: the site shell and design system fromDESIGN.md(desk, sheet frame, tokens, Newsreader self-hosted withOFL.txt, the mark), the homepage with the hero wired to a placeholder certificate template throughstudio.js, the four legal pages,/help/with "Print at actual size" and "Which paper" written in human voice,robots.txt,sitemap.xml,vercel.jsonwith the old-path redirects fromARCHITECTURE.mdsection 9, the favicon and OG set throughship-assets,site.jswith the GA4 host guard,.env.example, and the Supabase migration fromARCHITECTURE.mdsection 5 applied to the new project. Deploy on push to the Vercel URL. Then drive the deployed site at 1440 and 390 with a sweep the way measureandbuy'ssweep.cjsdoes, measure every contrast pair inDESIGN.mdsection 2, and put the results inHANDOFF.md. Do not build a template yet, and do not touch DNS.
And the second, for Phase 1:
Build the invoice end to end per
SPEC.mdPhase 1 andARCHITECTURE.md: the template package instudio/invoice/, the runtime, the customizer, checkout, webhook, render through Cloudflare Browser Run, storage, the receipt email, the success page, the free PDF, the template page generator, the shot generator and the sweep, all on the Vercel URL. Done means a Stripe test-mode purchase from my phone produces a PDF that opens in Preview and Acrobat with every font embedded, in Letter and A4, the email lands inside a minute, the re-download link works the next day, and a refund from the Stripe dashboard marks the order. Then walk me through the cutover inSETUP.mdsection 7, and after it extract thenew-templateskill the waynew-toolwas extracted from paint.
Done in the separation session, 2026-09-16. artisanpdfstudio.com and www.artisanpdfstudio.com are on the Vercel project, www connected to Production and the apex a 308 to it. Vercel asked for a CNAME on both, including the apex, to its project target eb700aefa7ca20e2.vercel-dns-016.com (the same one plan uses; Cloudflare flattens a CNAME at the root), not an A record. Both records had been proxied (orange cloud) and were switched to DNS only. Verified from a local machine: apex and www resolve to Vercel, www serves index.html from main, the apex redirects to www, plain http upgrades to https, and the plan subdomain still redirects to /plan/. The Lovable project was deleted after that check, and its Supabase went with it. www shows the holding page until Phase 0 replaces it.
Left for the end of Phase 1, about ten minutes:
https://www.artisanpdfstudio.com/api/stripe-webhook for checkout.session.completed, checkout.session.async_payment_succeeded and charge.refunded; put its signing secret in STRIPE_WEBHOOK_SECRET; redeploy; run one test purchase.anon and service_role keys if step 6 of section 2 did not already.sitemap.xml. The eight-week clock starts here.| Session | Delivers | Owner effort |
|---|---|---|
| 0 | Sections 1 to 4 of this document | about an hour |
| 1 | Phase 0: shell, homepage, legal, help, analytics wiring, migration, live at the Vercel URL | pick the tagline, look at the design |
| 2 to 3 | Phase 1: invoice end to end, then what is left of section 7, then the new-template skill |
one test purchase from a phone, one print at 100%, ten minutes of clicks |
| 4 onward | one template per session through the skill, roster order from CATALOG.md; the 2027 calendar and its month pages early, for Q4 |
a print of each, a look at each on a phone |
| when 30 are live | Phase 2 close-out: GSC, live Stripe keys, Pinterest, the first broadcast | switch Stripe to live, create the Pinterest account |
| eight weeks after the sitemap | the first honest read, then Phase 3 decisions | read the numbers with me |