← Jamverse

Developer Orientation

Read this before touching any Cloudflare config or asking the org admin for help.

Session rituals & coding agents

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.

Startup

  1. Run date in the terminal — note the exact timestamp. Use it for all log entries this session.
  2. Read status.md — note what was open at the end of the last session.
  3. Declare what work is planned.

Wrapup

Do not initiate wrapup without being asked. When wrapping up:

  1. Run date again — record the end timestamp.
  2. Append an entry to data/devlog.json — required; see entry schema in CLAUDE.md. Every session gets an entry, even short or inconclusive ones. Include your github handle.
  3. Regenerate the devlog page:
    /opt/homebrew/bin/python3 scripts/devlog_render.py
    This writes public/devlog.html, served at /devlog.
  4. Update status.md.
  5. Commit data/devlog.json and public/devlog.html together — always as a pair.
  6. Push to origin main.
  7. Post the wrapup checklist (format in 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.

Getting your coding agent to follow the ritual

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 Code — recommended
  1. Install from claude.ai/code or the VS Code extension, then run claude from the repo root.
  2. Claude Code reads CLAUDE.md automatically — the ritual is loaded into context at session start.
  3. Open with "new session" to trigger startup: the agent runs date, reads status.md, and asks what you're working on.
  4. When you're done, say "wrap up" or "what did we do" — the agent runs the full wrapup sequence, writes the devlog entry, regenerates the HTML, commits, and pushes.
  5. The session is not complete until the agent posts the wrapup checklist. If it skips any step, push back.
Other agents
  1. Include the full contents of CLAUDE.md in your agent's system prompt or context window at the start of each session.
  2. Tell the agent explicitly: "Follow the session ritual defined in CLAUDE.md."
  3. Verify startup and wrapup steps were executed — agents without CLAUDE.md in context won't volunteer them.

The stack

Jamverse runs entirely on Cloudflare. There is no separate server, no VPS, no traditional hosting. Three Cloudflare products do the work:

ProductWhat it isWhat 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.

How you deploy

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

Authentication

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 — the database

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/.

CLI

# 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

Worker bindings

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 — media storage

R2 is object storage. Blobs go in, blobs come out. The bucket is jamverse-media. Use path prefixes to keep things organized:

PrefixContents
submissions/{id}/Media attached to a specific submission
avatars/{user_id}/User profile images
assets/Shared static assets

CLI

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

Worker bindings

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);

Worker secrets

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.

The subdomain

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.

Zero Trust access gating

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.

What you don't need to touch

The 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.

References