Mint instructions — R2 portfolio-image-promotion
Why: decoupled, guard-railed credential for Claude Code to do ongoing portfolio image-promotion work (dare → dogwood → audrey) without reaching into the Private vault. Follows the layered guard-rail stack — see feedback_layered_guardrail_stack.md.
One-time setup. Estimated time: ~3 minutes at CDN, security layer, and DNS provider sitting in front of dare.co.uk.">CF dashboard + ~1 minute in 1Password desktop.
Layered scope summary
| Layer | What it allows | What it blocks |
|---|---|---|
| Secret-store | Code Shared vault (Claude SA can read) | Private vault stays untouched |
| Resource | Buckets dare-images, dogwood-photos only |
All other buckets (edge, xlabs, future client buckets) |
| Operation | Object Read & Write | Bucket create/destroy, account admin |
| Wrapper | ~/bin/r2-portfolio-promote (to build, post-mint) |
Free-form aws s3 shell calls |
Step 1 — Mint at Cloudflare (R2-native token UI, not Custom Token)
URL: https://dash.cloudflare.com/2366f43fb08cc98065551599ad8e6e63/r2/api-tokens
Click: Create API token
Settings:
- Token name: portfolio-image-promotion
- Permissions: Object Read & Write (do NOT pick Admin)
- Specify bucket: Apply to specific buckets only → tick:
- ✅ dare-images
- ✅ dogwood-photos
- ❌ leave edge and xlabs unticked
- TTL: blank (no expiry — revisit at the 8th-of-month safety review)
- Client IP filter: blank
Capture all four values before closing the modal — Cloudflare shows the secret exactly once:
1. Access Key ID
2. Secret Access Key
3. Token value (R2-native; usually unused if using aws s3 style access, capture anyway)
4. S3-compatible endpoint URL (https://2366f43fb08cc98065551599ad8e6e63.r2.cloudflarestorage.com)
Step 2 — Store in 1Password desktop app
op CLI can’t create items in service-token mode — has to be the desktop UI (per feedback_1password_desktop_for_new_tokens).
- Vault:
Code Shared(NOT Private) - Category: Password (NOT API Credential — per
feedback_1password_api_credential_category_trap) - Title:
R2 portfolio-image-promotion - Fields:
| Label | Type | Value |
|---|---|---|
access_key_id |
text | from step 1 |
secret_access_key |
CONCEALED | from step 1 |
token |
CONCEALED | from step 1 |
endpoint |
text | https://2366f43fb08cc98065551599ad8e6e63.r2.cloudflarestorage.com |
buckets |
text | dare-images, dogwood-photos |
valid from |
date | today (2026-05-14) |
notes |
text | “Portfolio image promotion — Object R/W on dare-images + dogwood-photos. Re-mint when audrey-images comes online. Spec: ~/Downloads/mint_r2_portfolio_token_2026-05-14.md” |
Step 3 — Verify (you do this; ping me when done)
After the 1Password item is saved, run this — it round-trips the credential and tries a no-op list:
AWS_ACCESS_KEY_ID=$(op read "op://Code Shared/R2 portfolio-image-promotion/access_key_id") \
AWS_SECRET_ACCESS_KEY=$(op read "op://Code Shared/R2 portfolio-image-promotion/secret_access_key") \
aws s3 ls s3://dare-images/ \
--endpoint-url "https://2366f43fb08cc98065551599ad8e6e63.r2.cloudflarestorage.com" \
--region auto \
| head -5
Expected: a list of objects (or empty if bucket is empty). 403 means token scope is wrong. 404 means bucket name typo.
Step 4 — What Claude does next (after you confirm verified)
- Use the token to list contents of the
edgebucket — wait, this token doesn’t includeedge. Decision needed: either addedgeto the include list at mint time (if the Tier 1 image lift will go there), OR migrate the 3edge/*images intodare-imagesunder a cleanedge/prefix and retire theedgebucket entirely. Recommend the latter — fewer surfaces to guard, fewer hostnames to wire. - Verify whether the 3 Tier 1 images already exist in
dare-imagesoredgebucket (cheap probe before any upload). - Apply SEO-naming convention (
debating-glass-room-thumb.jpg,henri-cartier-bresson.jpg,steven-soderbergh.jpg). - Upload via
aws s3 cp ... s3://dare-images/edge/...todare-imagesbucket. - Rewrite the 3 HTML refs from
wp-content/uploads/edge/*→images.dare.co.uk/edge/*(or whatever the production CDN hostname is). - Move on to Tier 2 (17 sitemap-referenced uploads).
Rollback / revocation
Single click at the same R2 tokens page → Revoke. The op://Code Shared/R2 portfolio-image-promotion/* references will start returning auth errors immediately; no caching to worry about. Update or delete the 1Password item if revoked.
Mint-once, scope-narrow, verify-then-trust. The credential never leaves your screen and the desktop app; the secret-store layer keeps it from anywhere Claude shouldn’t reach.