[object Object]

← back to Pattern Vault

setup guide page at /setup.html — live status pills, copy-paste steps for the 3 gated switches; stays behind Basic Auth even when PUBLIC_VAULT=1

8c2d2637e49a2eb3d14d62c822425dd7cd43e5c9 · 2026-07-02 07:27:24 -0700 · Steve Abrams

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Files touched

Diff

commit 8c2d2637e49a2eb3d14d62c822425dd7cd43e5c9
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jul 2 07:27:24 2026 -0700

    setup guide page at /setup.html — live status pills, copy-paste steps for the 3 gated switches; stays behind Basic Auth even when PUBLIC_VAULT=1
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---
 public/index.html |   2 +-
 public/setup.html | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 server.js         |   4 +--
 3 files changed, 104 insertions(+), 3 deletions(-)

diff --git a/public/index.html b/public/index.html
index 57e92f4..93a7c57 100644
--- a/public/index.html
+++ b/public/index.html
@@ -45,7 +45,7 @@ footer a{color:var(--gold)}
   <label>Density <input type="range" id="density" min="2" max="6" value="4"></label>
 </div>
 <div class="grid" id="grid"></div>
-<footer>Printed wallpaper of these designs → <a href="https://wallpapersback.com" rel="noopener noreferrer">wallpapersback.com</a> · Every design is an original from our studio pipeline, seam-verified and print-clean at 150 DPI.</footer>
+<footer>Printed wallpaper of these designs → <a href="https://wallpapersback.com" rel="noopener noreferrer">wallpapersback.com</a> · Every design is an original from our studio pipeline, seam-verified and print-clean at 150 DPI. · <a href="/setup.html">setup guide</a></footer>
 <dialog id="dlg"><div id="dlgTitle" class="t"></div><div class="s" id="dlgTier"></div>
   <input id="email" type="email" placeholder="your email">
   <button id="goBtn">Continue</button><div class="note" id="dlgNote"></div></dialog>
diff --git a/public/setup.html b/public/setup.html
new file mode 100644
index 0000000..e3c469e
--- /dev/null
+++ b/public/setup.html
@@ -0,0 +1,101 @@
+<!doctype html>
+<html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>Pattern Vault — Setup Guide</title>
+<style>
+:root{--bg:#0d0b10;--card:#17141d;--ink:#efe9f7;--mut:#9a8fb0;--gold:#d4af6a;--line:#2a2436;--ok:#6fcf8f;--warn:#e0a458}
+*{box-sizing:border-box;margin:0}
+body{background:var(--bg);color:var(--ink);font:15px/1.6 "Avenir Next",system-ui,sans-serif;padding-bottom:80px}
+header{padding:48px 6vw 24px;text-align:center;border-bottom:1px solid var(--line)}
+h1{font-size:clamp(24px,4vw,42px);font-weight:600;letter-spacing:.12em;text-transform:uppercase}
+h1 em{color:var(--gold);font-style:normal}
+.tag{color:var(--mut);font-style:italic;margin-top:8px}
+main{max-width:820px;margin:0 auto;padding:0 6vw}
+.status{display:flex;gap:12px;justify-content:center;flex-wrap:wrap;padding:22px 0}
+.pill{padding:6px 16px;border-radius:99px;border:1px solid var(--line);font-size:13px;letter-spacing:.04em}
+.pill.on{border-color:var(--ok);color:var(--ok)}
+.pill.off{border-color:var(--warn);color:var(--warn)}
+section{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:26px 28px;margin:22px 0}
+section.done{border-color:var(--ok)}
+h2{font-size:17px;letter-spacing:.06em;margin-bottom:4px}
+h2 .num{color:var(--gold);margin-right:8px}
+.why{color:var(--mut);font-size:13.5px;margin-bottom:14px}
+ol{padding-left:22px;display:grid;gap:9px}
+li strong{color:var(--gold);font-weight:600}
+code,pre{background:var(--bg);border:1px solid var(--line);border-radius:6px;font:13px/1.5 ui-monospace,Menlo,monospace}
+code{padding:2px 7px}
+pre{padding:12px 14px;margin:6px 0;overflow-x:auto;position:relative}
+.copy{position:absolute;top:8px;right:8px;background:transparent;border:1px solid var(--line);color:var(--mut);border-radius:6px;padding:3px 10px;font-size:11px;cursor:pointer}
+.copy:hover{border-color:var(--gold);color:var(--gold)}
+a{color:var(--gold)}
+.gate{display:inline-block;margin-left:8px;padding:2px 10px;border:1px solid var(--warn);border-radius:99px;color:var(--warn);font-size:11px;letter-spacing:.06em;vertical-align:middle}
+.check{float:right;font-size:20px}
+.mut{color:var(--mut)}
+footer{text-align:center;color:var(--mut);font-size:13px;margin-top:36px}
+</style></head><body>
+<header>
+  <h1>Vault <em>Setup</em> Guide</h1>
+  <div class="tag">Three switches stand between here and selling licenses — all yours to flip</div>
+</header>
+<main>
+  <div class="status" id="status"><span class="pill">checking status…</span></div>
+
+  <section id="secStripe">
+    <h2><span class="num">1</span>Turn on TEST-mode checkout <span class="check" id="chkStripe"></span></h2>
+    <div class="why">Right now clicking a license tier only records an <em>inquiry</em>. With a Stripe <strong>test</strong> key installed, the same click opens a real Stripe checkout page in TEST mode — no real money can move. The server refuses live keys outright.</div>
+    <ol>
+      <li>Open <a href="https://dashboard.stripe.com/test/apikeys" target="_blank" rel="noopener noreferrer">dashboard.stripe.com/test/apikeys</a> (log in if asked).</li>
+      <li>Make sure the <strong>Test mode</strong> toggle in the top-right of the Stripe dashboard is <strong>ON</strong> (orange).</li>
+      <li>Under <strong>Standard keys</strong>, find <strong>Secret key</strong> and click <strong>Reveal test key</strong>. It starts with <code>sk_test_</code>.</li>
+      <li>Copy it, then either <strong>paste it to Claude</strong> in any session (it gets routed via the secrets skill automatically), or add it by hand:
+        <pre><button class="copy">copy</button>echo 'STRIPE_SECRET_KEY=sk_test_PASTE_KEY_HERE' >> ~/Projects/pattern-vault/.env</pre></li>
+      <li>Restart the app:
+        <pre><button class="copy">copy</button>pm2 restart pattern-vault</pre></li>
+      <li>Reload this page — the badge above should flip to <strong>checkout: TEST mode</strong>. Try a purchase with Stripe's test card <code>4242 4242 4242 4242</code>, any future date, any CVC.</li>
+    </ol>
+  </section>
+
+  <section id="secPublic">
+    <h2><span class="num">2</span>Open the vault to the public <span class="check" id="chkPublic"></span></h2>
+    <div class="why">The whole site sits behind Basic Auth (<code>admin / DW2024!</code>) until you flip this. Flip it only when the catalog and checkout look right to you.</div>
+    <ol>
+      <li>Add the switch to the env file:
+        <pre><button class="copy">copy</button>echo 'PUBLIC_VAULT=1' >> ~/Projects/pattern-vault/.env</pre></li>
+      <li>Restart:
+        <pre><button class="copy">copy</button>pm2 restart pattern-vault</pre></li>
+      <li>Open <a href="/">the storefront</a> in a private/incognito window — it should load with <strong>no password prompt</strong>. (This setup page stays password-protected either way.)</li>
+      <li class="mut">To close it again, change the line to <code>PUBLIC_VAULT=0</code> and restart.</li>
+    </ol>
+  </section>
+
+  <section id="secKits">
+    <h2><span class="num">3</span>Marketplace uploads <span class="gate">STEVE-GATED</span></h2>
+    <div class="why">Ready-to-upload listing kits already exist for three marketplaces — titles, descriptions, tags, and preview images for all 24 designs. Nothing uploads until you create the accounts and say go.</div>
+    <ol>
+      <li>The kits live in <code>~/Projects/pattern-vault/kits/</code> — one folder each for <strong>Spoonflower</strong>, <strong>Etsy</strong>, and <strong>Creative Market</strong>, each with a <code>listings.csv</code>.</li>
+      <li>Preview images are in <code>kits/_images/</code> (web-res). The real masters — 150-DPI print-clean tiles — live on Kamatera prod; pulling those down is a separate gated step, done at upload time.</li>
+      <li>When you want to proceed: create the marketplace account(s), then tell Claude <em>"upload the &lt;marketplace&gt; kit"</em> — the approval memo is at <code>pending-approval/abramsego-golive-260701.md §5</code>.</li>
+    </ol>
+  </section>
+
+  <footer>Live status refreshes on page load from <code>/api/config</code> · <a href="/">← back to the vault</a></footer>
+</main>
+<script>
+document.querySelectorAll('.copy').forEach(b=>b.onclick=()=>{
+  navigator.clipboard.writeText(b.parentElement.textContent.replace('copy','').trim());
+  b.textContent='copied!';setTimeout(()=>b.textContent='copy',1400)});
+(async()=>{
+  try{
+    const c=await(await fetch('/api/config')).json();
+    const pills=[
+      `<span class="pill ${c.checkoutReady?'on':'off'}">checkout: ${c.checkoutReady?'TEST mode ✓':'inquiries only — needs test key'}</span>`,
+      `<span class="pill ${c.publicVault?'on':'off'}">vault: ${c.publicVault?'PUBLIC ✓':'private (Basic Auth)'}</span>`,
+      `<span class="pill off">marketplaces: kits ready — gated</span>`];
+    document.getElementById('status').innerHTML=pills.join('');
+    document.getElementById('chkStripe').textContent=c.checkoutReady?'✅':'⬜';
+    document.getElementById('chkPublic').textContent=c.publicVault?'✅':'⬜';
+    if(c.checkoutReady)document.getElementById('secStripe').classList.add('done');
+    if(c.publicVault)document.getElementById('secPublic').classList.add('done');
+  }catch(e){document.getElementById('status').innerHTML='<span class="pill off">status unavailable — is the server running?</span>'}
+})();
+</script></body></html>
diff --git a/server.js b/server.js
index 186eb8c..c5ef64c 100644
--- a/server.js
+++ b/server.js
@@ -14,9 +14,9 @@ const PASS = process.env.VAULT_PASS || 'DW2024!';
 const PUBLIC_VAULT = (process.env.PUBLIC_VAULT || '0') === '1'; // Steve's switch — never set here
 const DATA = path.join(__dirname, 'data');
 
-// Basic Auth (whole site until PUBLIC_VAULT=1)
+// Basic Auth (whole site until PUBLIC_VAULT=1; /setup.html stays gated always — admin-only guide)
 app.use((req, res, next) => {
-  if (PUBLIC_VAULT) return next();
+  if (PUBLIC_VAULT && req.path !== '/setup.html') return next();
   const h = req.headers.authorization || '';
   const [u, p] = Buffer.from(h.split(' ')[1] || '', 'base64').toString().split(':');
   if (u === USER && p === PASS) return next();

← 63bed8a signup handoff: Spoonflower=existing login, 3 marketplaces=S  ·  back to Pattern Vault  ·  signup tooling snapshot: browserbase step-driver (bb.js/step 65485ea →