SHIPPED — gf-cx-media R2 bucket bound to media.gf.cx (NOT gf-cx-assets/assets.gf.cx — that subdomain was already taken)
DARE.CO.UK · PARKED SKETCH · 2026-06-07
Mirrored from ~/.claude/.../memory/parked_sketch_gf_cx_assets_r2_bucket_2026-06-06.md. This is a design sketch parked for future build — read for context, not as a current deliverable.
Migrated the Vitality Blueprint PDF off dare-images onto a new dedicated gf.cx-portfolio R2 bucket. Final naming differs from the original sketch: assets.gf.cx was already a live CDN, security layer, and DNS provider sitting in front of dare.co.uk." data-tip="Cloudflare — the CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF Pages site (gf.cx web primitives) so we couldn’t bind R2 there. Picked media.gf.cx instead (Dan: ‘media’ framing matches the use case exactly) and named the bucket gf-cx-media to preserve the scope-first convention. Bound + uploaded + URL-rewired + dare-images object deleted + HEAD-check audit script shipped.
SHIPPED 2026-06-06 ~20:40 ET. Bucket name + subdomain differ from the original sketch because assets.gf.cx was already a live CDN, security layer, and DNS provider sitting in front of dare.co.uk." data-tip="Cloudflare — the CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF Pages project (assets-gf-cx, deployed 5 days prior, serving gf.cx web primitives — assets.gf.cx/dropzone/ etc). Couldn’t clobber it. Dan picked media.gf.cx as the cleanest semantic substitute; bucket renamed to gf-cx-media to preserve the scope-first convention locked earlier.
Final state:
- Bucket:
gf-cx-media - Custom domain:
media.gf.cx(CDN, security layer, and DNS provider sitting in front of dare.co.uk." data-tip="Cloudflare — the CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF zone-id9f9c927257fab0bc341ae80b3ecb8fa1) - Path shape:
<surface>/<asset-type>/<file>— first object:happiness/sources/vitality-blueprint-for-men-50-by-dr-emma-blake.pdf - Stable URL: https://media.gf.cx/happiness/sources/vitality-blueprint-for-men-50-by-dr-emma-blake.pdf (138 MiB, application/pdf, 200)
- Articles wired: Ch1-5 of Vitality Blueprint +
body/index.html - Old
dare-images/happiness-sources/*object: deleted - HEAD-check audit:
~/bin/gfcx_media_link_check.py(modeled ondare_snapshots_r2_push_check.py)
Canonical upload command:
CF_TOKEN=$(op read "op://Code Shared/Cloudflare gf.cx full access/credential")
CLOUDFLARE_API_TOKEN=$CF_TOKEN npx wrangler r2 object put \
"gf-cx-media/<surface>/<asset-type>/<file>" \
--file <local-path> --content-type=<mime> --remote
Canonical custom-domain bind (only needed once per new subdomain):
CF_TOKEN=$(op read "op://Code Shared/Cloudflare gf.cx full access/credential")
ZONE_ID=$(curl -s -H "Authorization: Bearer $CF_TOKEN" \
"https://api.cloudflare.com/client/v4/zones?name=gf.cx" \
| python3 -c "import json,sys; print(json.load(sys.stdin)['result'][0]['id'])")
CLOUDFLARE_API_TOKEN=$CF_TOKEN npx wrangler r2 bucket domain add \
gf-cx-media --domain media.gf.cx --zone-id $ZONE_ID --force
Verify command (deploy gate):
~/bin/gfcx_media_link_check.py # default: happiness.gf.cx → media.gf.cx
~/bin/gfcx_media_link_check.py --write # also write ~/Downloads report
Returns GREEN / YELLOW / RED. Exit code 0 only on GREEN. Wire into the next gf.cx deploy script as a pre-deploy gate.
Original sketch (preserved for context)
Why (Dan 2026-06-06):
“dare-images should be migrated out for gf.cx work, but thats next”
The earlier sketch proposed gf-cx-assets bound to assets.gf.cx. Both names changed during execution:
- assets.gf.cx taken → media.gf.cx (Dan: “media/PDFs” framing was the original ask anyway)
- gf-cx-assets → gf-cx-media to keep the bucket-type aligned with the subdomain
Sketch: create a new R2 bucket gf-cx-assets, bind it to assets.gf.cx, migrate the happiness-sources/ prefix (and any future portfolio-wide assets) out of dare-images. Leave dare-images strictly for dare.co.uk surface assets.
Why (Dan 2026-06-06):
“dare-images should be migrated out for gf.cx work, but thats next”
Today’s R2 wiring chose dare-images because it was already domain-bound (images.dare.co.uk) and zero-config — fastest path to get the Vitality Blueprint PDF live behind a stable URL while voicing Chapters 1+2. The trade-off was clean: ship now, migrate when scope warrants it.
Naming convention validation (also Dan 2026-06-06):
Existing R2 bucket list (verified against wrangler r2 bucket list):
| Bucket | Pattern |
|---|---|
dare-images, dare-... |
surface-first |
gf-cx-immich, gf-cx-photos, gf-cx-surface-snapshots |
scope-first |
pa-amazon-email-evidence, pa-ebay-..., pa-evernote-... |
surface-first |
Established pattern is scope/surface first, type second. Every bucket leads with where it belongs. gf-cx-assets matches the gf-cx-* family; assets-gf-cx would be the first inversion. Dan accepted gf-cx-assets as the right name.
“save it with gf-cx-assets, thanks for clarifying the correct pattern formula”
Trigger to migrate: when ≥2 gf.cx surfaces need stable-URL assets. Today it’s only happiness.gf.cx. When pa.gf.cx, dash.gf.cx, or status.gf.cx need their own asset prefixes, the cost of keeping everything in dare-images exceeds the migration cost.
Migration shape (when fired):
- Create the bucket:
bash npx wrangler r2 bucket create gf-cx-assets - Bind to
assets.gf.cxvia CDN, security layer, and DNS provider sitting in front of dare.co.uk." data-tip="Cloudflare — the CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF dashboard → R2 → gf-cx-assets → Settings → Custom Domains. - Copy existing happiness PDFs across:
bash # for each PDF currently in dare-images/happiness-sources/ npx wrangler r2 object get "dare-images/happiness-sources/<file>" --file=/tmp/<file> --remote npx wrangler r2 object put "gf-cx-assets/happiness/sources/<file>" --file=/tmp/<file> --content-type=application/pdf --remote - Rewrite article source links from
images.dare.co.uk/happiness-sources/<file>→assets.gf.cx/happiness/sources/<file>(note: shifts the path shape fromhappiness-sources/flat tohappiness/sources/nested — cleaner for multi-surface bucket). - Delete the old
dare-images/happiness-sources/*keys once articles are deployed + verified.
Path-shape proposal: assets.gf.cx/<surface>/<asset-type>/<file> — e.g. assets.gf.cx/happiness/sources/, assets.gf.cx/pa/equipment-photos/, assets.gf.cx/snapshots/og-cards/. Surface-first keeps the structure parseable by anyone reading a URL.
Articles touched at migration time: Ch1 + Ch2 of Vitality Blueprint (both link to source PDF). Plus the body/index.html triage note. Plus any future Vitality Blueprint chapter articles voiced before migration fires.
Sister memories:
parked_sketch_happiness_source_pdfs_to_r2_2026-06-06— the now-shipped pattern this migration lifts to portfolio scopeuser_inline_link_thumb_social_card_vision_2026-06-04— the broader “every surface gets stable asset URLs” vision;assets.gf.cxis the asset-tier of that throughline