← back to Dw Domain Fleet

PROGRESS.md

211 lines

# DW Domain Fleet — standalone-storefront build (44/22 SPLIT)

## SCOPE CORRECTION — 2026-05-18 (dtd debate-panel, Option B)

The original brief targeted all **66** domains. A dtd debate-panel ruled
**SPLIT (Option B)**:

- **BUILD 44** domains as standalone, DW-spec storefronts — these have
  genuinely distinct search intent.
- **KEEP 22** domains as **301 redirects** to designerwallcoverings.com —
  near-duplicate typo/variant spellings or redundant cluster siblings.
  Building them out = doorway-page / scaled-content-abuse SEO penalty risk.
  Leave their existing forwarding in place. **No DNS change** for these 22.

**Current build count: 44 domains.**

### The 22 REDIRECT-ONLY domains (NOT built — leave 301 as-is)
designerswallcovering.com · designerswallcoverings.com · designerswallpapers.com ·
designerwallcover.com · designerwallcovering.com · designwallcoverings.com ·
designerwallpaints.com · designerwalltiles.com · dwlosangeles.com ·
designerlaboratory.com · thedesignerlaboratory.com · handmadewallpapers.com ·
handscreenedwallpaper.com · hospitalitysalesagents.com · hospitalitysalesreps.com ·
commercialwalls.com · 1800wallpapers.com · 800wallpaper.com · wallpapernyc.com ·
wc01wallpaper.com · wc-01.com · steveabramsdesigns.com

## Architecture
- **Shared parameterized codebase** at `~/Projects/dw-domain-fleet/`.
  One `server.js`; per-domain config under `sites/<slug>.json`; pm2 runs one
  instance per domain (`SITE` + `PORT` env). Catalog snapshot from live
  `designerwallcoverings.com/products.json` → `data/catalog.json`.
- 12 palettes × 4 font pairs → distinct visual identity per domain (anti-doorway-page).
- `asseeninmovies.com` is now built INSIDE the shared codebase (config
  `sites/asseeninmovies.json`, port 9904) — the prior "separate project" plan
  is superseded.

## Two mandatory SEO must-dos (panel-required, on every one of the 44)
1. **Curated subset + original copy.** Each site serves a niche-filtered slice
   of the catalog (not the full catalog rendered identically) with original
   per-site landing/category copy. Driven by `niche.pos/neg/types` in each
   `sites/<slug>.json` + per-site `tagline`/`aboutCopy`/`metaDesc`. Domains
   whose intent is geographic/editorial rather than product-category
   (asseeninla, roomsettings, sheltermagazines, wallcovering, etc.) are
   differentiated by original copy + per-site title/meta, not a product filter.
2. **rel=canonical on every product page.** New `/product/:handle` route +
   `render.productPage()`. `rel=canonical` points to the SKU's canonical home
   on the main store (`designerwallcoverings.com/products/<handle>`), so shared
   SKUs never compete with the main store or each other as duplicate content.
   `<title>` + meta description are per-site-unique (carry the site name).

## Status legend
BUILT = config + boot-test pass · DEPLOYED = live on Kamatera pm2 · DNS = A record cut to 45.61.58.125 · SSL = certbot done · VERIFIED = curl 200 own content

## Fleet (44 standalone builds)

| # | Domain | BUILT | DEPLOYED | DNS | SSL | VERIFIED |
|---|--------|-------|----------|-----|-----|----------|
| 1-44 | all 44 build domains | YES (44/44 boot-test) | YES (Kamatera pm2, 2026-05-19) | YES (44/44 → 45.61.58.125) | YES (44/44 LE certs) | YES (44/44 live HTTPS 200 + cert CN match, 2026-05-19) |

Ports 10001–10044. App names `dwf-<slug>` in `ecosystem.config.js`.

## Deploy log — 2026-05-19 (phase: DEPLOY + SMOKE on Kamatera origin)
- `scripts/deploy-fleet.sh` run: rsync to `/root/public-projects/dw-domain-fleet`,
  `npm install --omit=dev`, `pm2 start ecosystem.config.js`, nginx vhosts installed.
- Prior partial deploy had left 3 dwf-* apps crash-looping on stale pre-rename
  ports (9904/9928/9930): asseeninhotels, fabricfridays, flocked. The fresh
  ecosystem (ports 10002/10016/10018) + `pm2 reload /^dwf-/ --update-env`
  brought all 3 online on correct ports.
- pm2 state: **44/44 dwf-* apps online**, ports 10001-10044, 0 not-online.
  No non-dwf process touched. No app deleted (bulk-delete was unnecessary —
  all 44 already healthy after reload; reload reloads fresh code without
  removing the app entry).
- Origin smoke test (`/tmp/dwf-smoke.sh`, Host-header against local nginx):
  **44/44 PASS** — HTTP 200, /health 200, no 301 to designerwallcoverings.com,
  each serving its own unique <title> + content.
- **BLOCKER (nginx vhost conflict):** `typo-redirect-1800wallcoverings.com`
  in sites-enabled claims `server_name 1800wallcoverings.com` — same as the
  new fleet vhost `1800wallcoverings.com.conf`. nginx logged "conflicting
  server name ... ignored". `1800wallcoverings.com` is a BUILD domain (#1,
  port 10001), NOT one of the 22 redirect-only domains. The stale typo-redirect
  vhost must be disabled (rm symlink from sites-enabled, keep sites-available
  copy) so the fleet vhost wins — but this needs Steve's go since it touches a
  typo-redirect file. Smoke test still passed because Host-header curl hits
  whichever vhost nginx picked; at DNS-cutover time the conflict could send
  1800wallcoverings.com traffic to the 301 vhost instead of the fleet site.
  The other 4 typo-redirect vhosts (1800wallpapers/800wallpaper/designerwallcover/
  designerwallcovering) correctly match redirect-only domains — leave them.

## DNS cutover — IN PROGRESS (Steve go given 2026-05-19)

### STEP 0 — nginx blocker RESOLVED 2026-05-19
Stale `typo-redirect-1800wallcoverings.com` sites-enabled symlink removed
(sites-available copy kept intact for reversibility). `nginx -t` ok,
reloaded. Host-header curl for `1800wallcoverings.com` → HTTP 200, own
title, no 301. Other 4 typo-redirect vhosts untouched.

### STEP 1 — Baseline 2026-05-19
`dns-cutover.js --check`: 1/44 already on origin (1800wallcoverings.com,
A-record pre-set at deploy). 43/44 on GoDaddy forwarding (15.197.x/3.33.x).

### STEP 2 — Cutover batches (offsets 0/9/18/27/36)
Batch 1 idx 0-8 · Batch 2 idx 9-17 · Batch 3 idx 18-26 ·
Batch 4 idx 27-35 · Batch 5 idx 36-43.

| Batch | Domains | Cutover | SSL | HTTPS 200 |
|-------|---------|---------|-----|-----------|
| 1 | idx 0-8  1800wallcoverings → carmelwallpaper | 9/9 OK | 9/9 OK | 9/9 200 |
| 2 | idx 9-17 cfafabrics → flocked.org | 9/9 OK | 9/9 OK | 9/9 200 |
| 3 | idx 18-26 grassclothwallcovering → philliperomano | 9/9 OK | 9/9 OK | 9/9 200 |
| 4 | idx 27-35 printmurals → traditionalwhimsy | 9/9 OK | 9/9 OK | 9/9 200 |
| 5 | idx 36-43 unitedstateswallpaper → wc01wallcoverings (8) | 8/8 OK | 8/8 OK | 8/8 200 |

### STEP 2 COMPLETE — 2026-05-19 (resume agent — independently re-verified)
All 5 batches cut over. Final full-fleet verify (live HTTPS curl + openssl
cert CN/expiry check, all 44 build domains): **44/44 PASS** — HTTP 200, valid
non-expired Let's Encrypt cert with matching CN, zero failures. GoDaddy
A-records all → 45.61.58.125 TTL 600; domain-forwarding cleared (the
`/forwards/` DELETE returned 404 on every domain — GoDaddy had no
forwarding-API object; the A-record PUT itself supersedes the forwarding
path, confirmed by the absence of any 301 Location header on all 44 build
domains; redirect-only domains still 301/405-park as expected).
The 22 redirect-only domains received ZERO DNS change.

Resume-agent notes: prior agent stalled after batch 3 DNS (uncommitted).
Resume agent cut + verified batch 4 itself; found batch 5 DNS already cut
+ SSL issued by a concurrent process (live wallpaperny.com certbot observed)
— re-ran `dns-cutover.js --check` (source of truth) confirming idx 36-43
all ON_ORIGIN, then independently SSL+HTTPS-verified all 8. Did not trust
any pre-written table claim; every domain re-curled live.

### Batch 5 cutover — 2026-05-19 (idx 36-43, verified by resume agent)
- unitedstateswallpaper.com   DNS OK · SSL OK · HTTPS 200 · cert CN match
- wallcovering.net            DNS OK · SSL OK · HTTPS 200 · cert CN match
- wallpaperexports.com        DNS OK · SSL OK · HTTPS 200 · cert CN match
- wallpaperfromthe80s.com     DNS OK · SSL OK · HTTPS 200 · cert CN match
- wallpaperny.com             DNS OK · SSL OK · HTTPS 200 · cert CN match
- wallpaperpurchasing.com     DNS OK · SSL OK · HTTPS 200 · cert CN match
- wallpaperweekly.com         DNS OK · SSL OK · HTTPS 200 · cert CN match
- wc01wallcoverings.com       DNS OK · SSL OK · HTTPS 200 · cert CN match

### Batch 3 reconciliation — 2026-05-19 (resumed after prior agent stalled)
Prior cutover agent cut batch-3 DNS but died before committing or confirming
SSL. Resume agent verified via `dns-cutover.js --check`: idx 18-26 all
`ON_ORIGIN=true`. Found prior agent's certbot loop was still alive on
`handmadewallcovering.com` — let it drain, then confirmed all 9 certs issued.
Per-domain HTTPS smoke (live curl, not Host-header):
- grassclothwallcovering.com   DNS OK · SSL OK · HTTPS 200 · cert CN match
- handmadewallcovering.com      DNS OK · SSL OK · HTTPS 200 · cert CN match
- hollywoodwallcovering.com     DNS OK · SSL OK · HTTPS 200 · cert CN match
- hospitalitysalesagency.com    DNS OK · SSL OK · HTTPS 200 · cert CN match
- malibuwallpaper.com           DNS OK · SSL OK · HTTPS 200 · cert CN match
- montereywallpaper.com         DNS OK · SSL OK · HTTPS 200 · cert CN match
- naturalwalltextures.com       DNS OK · SSL OK · HTTPS 200 · cert CN match
- patterndesignlab.com          DNS OK · SSL OK · HTTPS 200 · cert CN match
- philliperomano.com            DNS OK · SSL OK · HTTPS 200 · cert CN match

### Batch 4 cutover — 2026-05-19 (idx 27-35, cut + verified by resume agent)
`dns-cutover.js --batch` ran: 9/9 A-records PUT → 45.61.58.125 TTL 600
(forward-delete 404 = no GoDaddy forward object, expected). DNS propagated
to public + authoritative resolvers in ~15 min. SSL provisioned serially
after waiting out an unrelated wallpaperny.com certbot holding the lock.
- printmurals.com               DNS OK · SSL OK · HTTPS 200 · cert CN match
- restaurantfabrics.com         DNS OK · SSL OK · HTTPS 200 · cert CN match
- restaurantmurals.com          DNS OK · SSL OK · HTTPS 200 · cert CN match
- roomsettings.com              DNS OK · SSL OK · HTTPS 200 · cert CN match
- sheltermagazines.com          DNS OK · SSL OK · HTTPS 200 · cert CN match
- specifywallpaper.com          DNS OK · SSL OK · HTTPS 200 · cert CN match
- stevenabramsphotography.com   DNS OK · SSL OK · HTTPS 200 · cert CN match
- thehotelwallpaper.com         DNS OK · SSL OK · HTTPS 200 · cert CN match
- traditionalwhimsy.com         DNS OK · SSL OK · HTTPS 200 · cert CN match

## Done so far
- dw-site-build skill read in full.
- Shared codebase built: `server.js` + `shared/{catalog,sort,render}.js`.
- Catalog pulled from live DW `/products.json`.
- `gen-configs.js` SPEC table cut from 66 → 44 (22 redirect-only domains
  documented in `REDIRECT_ONLY` array, not emitted as configs).
- `asseeninmovies` config added (noir/bodoni, cinematic-statement niche).
- 44 per-domain configs generated (`sites/*.json`); 22 stale configs deleted.
  Boot-test: **44/44 pass**, 0 thin niches.
- **SEO must-do #1** — niche curation + original copy: in place per-site.
- **SEO must-do #2** — `/product/:handle` route + `render.productPage()`:
  rel=canonical → main store, per-site-unique title/meta, Product JSON-LD.
  Verified on grasscloth exemplar locally.
- Product cards rewritten: `<div class=card>` wrapper (NOT `<a>`) with inner
  `<a class=card-link>` → `/product/:handle` so every per-SKU page is crawlable.
- Grasscloth exemplar verified locally — hero rotation, Gucci nav, Enter CTA,
  sort+density grid w/ localStorage, vendor scrubbed, 0 banned "wallpaper"
  words in editorial copy, valid JSON-LD, product page canonical correct.
- pm2 `ecosystem.config.js` (44 apps) + 44 nginx vhosts regenerated.
- DNS cutover script + SSL script + deploy script written.

## Each site ships (DW-spec)
- Rotating hero (full-bleed crossfade ≥3 imgs, else 4-square) — `background-position` auto-crop.
- Gucci `≡ MENU` corner-nav UR · brand wordmark UL · centered-bottom Enter CTA.
- Catalog grid: sort `<select>` (9 modes) + density range slider, both localStorage-persisted.
- Per-SKU `/product/:handle` page — rel=canonical → main store, Product JSON-LD.
- Dark/light toggle, anti-flash inline script.
- Memo-sample CTA → `designerwallcoverings.com/products/{handle}#sample`.
- helmet + CSP + rate-limit security baseline.
- JSON-LD Organization + canonical + og/twitter meta.
- Word "Wallpaper" banned in all EDITORIAL copy (tagline/about/meta/hero-sub).
  Domain-stem brand wordmark (e.g. wallpaperny.com → "Wallpaper NY") kept —
  the registered domain is the SEO asset.

## Remaining
1. Deploy 44-site fleet to Kamatera (`scripts/deploy-fleet.sh`).
2. DNS cutover in batches of ~10 (`scripts/dns-cutover.js`) — **44 domains only;
   the 22 redirect-only domains get NO DNS change**.
3. SSL provision per batch after propagation (`scripts/provision-ssl.sh`).
4. Smoke-test each batch (curl 200 + own content, not DW redirect).