[object Object]

← back to IWasCute

Verify AdSense ownership without loading ads in private photo flows

9ee4d7d53bd027cce2705cd34b46a3f2bad0fa22 · 2026-09-10 09:10:12 -0700 · Steve Abrams

Files touched

Diff

commit 9ee4d7d53bd027cce2705cd34b46a3f2bad0fa22
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 10 09:10:12 2026 -0700

    Verify AdSense ownership without loading ads in private photo flows
---
 src/app/layout.tsx                 |   9 ++----
 verification/TK-11380/REVIEW.md    |  30 ++++++++++++++++++
 verification/TK-11380/login.png    | Bin 0 -> 76576 bytes
 verification/TK-11380/results.json |  62 +++++++++++++++++++++++++++++++++++++
 verification/TK-11380/verify.cjs   |  50 ++++++++++++++++++++++++++++++
 5 files changed, 144 insertions(+), 7 deletions(-)

diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 4e80308..d55a4e6 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,5 +1,4 @@
 import type { Metadata } from "next";
-import Script from "next/script";
 import { Geist, Geist_Mono } from "next/font/google";
 import "./globals.css";
 import VersionSwitcher from "@/components/shared/VersionSwitcher";
@@ -17,6 +16,8 @@ const geistMono = Geist_Mono({
 export const metadata: Metadata = {
   title: "I was Cute — Your Childhood Photos, Legally Licensed",
   description: "Upload your childhood photos, clear the rights, and earn royalties.",
+  // Verify ownership without executing advertising code in photo/account flows.
+  other: { "google-adsense-account": "ca-pub-5278231299883833" },
 };
 
 export default function RootLayout({
@@ -30,12 +31,6 @@ export default function RootLayout({
       className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
     >
       <body className="min-h-full flex flex-col">
-        <Script
-          async
-          src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5278231299883833"
-          crossOrigin="anonymous"
-          strategy="afterInteractive"
-        />
         {children}
         <VersionSwitcher />
       </body>
diff --git a/verification/TK-11380/REVIEW.md b/verification/TK-11380/REVIEW.md
new file mode 100644
index 0000000..096f51a
--- /dev/null
+++ b/verification/TK-11380/REVIEW.md
@@ -0,0 +1,30 @@
+# TK-11380 — three sites requiring review
+
+Local work is verified. Production changes and Google review submission have not occurred.
+
+## IWasCute
+
+The pending root-layout AdSense loader would execute on photo upload, login and admin routes. It is replaced with `metadata.other['google-adsense-account']`. Google accepts this ownership-verification mechanism: https://support.google.com/adsense/answer/7584263?hl=en . This does not serve ads or establish approval/readiness.
+
+`npm run build` passed using Next16.2.3. The installed metadata/scripts guides were read. `verify.cjs` verifies nine rendered routes, public-to-login navigation, zero attempted ad requests, exact ads.txt and unauthenticated admin API401. External requests are blocked in this test. Direct-route observation is bounded to DOMContentLoaded plus750ms; it does not prove authenticated workflows or all delayed behavior. Built JS/HTML/RSC scan found no adsbygoogle/googlesyndication references. `/3x` passed HTTP, screenshot, automation and Chrome; Safari/Firefox engines were unavailable. No account login or photo submission was performed.
+
+DTD: option A (verification only),2/2 valid voters Codex/Qwen; Claude/Grok/Kimi/Muse abstained. Adversarial Codex KEEP, noting that verification is not Google approval. Raw run retained at `/tmp/TK11380-dtd-adscope-host`.
+
+## Live findings, 2026-09-10
+
+* Carmel `/`: HTTP200, title `DW — Staged for Active (cadence backlog)`; `/ads.txt`404. Live Nginx routes9861 to PM2 `staged-viewer`, not Carmel. Port9832 belongs to `hollywood-wallcoverings`. Carmel source exists at `/root/public-projects/carmelwallpapers` but no matching plural-name PM2 app runs. The separate `dwf-carmelwallpaper` app is a different domain/project.
+* Hospitality `/`, `/ads.txt`, `/privacy`, `/about`: HTTP401. `/healthz`200. Full-site BasicAuth is intentional and remains intact. Local ads.txt and script exist behind that gate. Public access needs a separate owner decision.
+* IWasCute runs `gap-iwascute`, `/root/Projects/iwascute`, PORT9636. Live serves the older Next build. Remote source already has the unbuilt global loader; local source fix targets it.
+
+Both remote worktrees are dirty. A deployment must preserve their changes; never rsync/delete or overwrite the whole worktree. Carmel/Hospitality also lack privacy content; none of these findings establish AdSense readiness.
+
+## Proposed production actions — approval required
+
+1. Snapshot the two remote worktrees locally in their existing Git repos after checking excluded files/secrets. Back up the live Carmel Nginx file. Record PM2 state and current responses.
+2. Carmel: recheck19961 is unused by listeners, PM2 and Nginx. Apply only reviewed source/config hunks: explicit19961, bind127.0.0.1, health.site=carmelwallpapers, health URL19961/health, and the previously committed duplicate-loader removal. Preserve remote data/products.json and shared code. Install locked production dependencies. Start only `carmelwallpapers` using its ecosystem config. Require `/health` site identity and nonzero products, correct homepage title and exact ads.txt before switching routing. Patch only Carmel's live `proxy_pass` from9861 to `127.0.0.1:19961`, preserve TLS/headers/analytics, run nginx validation, then reload. Other services remain untouched. Do not point to9832.
+3. IWasCute: apply only the reviewed layout patch, preserve other source and analytics. Build in `/root/Projects/iwascute`, restart existing `gap-iwascute` on9636. Do not use the stale local ecosystem's7450 or legacy Express7350. Recheck live metadata, ads.txt, analytics, homepage, login and admin API401; assert no ad loader after hydration/navigation.
+4. After each deployment, repeat live identity/integration checks and record evidence. Do not submit Google review until public-content/privacy prerequisites have separately passed. Hospitality auth remains unchanged.
+
+Rollback: keep timestamped Nginx/source/config backups and a copy of IWasCute's previous `.next` build. Revert only this task's hunks and restore that build if needed. Rollback of Carmel's route returns its previously wrong page, so treat that as outage containment only and surface it. Stop only a newly created Carmel process; never stop staged-viewer or Hollywood Wallcoverings.
+
+Local Carmel preparation and verification live in `Projects/carmelwallpapers/verification/TK-11380`. The proposed port is a checked candidate, not a permanent reservation; recheck immediately before deployment.
diff --git a/verification/TK-11380/login.png b/verification/TK-11380/login.png
new file mode 100644
index 0000000..25691f6
Binary files /dev/null and b/verification/TK-11380/login.png differ
diff --git a/verification/TK-11380/results.json b/verification/TK-11380/results.json
new file mode 100644
index 0000000..9a51926
--- /dev/null
+++ b/verification/TK-11380/results.json
@@ -0,0 +1,62 @@
+{
+  "timestamp": "2026-09-10T16:04:37.915Z",
+  "base": "http://127.0.0.1:19962",
+  "results": [
+    {
+      "path": "/",
+      "html": "PASS",
+      "hydrated": "PASS"
+    },
+    {
+      "path": "/v1",
+      "html": "PASS",
+      "hydrated": "PASS"
+    },
+    {
+      "path": "/v2",
+      "html": "PASS",
+      "hydrated": "PASS"
+    },
+    {
+      "path": "/v3",
+      "html": "PASS",
+      "hydrated": "PASS"
+    },
+    {
+      "path": "/browse",
+      "html": "PASS",
+      "hydrated": "PASS"
+    },
+    {
+      "path": "/login",
+      "html": "PASS",
+      "hydrated": "PASS"
+    },
+    {
+      "path": "/licensor",
+      "html": "PASS",
+      "hydrated": "PASS"
+    },
+    {
+      "path": "/dashboard",
+      "html": "PASS",
+      "hydrated": "PASS"
+    },
+    {
+      "path": "/admin",
+      "html": "PASS",
+      "hydrated": "PASS"
+    },
+    {
+      "journey": "home -> v1 -> login",
+      "adRequests": 0,
+      "verdict": "PASS"
+    },
+    {
+      "adminWithoutCookie": 401,
+      "adsTxt": "PASS"
+    }
+  ],
+  "adRequests": [],
+  "thirdPartyRequestsBlocked": true
+}
diff --git a/verification/TK-11380/verify.cjs b/verification/TK-11380/verify.cjs
new file mode 100644
index 0000000..7840a04
--- /dev/null
+++ b/verification/TK-11380/verify.cjs
@@ -0,0 +1,50 @@
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const { chromium } = require('/Users/macstudio3/Projects/Designer-Wallcoverings/node_modules/playwright');
+const base = process.env.VERIFY_URL || 'http://127.0.0.1:19962';
+const publisher = 'ca-pub-5278231299883833';
+const results = [];
+
+(async () => {
+  const browser = await chromium.launch({ executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', headless: true });
+  try {
+    const page = await browser.newPage();
+    const adRequests = [];
+    page.on('request', req => { if (/adsbygoogle|googlesyndication|doubleclick/.test(req.url())) adRequests.push(req.url()); });
+    // Keep this verification isolated: no third-party requests or real ad impressions.
+    await page.route('**/*', route => new URL(route.request().url()).origin === new URL(base).origin ? route.continue() : route.abort());
+    for (const path of ['/', '/v1', '/v2', '/v3', '/browse', '/login', '/licensor', '/dashboard', '/admin']) {
+      const response = await fetch(base + path);
+      assert.equal(response.status, 200, path);
+      const html = await response.text();
+      assert.match(html, /<meta name="google-adsense-account" content="ca-pub-5278231299883833"/);
+      assert.doesNotMatch(html, /adsbygoogle\.js/);
+      await page.goto(base + path, { waitUntil: 'domcontentloaded' });
+      await page.locator('body').waitFor({ state: 'visible' });
+      // The uploader has ongoing work; network-idle is not its readiness contract.
+      await page.waitForTimeout(750);
+      assert.equal(await page.locator('meta[name="google-adsense-account"]').getAttribute('content'), publisher);
+      assert.equal(await page.locator('script[src*="adsbygoogle"]').count(), 0);
+      results.push({ path, html: 'PASS', hydrated: 'PASS' });
+    }
+    await page.goto(base, { waitUntil: 'networkidle' });
+    await page.locator('a[href="/v1"]').first().click();
+    await page.waitForURL('**/v1');
+    await page.locator('a[href="/login"]').first().click();
+    await page.waitForURL('**/login');
+    await page.waitForLoadState('networkidle');
+    assert.equal(await page.locator('script[src*="adsbygoogle"]').count(), 0);
+    assert.deepEqual(adRequests, []);
+    results.push({ journey: 'home -> v1 -> login', adRequests: 0, verdict: 'PASS' });
+    const admin = await fetch(base + '/api/admin');
+    assert.equal(admin.status, 401);
+    assert.deepEqual(await admin.json(), { error: 'Unauthorized' });
+    const ads = await fetch(base + '/ads.txt');
+    assert.equal(ads.status, 200);
+    assert.equal((await ads.text()).trim(), 'google.com, pub-5278231299883833, DIRECT, f08c47fec0942fa0');
+    results.push({ adminWithoutCookie: 401, adsTxt: 'PASS' });
+    await page.screenshot({ path: __dirname + '/login.png', fullPage: true });
+    fs.writeFileSync(__dirname + '/results.json', JSON.stringify({ timestamp: new Date().toISOString(), base, results, adRequests, thirdPartyRequestsBlocked: true }, null, 2) + '\n');
+    console.log(JSON.stringify({ verdict: 'PASS', checkedRoutes: 9, clientNavigation: 'PASS', adminAuth: 'PASS', adRequests: 0 }));
+  } finally { await browser.close(); }
+})().catch(err => { console.error(err); process.exitCode = 1; });

← fc38976 TK-11341: add AdSense Auto-Ads loader + ads.txt (revert to r  ·  back to IWasCute  ·  Record IWasCute verification boundaries and deployment gates a21f9dd →