DUNIN7 · Loomworks · Phase 61

record.dunin7.com production deployment

Scoping note v0.1·2026-05-22·Working draft
What this document is

The scoping note for landing record.dunin7.com as a real, deployed, authenticated site. The prototype validated the navigation, design language, and sort affordances. This phase moves from local-file prototype to served-from-the-web production deployment behind Cloudflare Access.

The sequence has Operator-side steps (Cloudflare dashboard work, DNS configuration, passkey enrollment) and Claude-side steps (build script production updates, GitHub Action, scoping documents). Each step is named with its actor so coordination is unambiguous.

Decisions carried in from prior conversation

Subdomain
record.dunin7.com

On the operating-account domain (dunin7.com), not the product domain (loomworks.com or doneinseven.com). The record is operator-facing infrastructure, not customer-facing product.

Coverage
Full repository

All folders, all documents. Methodology, architecture, protocols, phases, investigations, session handoffs, candidate seeds, queued directions, standing notes, brand, analyses, examples, change requests, current status, discovery. Not just the protocol specifications.

Read/edit boundary
Current versions editable; archive read-only

Documents at folder root are current and editable. Documents in archive/ subfolders are historical and read-only. Editor itself ships after the read viewer is in use and editing affordances are informed by lived experience.

Access control posture
Cloudflare Access · passkey primary · TOTP fallback · re-auth for edits

Cloudflare Access authenticates before any request reaches the site. Primary auth: passkey (Touch ID, Face ID, hardware key). Fallback: TOTP via Cloudflare's one-time PIN by email. Editing operations (when the editor ships) require fresh re-authentication. Recovery codes printed and stored physically.

Editing scope (deferred until editor build)
Casual edits and new artifacts

Quick edits to existing documents and capture of new artifacts (investigations, standing notes, session handoffs) from anywhere. NOT: CR drafting (stays in Claude.ai), phase work (stays in Claude.ai-and-Claude-Code), substantive document rewrites (stay in Claude.ai conversations).

Build approach
Static site generated on push to main

GitHub Action runs the build script (tools/build_site.py, moved from Downloads into the repository) on every push to main. Generated site deployed to Cloudflare Pages. Same architecture as the prototype, but auto-rebuilt and served over HTTPS.

What changes from the prototype

Build script production updates

The prototype build script lives at /home/claude/build_site.py (in a session sandbox) and reads from a local repository copy. The production version lives at tools/build_site.py inside loomworks-record itself. Differences:

What does NOT change from the prototype

The deployment sequence

Eleven steps. Each named with its actor (Claude.ai = Claude.ai produces an artifact; Claude Code = build machine executes; Operator = dashboard work or DNS or enrollment). Sequence ordering matters — earlier steps unblock later ones.

Claude.ai
Produce the production build script

Update build_site.py with the production changes named above: git log timestamps, inline HTML rendering, drop prototype chrome. Stage for Claude Code commit at tools/build_site.py.

Claude.ai
Produce the GitHub Action workflow

YAML workflow at .github/workflows/deploy.yml. Triggers on push to main. Installs Python, runs the build script, deploys the output to Cloudflare Pages using the official Cloudflare deploy action. About fifty lines.

Claude.ai
Produce the Claude Code kickoff to commit the production scaffolding

Kickoff prompt for Claude Code that lands tools/build_site.py and .github/workflows/deploy.yml on the repository. Single commit. The build script will not yet succeed because Cloudflare Pages doesn't exist — that's fine; the commit is sequencing-only.

Operator
Create the Cloudflare Pages project

In the Cloudflare dashboard: Workers & Pages → Pages → Create application → Connect to Git → select DUNIN7/loomworks-record. Build settings: framework preset = None; build command = python3 tools/build_site.py; build output directory = record-site/ (the build script writes here). Save. The first deploy fails because Python isn't installed in Cloudflare's default build image; the GitHub Action approach (next step) handles this.

Or, alternative simpler path: skip Cloudflare's Git integration entirely and have the GitHub Action push built artifacts to Cloudflare via the Wrangler CLI. Slightly more complex Action; simpler Cloudflare project setup. I'll recommend in the build script step which to use.

Operator
Generate a Cloudflare API token for the GitHub Action

In the Cloudflare dashboard: My Profile → API Tokens → Create Token. Use the "Edit Cloudflare Workers" template (which includes Pages). Scope to the account level. Copy the token (Cloudflare shows it exactly once).

Operator
Add the Cloudflare token as a GitHub secret

In the GitHub repository: Settings → Secrets and variables → Actions → New repository secret. Name: CLOUDFLARE_API_TOKEN. Value: paste the token. Also add CLOUDFLARE_ACCOUNT_ID (visible on the Cloudflare dashboard right sidebar).

Claude Code
Push the deploy commit to trigger the first build

After the secrets are in place, push a small commit (touching the workflow file is enough) to trigger the GitHub Action. The Action runs the build script, deploys to Cloudflare Pages. The site is now live at the Cloudflare-assigned *.pages.dev URL, but not yet at record.dunin7.com and not yet behind Access.

Operator
Add the custom domain

In the Cloudflare Pages project: Custom domains → Set up a custom domain → record.dunin7.com. Cloudflare creates the CNAME automatically because dunin7.com is already on Cloudflare DNS. Within a few minutes, record.dunin7.com resolves to the Pages project.

Operator
Configure Cloudflare Access

In the Cloudflare dashboard: Zero Trust → Access → Applications → Add an application → Self-hosted. Application name: "Loomworks Record". Application domain: record.dunin7.com. Session duration: 24 hours (sensible default; configurable).

Configure the policy: Action = Allow. Include = Emails (your email address). Require = Authentication method must include hardware key or passkey, with one-time PIN as fallback. This is configured in the Access policy builder, not in code.

Operator
Enroll the passkey

Visit record.dunin7.com. Cloudflare Access intercepts. Choose authentication method → select passkey (or hardware key). Browser walks through enrollment (Touch ID, Face ID, or hardware key tap). Cloudflare records the credential.

Print and store the recovery codes Cloudflare provides. Physical storage. This is the path-back-in if you lose the phone and the laptop on the same day.

Operator
Confirm the site works end-to-end

From a fresh browser session: visit record.dunin7.com. Authenticate. Land on the Record landing page. Navigate to a section. Open a document. Click Back. Confirm the experience matches the prototype, with HTML documents now rendering inline rather than opening in a new tab. Test from a phone if convenient.

After deployment lands

Once record.dunin7.com is live, three follow-up considerations:

The going-forward operating instructions update

The operating instructions v0.2 mention "Cross-session sync — the open question" as the only unresolved item. record.dunin7.com doesn't fully resolve it (the MCP connector is still the target), but it changes the workable patterns. Specifically: Claude.ai sessions can be told to read documents from record.dunin7.com URLs rather than from uploaded files. The session-context record can name URLs alongside uploads. A small v0.3 of the operating instructions captures this change.

The editor phase

After one to two weeks of using the read viewer in lived experience, the editor build is scoped from what you actually missed. Today's design intent — Monaco editor, GitHub API commits, archive-move-on-version-bump — is informed by the prototype's chrome. A real editor scope comes from real use.

Production hardening (deferred, not part of this phase)

None of these block deployment, but they're worth tracking:

What this phase does not do

Halt criteria — where to stop and check

Several points in the sequence where the right behavior is to pause and confirm before continuing.

Step 04 — before clicking Save in Cloudflare

If the Cloudflare Pages project's build settings don't accept Python or don't write to record-site/, the build will fail. Confirm the build command and output directory match what the build script produces before saving. If unclear, halt and ask Claude.ai.

Step 07 — before pushing the deploy commit

Verify both CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID are set in GitHub Secrets. Without them the Action will fail. Look at GitHub → Settings → Secrets → Actions to confirm both names appear.

Step 09 — before saving the Access policy

Confirm the policy is set to require authentication, not just identify. The default policy may allow anyone with a verified email; the policy this phase wants requires passkey (with TOTP fallback) for a specific email address. If unclear, halt and ask Claude.ai for screenshots of the right policy shape.

Step 10 — before storing recovery codes

Recovery codes are shown exactly once. If they're not stored physically before closing the tab, regenerating them is a small administrative pain. Print them. Or write them down. Then move on.

The next immediate action

If you concur with this scoping note, the next concrete step is for Claude.ai to produce two artifacts:

  1. The production build script. tools/build_site.py, with the production updates described above.
  2. The GitHub Action workflow. .github/workflows/deploy.yml.

Both bundled into a single Claude Code kickoff that commits them to loomworks-record. After that commit lands, you start the Operator-side Cloudflare dashboard work (steps 04 through 11).

The kickoff also commits this scoping note itself to phases/phase-61-record-deployment/scoping-note-v0_1.html so the phase folder exists and the record of this decision is in the repository alongside the work it scopes.