# Reader brief — build the smallest honest storefront, and prove it **Use this if:** you are about to pay a platform's monthly rent for a small shop, or you have a storefront and cannot prove the privacy claim in its footer. **Time:** a weekend for a working shop, one more day for the verification. **You need:** a domain, a Node box (a share of one is fine), a fulfilment or payment provider with a server API, and a browser you can drive. This is the build behind *The lightest storefront I could build*: one Node process, one runtime dependency, no build step, self-hosted fonts, and a "no third-party requests" claim that is checked in a browser rather than a grep. The pack has the actual third-party audit, the pricing code, the SMTP mailer, a browser verifier and the platform cost model. ## The prompt ```text You are building the smallest honest storefront that can take a real order. The human sells a small number of physical products through a print-on-demand or fulfilment provider. Optimise for "no fixed rent and nothing on the page that is not ours", not for feature count. PHASE 1 — PRICE THE RENT 1. Build a cost table for the mainstream hosted options: monthly floor, transaction fee, apps and theme, and what each hides (per-placement print charges, payment-gateway surcharge, sales tax). 2. Build the same table for the build path: hosting share, payment processing, provider per-item cost, and the hours of maintenance. 3. State which fixed costs disappear and which do not. Do not claim "free"; claim the shape of the cost. PHASE 2 — BUILD 4. One process, one runtime dependency (the payment SDK or none), no build step. The files on disk are the files the browser runs. 5. Secrets live OUTSIDE the app tree (a vault at 0600), never in the directory a deploy overwrites. 6. Exactly ONE function prices a cart. The quote the customer sees and the amount the processor charges must come from that function; two code paths for one price is the bug. 7. The cart collects only the fields that change the price. Shipping and tax are the provider's numbers, displayed, never invented. 8. The checkout's disabled state is a real, tested state: disabled + aria-disabled + a visible reason + honest meta description. Go-live is a configuration change, not a code edit. 9. The footer's privacy claim names its one exception (the payment page's own script). No analytics, no trackers, no CDN fonts, no cookie banner. PHASE 3 — VERIFY IN A BROWSER, NOT A GREP 10. Load the storefront in a real browser. Record: every request origin, every script/img/link/iframe in the rendered DOM, performance resource entries, console errors, cookies, service workers. 11. Fail the audit if any origin other than your own is contacted (except the named checkout exception), if a cookie is set, or if the rendered DOM contains an unexpected element. 12. Prove the price path: the cart quote and the processor session amount reconcile to the cent. 13. Prove the honest state: with no provider key, the button is disabled and says why; with a key, it is enabled. A deploy cannot flip the claim by accident. 14. Re-run 10–12 on the DEPLOYED host. The edge can inject what your repository does not contain. ACCEPTANCE CHECKS - The storefront page contacts zero third-party origins; the checkout page contacts exactly the payment provider and nothing else. - No Set-Cookie on the public pages; no analytics global in the DOM. - quote == charge, including shipping and tax, for at least three carts. - The disabled checkout is disabled, announced as disabled, and gives the reason. - The vault is outside the deployed tree and mode 0600. - A preview/review page says it is not the live shop. DELIVERABLE The code, the cost table, the browser audit output as JSON, and a "what I could not verify" list. ``` ## Feed it these files | File | What it gives you | |---|---| | `third-party-audit.md` | the real audit: the edge-injected beacon that `curl` never shows, and the fix | | `verify-origins.mjs` | a runnable browser verifier — origins, DOM, resource entries, cookies, console | | `server-excerpts.mjs` | the config posture, session signer, one pricing path and the reconciliation point | | `mailer.mjs` | a 192-line SMTP client on `node:net`/`node:tls` — the "one dependency" reasoning, in code | | `platform-costs.csv` | the 2026 platform floor, fees and hidden costs, with sources | ## What good looks like - [ ] The rendered DOM and the request log both show only your origin on the storefront. - [ ] The one allowed exception is named and scoped to the checkout page — not "some third-party scripts". - [ ] `grep` returning zero is **not** accepted as evidence; the browser audit is the evidence. - [ ] The cart quote and the charged amount reconcile, because both come from one function. - [ ] The billing address is not used as the shipping address; the form collects the destination the quote depends on. - [ ] A provider field the fulfilment API requires (e.g. a phone number) is collected, not discovered after the customer has paid. - [ ] The disabled checkout state passes its own test: text, `disabled`, `aria-disabled`, reason, contrast, meta description. - [ ] The vault is outside the app tree; a redeploy cannot wipe the keys. - [ ] The deployed host is re-audited after every edge change, because an edge default can add a script your repository has never seen. ## Failure modes this piece paid for - **The beacon that only browsers see.** Cloudflare's Web Analytics was injected at the edge into real browser sessions. `curl`, the response body and the local server all showed zero references. The rendered DOM and the request events showed one. **"No third-party code" is a claim about the wire.** A CSP that blocks the script is mitigation, not a fix; switch the injection off and re-measure. - **Two pricing paths.** The cart quoted shipping and tax; the Stripe session charged the bare subtotal. A customer shown $25.33 would have been charged $19.76 and the shop would have eaten shipping on every order. One function, called by both. - **Billing address is not a shipping address.** For a physical order they routinely differ; collect the destination you quote from, and pass the same one to the printer. - **A provider requirement discovered after payment.** The fulfilment API required a phone number. Disabling collection left the order unfulfillable; that failure surfaces only after the charge. - **Collecting tax you cannot remit.** If you are not registered, collecting VAT is worse than not collecting it. Keep the ship-to list short until registration exists. - **A vault inside the deployed tree.** A routine rsync or `rm -rf` destroys the keys. Keep it at `0600` outside the app, and never fall back silently if it is unwritable. - **A preview page that looks live.** The design-review page must say it is not the live shop, in its meta description and its first paragraph. - **Edge defaults that change under you.** The claim was true, then an edge feature made it false, then the fix made it true again. A privacy claim you do not re-measure is marketing. ## Where to go next in the pack Start with `third-party-audit.md` — it is the proof method, and the failure it caught. Then `verify-origins.mjs`: run it against your own page before you write a word of footer copy. Read `server-excerpts.mjs` for the pricing-path and reconciliation patterns. `mailer.mjs` is the argument for writing one small thing instead of taking a second dependency; it is not a deliverability strategy. The one-line test for whether the shop is honest: **can a skeptic with DevTools falsify your claim in ten seconds?** If yes, weaken the claim until they cannot.