[object Object]

← back to Codex Review 2026 04 30

snapshot: 4 file(s) changed, +2 new, ~2 modified

665a63ceda39933afcf9450227b13792f3ed6b48 · 2026-05-13 08:57:47 -0700 · Steve

Files touched

Diff

commit 665a63ceda39933afcf9450227b13792f3ed6b48
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed May 13 08:57:47 2026 -0700

    snapshot: 4 file(s) changed, +2 new, ~2 modified
---
 _PRIORITY_PATCHES.md                               |  10 +-
 _PRIORITY_PATCHES.md.pre-scrub-2026-05-07.bak      | 202 +++++++++++++++++++++
 jill-website/claude-review.md                      |   2 +-
 .../claude-review.md.pre-scrub-2026-05-07.bak      |  36 ++++
 4 files changed, 244 insertions(+), 6 deletions(-)

diff --git a/_PRIORITY_PATCHES.md b/_PRIORITY_PATCHES.md
index 2c97058..b193dd6 100644
--- a/_PRIORITY_PATCHES.md
+++ b/_PRIORITY_PATCHES.md
@@ -21,7 +21,7 @@ Late-night Claude grep surfaced these. Files contain literal `sk-ant-api03-...`
 **Action**: rotate all 5 Anthropic keys at https://console.anthropic.com/settings/keys, then use `/secrets` skill to fan the new key into the env files. Then sed-replace the inline literals with `process.env.ANTHROPIC_API_KEY` and remove the fallback (fail-fast pattern).
 
 ## 🚨 P0 — LIVE Gemini API keys in 14+ files (rotate immediately)
-Same key (`AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`) repeated across:
+Same key (`${GOOGLE_API_KEY}`) repeated across:
 ```
 ~/Projects/Bertha/kalshi-dash/server.js:130
 ~/Projects/SDCC-Stories/lib/gemini.js:8
@@ -37,7 +37,7 @@ Same key (`AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`) repeated across:
 ~/Projects/Designer-Wallcoverings/enrich-arte-gaps.js:4
 ~/Projects/Designer-Wallcoverings/enrich-arte-puppeteer.js:5
 ```
-Plus a 2nd key `AIzaSyBHmT3GLHlzjvFVngT93bvEPQ1AZpzS2ic` in `~/Projects/SDCC-Stories/lib/gemini.js:9`.
+Plus a 2nd key `${GOOGLE_API_KEY}` in `~/Projects/SDCC-Stories/lib/gemini.js:9`.
 
 **Action**: rotate both at https://aistudio.google.com/app/apikey, sed-replace literals with `process.env.GEMINI_API_KEY`.
 
@@ -56,9 +56,9 @@ Token `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` appears in:
 ## 🚨 P0 — LIVE Slack tokens in 9+ files (incl. dangerous **user-scope** token)
 Three distinct tokens, all in Kamatera mirror + Designer-Wallcoverings DW-Programming:
 ```
-xoxb-3958182050256-9814366234406-HiR1ybK6HT8JViWDEpjXpmsq   (DWMCP bot, 5 files)
-xoxb-3958182050256-9852565284259-8kfXhJQWVsf515doMRI67AHQ   (Designer bot, 2 files)
-xoxp-3958182050256-3947130410993-10509093793987-db03ca5d0c121d415a816178f4aac1a7   (USER scope, 2 files — dangerous, can read DMs)
+${SLACK_BOT_TOKEN}   (DWMCP bot, 5 files)
+${SLACK_BOT_TOKEN}   (Designer bot, 2 files)
+${SLACK_USER_TOKEN}-redacted-NEEDS-ROTATION-2026-05-07   (USER scope, 2 files — dangerous, can read DMs)
 ```
 
 Files include:
diff --git a/_PRIORITY_PATCHES.md.pre-scrub-2026-05-07.bak b/_PRIORITY_PATCHES.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..2c97058
--- /dev/null
+++ b/_PRIORITY_PATCHES.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,202 @@
+# Priority patches Steve can apply in the morning
+
+These are concrete diffs/commands Claude generated tonight from the deferred lists. Each is verified read-only safe — Steve applies, Claude doesn't auto-apply.
+
+## 🚨 P0 — LIVE Anthropic API keys hardcoded in source (**rotate immediately**)
+Late-night Claude grep surfaced these. Files contain literal `sk-ant-api03-...` keys, some without env fallback. Five distinct keys leaked across 11 files. Locations (see `_SECRETS_INLINE.md` for full list):
+
+```
+~/Projects/Designer-Wallcoverings/DW-Programming/KravetDesignTagger/server.js:12
+~/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/dwjs-ai-retag.ts:21
+~/Projects/Designer-Wallcoverings/DW-Programming/update-specific-schumacher.ts:13
+~/Projects/Designer-Wallcoverings/DW-Programming/schumacher-dwsw-full-updater.ts:19
+~/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/fix-all-dwpd-colors.ts:15
+~/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/fix-dwjs-red-tags.ts:17
+~/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/fix-color-tags.ts:14
+~/Projects/Kamatera/dear-bubbe-nextjs/ecosystem.config.js:10
+~/Projects/Kamatera/DW-Agents/bob-the-blogger/server.js:33
+~/Projects/Kamatera/DW-Agents/thibaut-agent/server.js:17  (no env fallback at all — inline)
+```
+
+**Action**: rotate all 5 Anthropic keys at https://console.anthropic.com/settings/keys, then use `/secrets` skill to fan the new key into the env files. Then sed-replace the inline literals with `process.env.ANTHROPIC_API_KEY` and remove the fallback (fail-fast pattern).
+
+## 🚨 P0 — LIVE Gemini API keys in 14+ files (rotate immediately)
+Same key (`AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`) repeated across:
+```
+~/Projects/Bertha/kalshi-dash/server.js:130
+~/Projects/SDCC-Stories/lib/gemini.js:8
+~/Projects/sku-check-skill/server.js:25
+~/Projects/Designer-Wallcoverings/gemini-classify-texture.js:6
+~/Projects/Designer-Wallcoverings/gemini-enrich-arte.js:4
+~/Projects/Designer-Wallcoverings/enrich-remaining-13.js:5
+~/Projects/Designer-Wallcoverings/gemini-enrich-gaps.js:5
+~/Projects/Designer-Wallcoverings/gemini-enrich-arte9.js:4
+~/Projects/Designer-Wallcoverings/arte-fill-tags.js:5
+~/Projects/Designer-Wallcoverings/ai-enrich-gaps2.js:5
+~/Projects/Designer-Wallcoverings/ai-enrich-arte-fix.js:4
+~/Projects/Designer-Wallcoverings/enrich-arte-gaps.js:4
+~/Projects/Designer-Wallcoverings/enrich-arte-puppeteer.js:5
+```
+Plus a 2nd key `AIzaSyBHmT3GLHlzjvFVngT93bvEPQ1AZpzS2ic` in `~/Projects/SDCC-Stories/lib/gemini.js:9`.
+
+**Action**: rotate both at https://aistudio.google.com/app/apikey, sed-replace literals with `process.env.GEMINI_API_KEY`.
+
+## 🚨 P0 — LIVE Shopify admin token hardcoded in **18+ files** (single token mass leak)
+Token `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` appears in:
+```
+~/Projects/archive-agent/server.js:15                         (env-fallback)
+~/Projects/archive-agent/ecosystem.config.js:10               (raw in pm2 env block)
+~/Projects/Letsbegin/migrate_global_to_custom.js:29           (inline, no env)
+~/Projects/dw-war-room/vite.config.ts:8                       (inline, no env)
+~/Projects/dwjs-consolidation-2026-04-23/*.js                 (15+ one-off scripts, all inline)
+```
+
+**Action**: rotate at Shopify admin → Apps → Develop apps → rotate the admin API token. Then sed-replace literals with `process.env.SHOPIFY_ADMIN_TOKEN` (fail-fast on missing). The dwjs-consolidation scripts are dated 2026-04-23 one-offs — consider archiving the whole dir + redacting tokens before git-committing anywhere.
+
+## 🚨 P0 — LIVE Slack tokens in 9+ files (incl. dangerous **user-scope** token)
+Three distinct tokens, all in Kamatera mirror + Designer-Wallcoverings DW-Programming:
+```
+xoxb-3958182050256-9814366234406-HiR1ybK6HT8JViWDEpjXpmsq   (DWMCP bot, 5 files)
+xoxb-3958182050256-9852565284259-8kfXhJQWVsf515doMRI67AHQ   (Designer bot, 2 files)
+xoxp-3958182050256-3947130410993-10509093793987-db03ca5d0c121d415a816178f4aac1a7   (USER scope, 2 files — dangerous, can read DMs)
+```
+
+Files include:
+```
+~/Projects/Kamatera/Designer-Wallcoverings-scripts/activate-koroseal-cron.js:11
+~/Projects/Kamatera/Designer-Wallcoverings-scripts/activate-arte-cron.js:12
+~/Projects/Kamatera/DW-Agents/slack-dm-viewer/server.js:34,36,40
+~/Projects/Kamatera/DW-Agents/scripts/slack-bulk-delete.js:23,24,25
+~/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/universal-completion-notifier.ts:9
+~/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/dwjs-ai-retag.ts:24
+```
+
+**Action**: revoke all 3 at https://api.slack.com/apps → respective app → OAuth & Permissions → "Revoke installation" or rotate. The xoxp- (user) token is highest risk — anyone with it can act as Steve in Slack. Re-issue as bot-scope where possible; user-scope only when truly needed.
+
+## 🚨 P0 — LIVE Cloudflare API token (already in MEMORY.md)
+```
+~/Projects/philipperomano/finish_dns.sh:8
+CF="${CLOUDFLARE_API_TOKEN:-yMyJ8CeD7zzXc0C3UxCHVwQG6QATAs6Kxuw6mn9C}"
+```
+Per `feedback_cf_token_leaked.md`: live CF token committed plaintext. DNS-edit only, no zone-create. Action: rotate at https://dash.cloudflare.com/profile/api-tokens → mint new account-scoped token, replace fallback.
+
+## 🚨 P0 — Postgres password `DW2024SecurePass` for `dw_admin` leaked in 10+ files
+This is the credential for the `dw_unified` PG database — the wallcoverings business core data store.
+```
+~/Projects/Bertha/kalshi-dash/server.js:351,4875
+~/Projects/Bertha/react-dash/server.js:28
+~/Projects/Bertha/src/lib/db.js:4
+~/Projects/archive-agent/server.js:17                  (env-fallback)
+~/Projects/archive-agent/ecosystem.config.js:11        (raw in pm2 env block)
+~/Projects/Letsbegin/scrape_dg_specs.js:12
+~/Projects/timeout-agent/server.js:21                  (uses :15432 tunnel port — Mac Studio 2)
+~/Projects/fix-no-image-products.js:172                (one-off script)
+~/Projects/fix-as-creation-17.js:99                    (one-off script)
+```
+**Action**: rotate the `dw_admin` PG password (`ALTER USER dw_admin WITH PASSWORD '<new>';`). Update `.env` files via `/secrets` skill. Sed-replace inline literals with `process.env.DATABASE_URL` (fail-fast). Order matters: per memory, schema changes go Local-first → Kamatera publishes via logical replication; password is a server-side change so do it on Mac2 first, then update Kamatera env, then sync.
+
+## 🚨 P0 — Vendor scraper passwords leaked (`*<service>access911*` pattern)
+```
+~/Projects/watches/server.js:85                        AUTH_PASSWORD = 'DW2025Secure!'
+~/Projects/watches/thibaut-description-backfill.js:22  password: '*Thibautaccess911*'
+~/Projects/watches/thibaut-room-settings-scraper.js:13 THIBAUT_PASSWORD = '*Thibautaccess911*'
+~/Projects/dear-bubbe-nextjs/lib/instagram-poster.js:15            INSTAGRAM_PASSWORD: '*Instaaccess911*'
+~/Projects/dear-bubbe-nextjs/archived/social-posters/*             4 more files with Bluesky/Twitter/Instagram '*<svc>access911*' pattern
+~/Projects/sku-check-skill/server.js:31                YORKWALL_PASSWORD = '*Brewsteraccess911*'
+~/Projects/Kamatera/dear-bubbe-nextjs/lib/instagram-poster.js:15   (mirror)
+~/Projects/Kamatera/kamatera-claude-skills/resize-it/server.py:22  SPOONFLOWER_PASSWORD = '*Spoonaccess911*'  (Kamatera mirror)
+```
+**Action**: rotate ALL of these at their respective vendor sites (Thibaut, Instagram, Bluesky, Twitter, Yorkwall/Brewster, Spoonflower). The `*<service>access911*` pattern is so consistent that anyone seeing ONE password can probably guess the others. After rotation, switch to a per-service env var (no fallback literal) and consider random-generated passwords stored in 1Password.
+
+## 🚨 P0 — `room-setting-agent` (LIVE pm2) password is `DW2025Secure!` literally written in source comment
+```
+~/Projects/Kamatera/DW-Programming/room-setting-app/app/api/auth/login-password/route.ts:8
+~/Projects/Designer-Wallcoverings/DW-Programming/room-setting-app/app/api/auth/login-password/route.ts:8
+```
+```ts
+// Hardcoded hash for DW2025Secure! (bcrypt hash with special chars that env vars can't handle)
+const AUTH_PASSWORD_HASH = process.env.AUTH_PASSWORD_HASH || '$2b$10$GXL6hj.1OAdCJ7tDSPYIPuhJxq/MgIYynNyAFdqfrwG9T4yImb3H.';
+```
+Confirmed via `pm2 env 11` — `AUTH_PASSWORD_HASH` is **NOT set in pm2 env**, so the inline default IS what's used at runtime. Anyone reading the source knows admin/DW2025Secure! gets in. The bcrypt hash is technically secure (10 rounds), but the comment renders that moot.
+
+**Action**:
+1. Pick a new password (e.g., `pwgen -s 24 1` or 1Password generator).
+2. Generate fresh bcrypt: `node -e "console.log(require('bcrypt').hashSync(process.argv[1], 12))" '<NEW_PW>'`.
+3. Set in pm2 env: edit `~/Projects/Kamatera/DW-Programming/room-setting-app/ecosystem.config.js` to set `AUTH_PASSWORD_HASH: '<new-hash>'`.
+4. Remove the inline default + comment from `route.ts`.
+5. `pm2 restart room-setting-agent --update-env` and `pm2 save`.
+
+Note: the comment also leaks the production path `/root/Projects/Designer-Wallcoverings/DW-Programming/room-setting-app/data/login-log.json` — confirms this serves on Kamatera.
+
+## P1 — bcrypt hashes inline (lower risk, dormant projects)
+- `~/Projects/handbag-auth-nextjs/src/app/api/auth/login/route.ts:26,33` — 2 inline hashes. Project not running on pm2 (dormant). Still: if you ever spin it up, fix first.
+- `~/Projects/dear-bubbe-admin/backend/routes/auth.js:11` — comment says `// password` next to a bcrypt hash. The hash format suggests the password is literally `password`. Project not running on pm2 (dormant). Still: don't reuse this code without rotating.
+
+## P0 — `Designer-Wallcoverings/shopify/scripts/wm-*.js` hardcoded puppeteer path
+Three scripts hard-require puppeteer from a sibling project's node_modules:
+
+```js
+// ~/Projects/Designer-Wallcoverings/shopify/scripts/wm-price-scrape-morris.js:8
+// ~/Projects/Designer-Wallcoverings/shopify/scripts/wm-pattern-retry.js:8
+// ~/Projects/Designer-Wallcoverings/shopify/scripts/wm-price-scrape.js:15
+const puppeteer = require('/Users/stevestudio2/Projects/Designer-Wallcoverings/DW-Websites/GrassclothWallpaper.com/node_modules/puppeteer');
+```
+
+Why bad: if `GrassclothWallpaper.com/node_modules/` gets cleaned/reinstalled, these scripts break silently. Also breaks any CI/CD that doesn't have GrassclothWallpaper preinstalled.
+
+Fix: install puppeteer in the shopify dir:
+```bash
+cd ~/Projects/Designer-Wallcoverings/shopify
+npm install puppeteer-core --save  # or full puppeteer if you need bundled chromium
+# Then in each wm-*.js: const puppeteer = require('puppeteer-core');
+```
+
+## P0 — Stripe webhook secret enforcement (DEPLOY STEP)
+The 3 next.js sites under `site-factory/sites/*.com/app/` have updated source but stale builds. Activate codex's fix:
+```bash
+for site in wholivedthere.com bubbesblock.com claimmyaddress.com; do
+  cd ~/Projects/site-factory/sites/$site/app
+  npm run build && pm2 restart site-${site%.com}
+done
+sleep 5
+for p in 3001 3002 3003; do curl -s -o /dev/null -w ":$p %{http_code}\n" http://127.0.0.1:$p/; done
+# Should still be 200 — the fix only changes behavior on missing STRIPE_WEBHOOK_SECRET
+```
+
+## P0 — am-recrawl one-shot misconfiguration (KAMATERA-SIDE)
+Codex's POKE found scrape-andrewmartin.js + scraper-utils.js bugs. **Edit on Kamatera, not Mac.** Patch in `_PM2_NOTES.md`. Stop the local pm2 process from auto-starting on boot:
+```bash
+pm2 delete am-recrawl  # currently stopped, but this removes from pm2 dump too
+pm2 save
+```
+Then on Kamatera: apply the codex patch (idempotent closePool, browser.isConnected guards, throw on disconnect), and run as cron (not pm2-autorestart) for periodic recrawls.
+
+## P1 — Done tonight (no action needed)
+- ✓ `.gitignore` protection added to: resize-it, secrets-manager, wholivedthere/bubbesblock/claimmyaddress next.js apps, site-factory/admin
+- ✓ `.env.example` template added to: secrets-manager (keys only, values redacted)
+- ✓ sf-admin SESSION_SECRET wired in (96-char random) — survives next pm2 restart
+
+## P1 — sf-admin SF_ADMIN_DEV=1 dev-bypass leak
+`ecosystem.config.js:49` has `SF_ADMIN_DEV: '1'` which bypasses Google OAuth (auto-authenticates as Steve). The SESSION_SECRET fix Claude wired in tonight prevents session tampering, but anyone reaching :9883 still gets admin access in dev-bypass mode. Fix:
+```js
+// ~/Projects/site-factory/ecosystem.config.js:49
+- SF_ADMIN_DEV: '1',
++ SF_ADMIN_DEV: '0',  // require real Google OAuth
+```
+Then ensure GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET are set in the same env block.
+
+(Note: sf-admin is bound to 127.0.0.1 only, so the dev-bypass leak is contained to local-machine attackers. Still worth fixing.)
+
+## P2 — vf-orchestrator quirky restart pattern
+visual-factory-orchestrator restarts every ~25min with exit code 0. Not a fire (autorestart catches it, HTTP 200 throughout) but worth understanding. Look for:
+- `process.exit(0)` calls in server.js or stage runners
+- Any `setInterval(..., 25*60*1000)` cleanup hook
+- `pm2 reload` triggers from another cron/watcher
+
+This is informational, not blocking.
+
+## What's NOT in this list
+- All the deferred items in per-project CHANGES.md `## Deferred (needs Steve)` sections — those need codex (not yet Claude-able) or domain knowledge Steve has.
+- Schema/migration work for bankrupt-leads, professional-directory, lawyer-directory-builder — DDL is genuinely Steve-only.
+- Authentication policy choices for public POST endpoints — needs Steve to decide token vs OAuth vs IP-allowlist (`NEEDS_AUTH.md` lists candidates).
+- Codex round-3 attempts (15 projects) — blocked at quota; resume in the morning per `_QUOTA_HALT.md`.
diff --git a/jill-website/claude-review.md b/jill-website/claude-review.md
index 2d07755..a0c998d 100644
--- a/jill-website/claude-review.md
+++ b/jill-website/claude-review.md
@@ -32,5 +32,5 @@ MIXED — the security patches are directionally correct but the deploy is curre
 1. **BEFORE any restart** — add the four required env vars to `/root/Projects/jill-website/.env` on Kamatera (and local `.env`). Values must match whatever the admin UI sends as credentials once session auth is wired.
 2. **Fix admin auth loop** — either: (a) update `requireAuth` in `src/middleware/auth.ts` to accept session-authenticated requests, or (b) revert `views/admin.ejs` to send Basic Auth with the new `ADMIN_PASSWORD` env value. Current state leaves the CMS completely non-functional.
 3. **Mount unsubscribe router** in `src/server.ts` — one-line fix (`app.use('/api', unsubscribeRouter)`).
-4. **Rotate Google API keys** — `AIza_REDACTED_7ETw_was_here` and `AIzaSyCr_VbbAcpGeY0CRnTV-YcCQENGOLrbxg0` are committed in the local `.env` and likely the Kamatera copy. Restrict by referrer/API in Google Cloud Console immediately; rotate if already abused.
+4. **Rotate Google API keys** — `AIza_REDACTED_7ETw_was_here` and `${GOOGLE_API_KEY}` are committed in the local `.env` and likely the Kamatera copy. Restrict by referrer/API in Google Cloud Console immediately; rotate if already abused.
 5. Remove stale credential hint at `views/admin-login.ejs:179`.
diff --git a/jill-website/claude-review.md.pre-scrub-2026-05-07.bak b/jill-website/claude-review.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..2d07755
--- /dev/null
+++ b/jill-website/claude-review.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,36 @@
+# Claude review of codex patches — jill-website
+
+## Verdict
+MIXED — the security patches are directionally correct but the deploy is currently broken and the live site will crash on next restart.
+
+## Where codex got it right
+- `src/services/emailService.ts:30,94` — `escapeHtml()` added and applied to all inquiry fields before HTML email interpolation. Clean, correct.
+- `src/routes/unsubscribe.ts:3,98` — same `escapeHtml()` pattern; reflected-email XSS closed.
+- Body guards on `activities.ts`, `inquiries.ts`, `linkValidation.ts`, `admin.ts` — all return 400 on missing body, no regressions.
+- `views/admin.ejs:414,475,497` (round 2) — hardcoded `Authorization: Basic admin:admin123` headers correctly removed; replaced with `credentials: 'same-origin'`. The admin UI will now rely on the session cookie rather than a static credential header.
+
+## Where codex got it wrong or missed (file:line)
+
+**Critical — site will not start:**
+- `src/server.ts:62-65` — `requireEnv()` throws at startup if `SITE_USERNAME`, `SITE_PASSWORD`, `SESSION_SECRET`, or `ADMIN_PASSWORD` are absent. The production `.env` (local copy at `/Users/stevestudio2/Projects/jill-website/.env`) contains **none of these four variables**. The PM2 `ecosystem.config.js:11` also does not set them. Any `pm2 restart jill-website` or server reboot will crash the process immediately with an uncaught `Error: Missing required environment variable`. The site is LIVE — this is the highest-priority gap.
+
+**Auth loop introduced:**
+- `src/middleware/auth.ts:10` / `views/admin.ejs:414,475,497` — The admin UI now sends `credentials: 'same-origin'`, which passes a session cookie. But `requireAuth` in `auth.ts` checks `req.headers.authorization` (Basic Auth), not the session. There is no session-to-Basic-Auth bridge. Admin CMS calls will all 401/403 until either the auth middleware is updated to read the session or the UI reverts to Basic Auth with the new password.
+
+**Unsubscribe router still not mounted:**
+- `src/server.ts` — no `import` or `app.use` for the unsubscribe router anywhere. The fix to `unsubscribe.ts` is correct in isolation, but `/api/unsubscribe` still 404s for every real email link.
+
+**Stale credential hint in UI:**
+- `views/admin-login.ejs:179` — still reads `Default credentials: admin / admin123`. Cosmetic but misleading and should be removed.
+
+## Operational concerns codex missed
+- The `.env` on Kamatera production almost certainly also lacks the four new required vars. Codex deferred `ecosystem.config.js` as out of scope, but the consequence is **the next PM2 restart kills the live site** without any warning. Steve must add `SITE_USERNAME`, `SITE_PASSWORD`, `SESSION_SECRET`, and `ADMIN_PASSWORD` to the production `.env` (or ecosystem env block) before any restart.
+- The Google API keys committed in `.env:31-32` remain live and unrotated — codex flagged this as deferred but these are real keys serving a LIVE site.
+
+## Final action items (priorities)
+
+1. **BEFORE any restart** — add the four required env vars to `/root/Projects/jill-website/.env` on Kamatera (and local `.env`). Values must match whatever the admin UI sends as credentials once session auth is wired.
+2. **Fix admin auth loop** — either: (a) update `requireAuth` in `src/middleware/auth.ts` to accept session-authenticated requests, or (b) revert `views/admin.ejs` to send Basic Auth with the new `ADMIN_PASSWORD` env value. Current state leaves the CMS completely non-functional.
+3. **Mount unsubscribe router** in `src/server.ts` — one-line fix (`app.use('/api', unsubscribeRouter)`).
+4. **Rotate Google API keys** — `AIza_REDACTED_7ETw_was_here` and `AIzaSyCr_VbbAcpGeY0CRnTV-YcCQENGOLrbxg0` are committed in the local `.env` and likely the Kamatera copy. Restrict by referrer/API in Google Cloud Console immediately; rotate if already abused.
+5. Remove stale credential hint at `views/admin-login.ejs:179`.

← 5e8eb5c initial snapshot — gitify all builds (CLAUDE.md rule 2026-05  ·  back to Codex Review 2026 04 30  ·  gitignore: add *.bak / *.bak-* / *.pre-* / *.orig patterns t 89ccaaf →