r2-postplan
v0.1.0
Published
Publish versioned HTML drafts to Cloudflare R2 and serve them through a Worker
Readme
r2-postplan
Publish one HTML draft to Cloudflare R2 and serve it through a tiny Worker. The stable URL always shows the latest publish, and every changed publish also gets a permanent version URL.
Requirements
- Bun, or Node.js 22.18+
- An existing R2 bucket
- Bucket-scoped R2 credentials with object read and write access
Set credentials outside the project:
export R2_POSTPLAN_ACCESS_KEY_ID=...
export R2_POSTPLAN_SECRET_ACCESS_KEY=...Temporary credentials may also set R2_POSTPLAN_SESSION_TOKEN.
The CLI also accepts CLOUDFLARE_R2_ACCESS_KEY_ID and CLOUDFLARE_R2_SECRET_ACCESS_KEY, so the 1Password postplan Environment can be mounted directly at the project root as .env.
Worker
This repository is a Bun workspace: the npm CLI stays at the root and worker/ contains the postplan Worker.
Create the Worker environment file and set it to your existing R2 bucket, then deploy:
bun install
cp worker/.env.example worker/.env
# Edit worker/.env: CLOUDFLARE_R2_BUCKET=your-bucket
bun run worker:deployThe 1Password postplan Environment can be mounted directly at worker/.env; its existing CLOUDFLARE_R2_BUCKET, account ID, and API token names work as-is.
Cloudflare assigns a URL shaped like https://postplan.<account-subdomain>.workers.dev. The Worker maps clean public paths to the existing R2 objects:
/<draft-id> -> drafts/<draft-id>/index.html
/<draft-id>/v2 -> drafts/<draft-id>/v2/index.htmlSetup
bunx r2-postplan setup ACCOUNT_ID BUCKET https://postplan.<account-subdomain>.workers.devSetup validates bucket access and stores only the account ID, bucket, S3 endpoint, Worker URL, and optional credential-file path. For a jurisdiction-specific or test endpoint, pass --endpoint URL.
Pass --env-file /absolute/path/to/.env to remember a 1Password-mounted environment file. The path is stored in config; the secrets stay in 1Password.
Publish
bunx r2-postplan publish ./draft.htmlProgress and the permanent version URL go to stderr. Only the stable URL goes to stdout, so it is safe to capture in scripts.
# Publish the same file as a separate draft
bunx r2-postplan publish ./draft.html --new
# Reconnect a local file to a remote draft
bunx r2-postplan publish ./draft.html --draft a1b2c3d4e5f6
# Skip the Worker-byte verification
bunx r2-postplan publish ./draft.html --no-waitOnly one regular .html file is uploaded. Use self-contained HTML or absolute asset URLs. The bucket can stay private; no public r2.dev URL is needed.
List and delete
bunx r2-postplan list
bunx r2-postplan deletelist always reads R2, so it works on a new machine and repairs stale or broken display cache. delete uses the same remote list; select drafts with the arrow keys and Space, then press Enter. Cancellation changes nothing.
Configuration lives under $XDG_CONFIG_HOME/r2-postplan (normally ~/.config/r2-postplan). R2 is the source of truth; local files are disposable cache, source-path mappings, and a short-lived journal used only to finish an interrupted publish without duplicating its permanent version.
The Worker exposes only draft pages and the short-lived object used by setup to verify access. This tool does not create buckets, upload assets, or configure custom domains.
Development
bun install
bun run check
bun test
bun run buildBefore a release, set R2_POSTPLAN_LIVE_ACCOUNT_ID, R2_POSTPLAN_LIVE_BUCKET, and R2_POSTPLAN_LIVE_WORKER_URL alongside the credentials, then run bun run test:live. The opt-in check publishes two versions, measures Worker visibility, and cleans up its draft.
The npm package includes an agent skill at skills/r2-postplan/SKILL.md.
