[object Object]

← back to Secrets Manager

auto-save: 2026-07-07T08:56:34 (2 files) — registry.json stage/

a9ae69ef75ca5de8e1a2b57ddffec35f49c42cfa · 2026-07-07 08:56:35 -0700 · Steve Abrams

Files touched

Diff

commit a9ae69ef75ca5de8e1a2b57ddffec35f49c42cfa
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Jul 7 08:56:35 2026 -0700

    auto-save: 2026-07-07T08:56:34 (2 files) — registry.json stage/
---
 registry.json              | 68 ++++++++++++++++++++++++++++++++++++++++++++
 stage/swap-prod-bb-key.mjs | 70 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 138 insertions(+)

diff --git a/registry.json b/registry.json
index 016b268..19e74cf 100644
--- a/registry.json
+++ b/registry.json
@@ -1199,6 +1199,74 @@
         "/Users/macstudio3/Projects/dw-vendor-microsites/.env",
         "ssh:root@45.61.58.125:/root/Projects/dw-vendor-microsites/.env"
       ]
+    },
+    "TWOCAPTCHA_API_KEY": {
+      "digest": "9c9b:bcd6b513",
+      "label": "TWOCAPTCHA_API_KEY",
+      "last_updated": "2026-07-03T16:33:49.770Z",
+      "validated": false,
+      "verify_status": null,
+      "written_to": [
+        "/Users/macstudio3/Projects/secrets-manager/.env",
+        "/Users/macstudio3/.claude/skills/2captcha/.env",
+        "/Users/macstudio3/.claude/skills/browserbase/.env"
+      ]
+    },
+    "DEDAR_LOGIN_URL": {
+      "digest": ".php:6c4461bf",
+      "label": "Dedar trade-portal login_url (pulled from Kamatera dw-price-stock 2026-07-06, supervised outbound-tier)",
+      "last_updated": "2026-07-07T00:23:27.474Z",
+      "validated": false,
+      "verify_status": null,
+      "written_to": [
+        "/Users/macstudio3/Projects/secrets-manager/.env",
+        "/Users/macstudio3/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/.env.local",
+        "/Users/macstudio3/Projects/secrets-manager/.env"
+      ]
+    },
+    "DEDAR_USERNAME": {
+      "digest": ".com:3c24357d",
+      "label": "Dedar trade-portal username (pulled from Kamatera dw-price-stock 2026-07-06, supervised outbound-tier)",
+      "last_updated": "2026-07-07T00:23:27.476Z",
+      "validated": false,
+      "verify_status": null,
+      "written_to": [
+        "/Users/macstudio3/Projects/secrets-manager/.env",
+        "/Users/macstudio3/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/.env.local",
+        "/Users/macstudio3/Projects/secrets-manager/.env"
+      ]
+    },
+    "DEDAR_PASSWORD": {
+      "digest": "911*:7a90a92f",
+      "label": "Dedar trade-portal password (pulled from Kamatera dw-price-stock 2026-07-06, supervised outbound-tier)",
+      "last_updated": "2026-07-07T00:23:27.477Z",
+      "validated": false,
+      "verify_status": null,
+      "written_to": [
+        "/Users/macstudio3/Projects/secrets-manager/.env",
+        "/Users/macstudio3/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/.env.local",
+        "/Users/macstudio3/Projects/secrets-manager/.env"
+      ]
+    },
+    "WALLPAPERSBACK_CREATIVEMARKET_PW": {
+      "digest": "Jzt9:386a64e7",
+      "label": "WALLPAPERSBACK_CREATIVEMARKET_PW",
+      "last_updated": "2026-07-07T14:03:46.296Z",
+      "validated": false,
+      "verify_status": null,
+      "written_to": [
+        "/Users/macstudio3/Projects/secrets-manager/.env"
+      ]
+    },
+    "WALLPAPERSBACK_ETSY_PW": {
+      "digest": "kDha:e4f5a224",
+      "label": "WALLPAPERSBACK_ETSY_PW",
+      "last_updated": "2026-07-07T14:37:26.652Z",
+      "validated": false,
+      "verify_status": null,
+      "written_to": [
+        "/Users/macstudio3/Projects/secrets-manager/.env"
+      ]
     }
   }
 }
\ No newline at end of file
diff --git a/stage/swap-prod-bb-key.mjs b/stage/swap-prod-bb-key.mjs
new file mode 100644
index 0000000..6b304c1
--- /dev/null
+++ b/stage/swap-prod-bb-key.mjs
@@ -0,0 +1,70 @@
+#!/usr/bin/env node
+// One-shot: swap Kamatera PROD all-designerwallcoverings onto its DEDICATED Browserbase key.
+// Secrets are read from a chmod-600 file (BB_STAGE_FILE), NEVER from argv/command line.
+//
+//   printf 'BROWSERBASE_API_KEY=bb_live_NEW\nBROWSERBASE_PROJECT_ID=NEWUUID\n' > stage/new-bb-prod.env
+//   chmod 600 stage/new-bb-prod.env
+//   node stage/swap-prod-bb-key.mjs
+//
+// Does: validate new key vs Browserbase API -> write prod .env via ssh+tee (stdin) ->
+//       pm2 restart --update-env -> one live verify scrape -> confirm old key gone.
+import fs from 'node:fs';
+import https from 'node:https';
+import { spawnSync } from 'node:child_process';
+
+const HOST = '45.61.58.125', USER = 'root';
+const PROD_ENV = '/root/Projects/all-designerwallcoverings/.env';
+const PM2 = 'all-designerwallcoverings';
+const STAGE = process.env.BB_STAGE_FILE || new URL('./new-bb-prod.env', import.meta.url).pathname;
+const VERIFY_SKU = process.env.VERIFY_SKU || 'DWQW-56261';
+const last4 = v => '…' + String(v).slice(-4);
+
+function parseEnv(txt) { const kv={}; for (const l of txt.split('\n')) { const m=l.match(/^([A-Z0-9_]+)=(.*)$/); if(m) kv[m[1]]=m[2].replace(/^["']|["']$/g,''); } return kv; }
+function ssh(cmd, stdin) { return spawnSync('ssh', ['-o','StrictHostKeyChecking=accept-new',`${USER}@${HOST}`, cmd], { input: stdin, encoding: 'utf8' }); }
+function bbVerify(key) { return new Promise(res => { const r=https.request('https://api.browserbase.com/v1/projects',{headers:{'x-bb-api-key':key}},x=>{let b='';x.on('data',d=>b+=d);x.on('end',()=>res({code:x.statusCode,body:b}));}); r.on('error',e=>res({code:0,body:e.message})); r.end(); }); }
+
+(async () => {
+  if (!fs.existsSync(STAGE)) { console.error(`✗ stage file missing: ${STAGE}\n  Create it (chmod 600) with the new key+project, then re-run.`); process.exit(2); }
+  const nw = parseEnv(fs.readFileSync(STAGE,'utf8'));
+  const NEWKEY = nw.BROWSERBASE_API_KEY, NEWPROJ = nw.BROWSERBASE_PROJECT_ID;
+  if (!NEWKEY || !NEWPROJ) { console.error('✗ stage file needs BOTH BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID'); process.exit(2); }
+  console.log(`new key ${last4(NEWKEY)} · new project ${last4(NEWPROJ)}`);
+
+  // 1. validate the new key against Browserbase
+  const v = await bbVerify(NEWKEY);
+  if (v.code !== 200) { console.error(`✗ new key failed Browserbase verify (HTTP ${v.code}). Aborting — prod untouched.`); process.exit(1); }
+  const projOk = v.body.includes(NEWPROJ);
+  console.log(`✓ new key valid (HTTP 200)${projOk?` · project ${last4(NEWPROJ)} visible`:' · ⚠ project id not in project list — check it'}`);
+
+  // 2. read prod .env, swap the two keys, preserve everything else
+  const cur = ssh(`cat ${PROD_ENV}`);
+  if (cur.status !== 0) { console.error('✗ could not read prod .env:', (cur.stderr||'').slice(0,200)); process.exit(1); }
+  const oldKey = parseEnv(cur.stdout).BROWSERBASE_API_KEY || '(none)';
+  let lines = cur.stdout.split('\n'); const seen=new Set();
+  lines = lines.map(l => { const m=l.match(/^(BROWSERBASE_API_KEY|BROWSERBASE_PROJECT_ID)=/); if(!m) return l; seen.add(m[1]); return m[1]==='BROWSERBASE_API_KEY'?`BROWSERBASE_API_KEY=${NEWKEY}`:`BROWSERBASE_PROJECT_ID=${NEWPROJ}`; });
+  if(!seen.has('BROWSERBASE_API_KEY')) lines.push(`BROWSERBASE_API_KEY=${NEWKEY}`);
+  if(!seen.has('BROWSERBASE_PROJECT_ID')) lines.push(`BROWSERBASE_PROJECT_ID=${NEWPROJ}`);
+  const body = lines.join('\n').replace(/\n+$/,'')+'\n';
+
+  // 3. write back via ssh+tee over stdin (secret never on the command line), chmod 600
+  const w = ssh(`cat > ${PROD_ENV} && chmod 600 ${PROD_ENV}`, body);
+  if (w.status !== 0) { console.error('✗ prod .env write failed:', (w.stderr||'').slice(0,200)); process.exit(1); }
+  console.log(`✓ prod .env swapped ${last4(oldKey)} → ${last4(NEWKEY)} (other vars preserved)`);
+
+  // 4. restart pm2 with fresh env
+  const rs = ssh(`cd /root/Projects/all-designerwallcoverings && pm2 restart ${PM2} --update-env && sleep 2 && curl -sf http://127.0.0.1:9958/healthz`);
+  console.log(rs.status===0 ? '✓ pm2 restarted + healthz ok' : '⚠ pm2 restart/health check: '+(rs.stderr||rs.stdout||'').slice(0,200));
+
+  // 5. confirm old shared key is GONE from prod
+  const after = parseEnv(ssh(`cat ${PROD_ENV}`).stdout);
+  console.log(after.BROWSERBASE_API_KEY===NEWKEY ? `✓ prod now on dedicated key ${last4(NEWKEY)}; shared ${last4(oldKey)} gone` : '✗ prod key mismatch after write — inspect manually');
+
+  // 6. one live verify scrape through the public endpoint (~$0.10)
+  console.log(`\nrunning ONE live verify scrape (SKU ${VERIFY_SKU}, ~$0.10)…`);
+  const c = spawnSync('curl',['-s','-u','admin:DW2024!',`https://all.designerwallcoverings.com/api/livestock?sku=${VERIFY_SKU}&live=1`],{encoding:'utf8'});
+  try { const j=JSON.parse(c.stdout); console.log('  live:',j.live,'| in_stock:',j.in_stock,'| source:',j.source,'| cost_usd:',j.cost_usd,'| cached:',j.cached); console.log(j.live===true?'✓ prod live-check WORKS on the dedicated key':'⚠ live not true — inspect: '+c.stdout.slice(0,200)); }
+  catch(e){ console.log('  raw:', c.stdout.slice(0,300)); }
+
+  // 7. wipe the stage file so the plaintext key doesn't linger
+  fs.rmSync(STAGE,{force:true}); console.log(`\n✓ stage file wiped. Rotation complete.`);
+})();

← 627cc26 browserbase: unhook Kamatera prod all-dw from shared key fan  ·  back to Secrets Manager  ·  browserbase: pre-stage one-shot prod dedicated-key swap help 75dc44c →