Read this before touching any Cloudflare config or asking the org admin for help.
Every working session follows a mandatory startup and wrapup ritual. The session is not considered closed until the wrapup checklist is posted. This applies to all contributors.
date in the terminal — note the exact timestamp. Use it for all log entries this session.status.md — note what was open at the end of the last session.Do not initiate wrapup without being asked. When wrapping up:
date again — record the end timestamp.data/devlog.json — required; see entry schema in CLAUDE.md. Every session gets an entry, even short or inconclusive ones. Include your github handle./opt/homebrew/bin/python3 scripts/devlog_render.py
This writes public/devlog.html, served at /devlog.
status.md.data/devlog.json and public/devlog.html together — always as a pair.origin main.CLAUDE.md).The devlog is public and serves as the engineering record for the project. Write items as if briefing a future engineer — explain why decisions were made, not just what changed.
The ritual is defined in CLAUDE.md at the repo root. Coding agents that read this file will enforce it automatically — including running date, writing the devlog entry, regenerating HTML, committing, and posting the checklist.
claude from the repo root.CLAUDE.md automatically — the ritual is loaded into context at session start.date, reads status.md, and asks what you're working on.CLAUDE.md in your agent's system prompt or context window at the start of each session.CLAUDE.md in context won't volunteer them.Jamverse runs entirely on Cloudflare. There is no separate server, no VPS, no traditional hosting. Three Cloudflare products do the work:
| Product | What it is | What Jamverse uses it for |
|---|---|---|
| Pages | Static site hosting with optional serverless functions | Serves the site at jamverse.protocolized.io |
| D1 | SQLite database, runs on the edge | Users, roles, content, submission metadata |
| R2 | Object storage — like S3, but no egress fees | Media uploads: images, video, documents |
The form handler that accepts submissions is a Cloudflare Worker — a serverless function that runs on the edge. It receives form POSTs, writes metadata to D1, and writes media to R2.
The database (jamverse-db) and media bucket (jamverse-media) are already provisioned. You don't need to create them.
You do not push to Cloudflare through a web dashboard. Everything goes through wrangler, Cloudflare's CLI tool.
npm install -g wrangler # install once, globally
To deploy the site:
wrangler pages deploy public/ --project-name jamverse --commit-dirty=true
This uploads everything in public/ and makes it live in about 10 seconds.
To deploy the worker (once it exists in worker/):
cd worker && wrangler deploy
Critical: wrangler 4.x defaults to local miniflare storage when a wrangler.toml is present in the current directory. Always use --remote for live CF resources:
wrangler r2 object put jamverse-media/key --file=... --remote
wrangler d1 execute jamverse-db --remote --file=schema/001_init.sql
You do not need a Cloudflare account. You do not need to log into the Cloudflare dashboard. Everything is done through wrangler using an API token — a credential that gives wrangler exactly the permissions it needs for this project and nothing else.
Your token is scoped specifically to Jamverse. It can deploy Pages, query D1, read/write R2, and set Worker secrets. It cannot touch anything else in the org account.
Create a .env file in the repo root (it is gitignored):
CLOUDFLARE_API_TOKEN=<your token — ask the org admin>
CLOUDFLARE_ACCOUNT_ID=7e8c7969b2464d23795c555bc6a32af8
Then either source .env before running wrangler, or prefix commands:
env $(cat .env | grep -v '^#' | xargs) wrangler pages deploy public/ ...
After creating .env, mark it so Dropbox does not sync it (if you use Dropbox):
xattr -w com.dropbox.ignored 1 .env
D1 is SQLite. If you know SQL, you know D1. The database is jamverse-db and holds everything: users, roles, content, submissions. Schema migrations live in schema/.
# Run a query
wrangler d1 execute jamverse-db --command "SELECT * FROM submissions LIMIT 10;"
# Run a SQL file (for migrations)
wrangler d1 execute jamverse-db --file schema/001_init.sql
In the Worker, D1 is accessed via a binding object injected into the Worker's environment. The binding name is DB (defined in wrangler.toml):
// Insert a submission
const result = await env.DB.prepare(
"INSERT INTO submissions (user_id, content, created_at) VALUES (?, ?, ?)"
).bind(userId, content, Date.now()).run();
// Fetch a user
const user = await env.DB.prepare(
"SELECT * FROM users WHERE id = ?"
).bind(userId).first();
R2 is object storage. Blobs go in, blobs come out. The bucket is jamverse-media. Use path prefixes to keep things organized:
| Prefix | Contents |
|---|---|
submissions/{id}/ | Media attached to a specific submission |
avatars/{user_id}/ | User profile images |
assets/ | Shared static assets |
wrangler r2 object put jamverse-media/submissions/test/photo.jpg --file ./photo.jpg --remote
wrangler r2 object get jamverse-media/submissions/test/photo.jpg --file ./out.jpg --remote
Binding name is MEDIA (defined in wrangler.toml):
await env.MEDIA.put(key, file.stream(), {
httpMetadata: { contentType: file.type }
});
const obj = await env.MEDIA.get(key);
Secrets — API keys, auth tokens — are set via wrangler and stored encrypted by Cloudflare. Never put them in code or .env:
wrangler secret put MY_SECRET_KEY
# prompts for value, stores it encrypted on CF's end
Secrets are then available in the Worker as env.MY_SECRET_KEY.
jamverse.protocolized.io is a subdomain of protocolized.io, managed by the Protocol Institute org on Cloudflare. The DNS record is already set up and points to this Pages project. You do not need to touch DNS.
The Pages project also has a *.jamverse.pages.dev URL that always works — useful for testing before the custom domain propagates or to verify a deploy independently.
Cloudflare Zero Trust (Access) can gate any URL path behind an identity check — GitHub login, Google OAuth, one-time PIN email — without any code changes. A gated path redirects to a CF login page before the request reaches your worker or static files.
This is an org-level configuration the dev cannot set up directly. If you need a path protected (e.g. /admin/*, a moderation dashboard, an invite-only preview), file a request in zero-trust-requests.md using the template in that file. The org admin will configure it and confirm when it's live.
protocolized.io zone or DNS settingsThe only things outside your token's scope are creating new D1 databases or R2 buckets (account-level operations) and configuring Zero Trust policies. For everything else — schema changes, bucket contents, deploys, secrets — your token is sufficient.