Build log ยท 2026-06-07
Sheriff Lone Star Cloudflare Worker + D1 backend setup.
A practical build log for moving the Las Jaras Sheriff board from browser-local storage to a real shared production backend.
Why This Upgrade Is Needed
The first Sheriff Lone Star board used browser storage because it let the public static page work immediately: no login, no server, and no database. That was fine for a prototype, but it broke the production promise the moment a case was entered from a mobile phone. Browser storage belongs to one browser on one device, so a case created on a phone cannot reliably appear on a laptop, in an admin view, or in a later Sheriff investigation. A real Sheriff needs jurisdiction beyond a single browser tab. Cloudflare Worker plus D1 gives the form a small public API where cases can be created, listed, updated, checked off, and closed across devices while keeping write logic off the static GitHub Pages site.
Backend Choice
Chosen first pass: Cloudflare Worker + D1.
D1 is a better first fit than KV for Sheriff cases because cases are records with status, update time, checklist state, and closeout history. KV is useful for simple key-value blobs, but D1 gives cleaner listing and updating for a case board.
Setup Steps And Timing
| Step | Status | Estimated Time | Notes |
|---|---|---|---|
| Confirm the production problem | Complete | 5 minutes | Verified GitHub Pages is static and browser storage cannot sync cases across mobile and desktop. |
| Pick backend shape | Complete | 10 minutes | Selected Worker + D1 so the static site can call a small API for create, list, update, and close actions. |
| Scaffold Worker API | Complete | 20 minutes | Created workers/sheriff-lone-star-case-api/worker.js with CORS, list, create, read, and update routes. |
| Create D1 schema | Complete | 8 minutes | Created a compact cases table storing case JSON plus indexed status and updated timestamps. |
| Add admin close token | Complete | 10 minutes | Public case creation can remain open, but closing a case now requires X-Sheriff-Admin-Token matching the Worker secret ADMIN_TOKEN. The token is stored as a Cloudflare Worker secret and is not committed to the repo. |
| Create Cloudflare D1 database | Complete | 5 to 10 minutes | Created sheriff_lone_star_cases in Cloudflare D1 and bound it to the Worker as env.DB. The generated database ID is kept in deploy config, not repeated in this public note. |
| Apply schema to D1 | Complete | 3 to 5 minutes | Applied schema.sql remotely with npx wrangler d1 execute sheriff_lone_star_cases --remote --file=schema.sql. |
| Deploy Worker | Complete | 5 to 10 minutes | Deployed the D1-bound Worker to https://sheriff-lone-star-case-api.ilanajill.workers.dev. |
| Smoke-test Worker API | Complete | 8 minutes | Verified /health, case create, case list, unauthenticated close rejection, authenticated POST /cases/:id/close, and cleanup of the temporary test row. |
| Wire Sheriff page to API | Complete | 25 to 45 minutes | The public Sheriff page now loads, creates, updates checklist state, and closes cases through the Worker API, with browser storage as fallback. |
| Add case confirmation receipt | Complete | 15 minutes | Added a visible post-submit receipt with case ID, status, evidence state, update state, and a case-board link so mobile users can tell when a case actually filed. |
| Test cross-device flow | Next | 15 to 25 minutes | Submit one case on mobile, verify it appears on desktop, update checklist status, then close it. |
| Media handling decision | Complete | 20 to 40 minutes | The Worker evidence route now has an R2 bucket binding, private object path, upload limits, and health-check policy output. Bucket sheriff-lone-star-evidence was created and deployed as env.EVIDENCE. |
| Set mobile evidence upload limit | Complete | 10 minutes | Direct uploads are capped at 25 MB per file and 6 files per request. Larger videos should go through chat/manual evidence handling or a future R2 multipart/direct-upload flow. |
| Add evidence lifecycle cleanup on closeout | Complete | 15 minutes | Closing a case now runs an admin cleanup flow: stored R2 evidence objects are deleted, attachment records are cleared from the D1 case payload, and the close response reports the cleanup count. |
| Add active investigation workflow state | Complete | 20 minutes | New cases now carry a durable Sheriff workflow record in D1: queued, in progress, needs evidence, ready for review, or resolved. The mobile receipt and case board show the workflow state and next action. |
| Add human follow-up capture | Complete | 20 minutes | Open cases now show a follow-up text box and photo/video picker where the human can answer Sheriff questions. Text and file metadata save back to D1, then the case re-enters the investigation queue. |
| Add email evidence fallback | Retired | 20 minutes | FormSubmit was tested as a no-R2 evidence bridge, but mobile browser delivery was unreliable and mailbox receipt could not be verified. The board now records file metadata in D1 and routes actual image review through the direct Gmail/chat admin workflow. |
| Add Google Drive evidence storage | Ready for credentials | 20 to 30 minutes after Google setup | The Worker evidence route can now upload evidence files to Google Drive when GOOGLE_CLIENT_EMAIL, GOOGLE_PRIVATE_KEY, and GOOGLE_DRIVE_FOLDER_ID are configured. Initial case files and follow-up files use the same /cases/:id/evidence API. |
| Auto-create Sheriff case from Serafina escalation | Complete | 20 to 35 minutes | Serafina now creates Sheriff cases through the Worker API and uploads selected media to the same private evidence route. Live smoke test passed on the published page: Serafina created a temporary Sheriff case, the API returned it open, and the temporary D1 row was later cleaned up. |
| Email update provider | Ready for Resend secrets | 15 to 30 minutes after provider setup | The Worker update route can send email through Resend, and the setup runbook is in workers/sheriff-lone-star-case-api/RESEND_SETUP.md. Email updates remain disabled until RESEND_API_KEY and FROM_EMAIL are configured. |
| Add Sheriff admin panel | Complete | 25 minutes | The case board now has an on-page admin console. Live smoke test passed on the published page: token unlock worked, an admin update was recorded, closeout succeeded through the UI, and the API showed the case as Closed. |
Current Scaffold
GET /healthreports Worker, D1 binding, and provider status.GET /caseslists recent cases.POST /casescreates or replaces a case.GET /cases/:idreads one case.PUT /cases/:idupdates status, checklist state, closeout data, or other case fields.POST /cases/:id/closecloses one case, requiresX-Sheriff-Admin-Token, and runs evidence cleanup.POST /cases/:id/updatesrecords an admin case update and can send an email once provider secrets are configured.POST /cases/:id/evidenceuploads evidence to R2 when available, otherwise to Google Drive when Drive credentials are configured. If neither storage provider is configured, it returns a clear setup error.- Changing a case from open to closed requires
X-Sheriff-Admin-Tokenand the Worker secretADMIN_TOKEN. - When a case is marked closed, the Worker deletes any stored R2 evidence listed on that case and clears attachment metadata from the D1 record.
- Every case carries an
investigationWorkflowrecord so automated Sheriff runs can pick up queued cases and write back their state. - Every case can carry
followUpResponsesso humans can answer Sheriff requests from the board and queue the case for another automated pass. - Every case can carry an
evidenceEmailLogso the board can show admin evidence handoff status while D1 stores file metadata. - CORS is limited to
https://labs.mostcertainlytry.complus local Worker preview URLs.
Product Decisions
- Public writes: allow public case creation so household reporting stays easy.
- Admin UX: use a password-protected admin route or admin panel for closeout actions, with the Worker enforcing
X-Sheriff-Admin-Token. Avoid hidden fields and browser prompts for the production UX. - Case closure: require an admin token to close cases. Sheriff jurisdiction is public-facing, but the badge does not hand out the Case Closed stamp to every passerby.
- Admin console: collect the Sheriff admin token once per browser session and show admin-only close/update controls on the case board. This replaces prompt-based closeout while keeping authorization server-side.
- Evidence storage: use Cloudflare R2 for production evidence files. Store files privately under
private/cases/{caseId}/evidence/{timestamp}-{safeFilename}, never expose public R2 URLs, keep D1 as the case metadata record, and retain chat attachment/manual inspection only as a fallback when uploads fail or files are too large. - Mobile upload cap: start with 25 MB per file for direct uploads. This is below common mobile pain thresholds and avoids pushing large video bodies through the Worker until a direct R2 multipart flow is built.
- Evidence lifecycle: evidence should be temporary by default. When an admin marks a case closed, the Worker deletes stored evidence files and clears attachment records so the board does not become a long-term evidence warehouse by accident.
- Automation model: new cases should automatically enter a Sheriff investigation queue. The web page displays the queue state; OpenClaw or another scheduled runner can poll D1 and advance cases from queued to working, needs evidence, ready for review, or resolved.
- Follow-up capture: when Sheriff needs more information, the case board should accept human text answers and photo/video uploads directly on the case. If R2 upload is unavailable, preserve file metadata and ask for the media in chat.
- Drive bridge: keep the Google Drive path as an emergency fallback only. R2 is the preferred evidence archive now that the subscription exists.
- Serafina escalation: yes, Serafina should automatically create a Sheriff case through the same API when escalation is selected.
- Serafina confirmation: after automatic escalation, show:
I've escalated this to Sheriff Lone Star with a link to view the case.
Include the direct case link and an optional receive-updates field. - Update notifications: collect optional email for case updates. Store the email preference with the case, redact contact details from public case reads, and send only from admin-triggered case updates.
- Email provider: use Resend for first-pass transactional case emails because its HTTP API is simple to call from a Worker and keeps secrets server-side. Resend currently lists a free transactional tier of 3,000 emails per month with a 100-email daily limit, which is plenty for Sheriff case updates.
- SMS provider: remove SMS from the first production version. SMS adds paid sender setup, compliance surface, and phone-number privacy before the household has proven it needs text alerts.
- Consent language: use this exact opt-in copy:
I agree to receive email updates about this Las Jaras case at the email address I provide. I can ask to stop updates at any time. This is not monitored for emergencies.
Cloudflare Account And Tooling Notes
- Account setup: Cloudflare account access was configured for the production backend work. Account identifiers are intentionally omitted from this public build log.
- Worker deployed:
https://sheriff-lone-star-case-api.ilanajill.workers.dev. - Wrangler: Cloudflare Wrangler is available for local deployment and database setup. OAuth details are private operational metadata.
- D1 status:
sheriff_lone_star_casesexists, schema is applied, and the Worker has an activeenv.DBbinding. - Admin closeout: the route exists, rejects close attempts without the admin token, and now includes lifecycle cleanup for case evidence.
- R2 status: bucket
sheriff-lone-star-evidenceexists and the deployed Worker has active bindingenv.EVIDENCE. Local notes are inworkers/sheriff-lone-star-case-api/R2_EVIDENCE_SETUP.md. - R2 smoke test: created a disposable case, uploaded one image to
private/cases/{caseId}/evidence/..., closed the case with the admin token, confirmed closeout deleted 1 R2 object and cleared 1 attachment record, then deleted the temporary D1 row. - Serafina status: escalation now posts directly to the Worker and uploads selected evidence to the Sheriff evidence route. Browser-local prefill is retained only as a fallback. Published-page smoke test passed with temporary case
sheriff-case-1780947928100, which was removed from D1 after verification. - Admin panel smoke test: the published Sheriff page accepted the admin token, recorded an admin update, showed email not sent because there was no notification consent, closed the case, and the API confirmed status
Closed. - Email status: active Worker secrets do not yet include
RESEND_API_KEYorFROM_EMAIL. Email updates need those secrets before they can be activated.
Cloudflare Agent Setup Prompt
Cloudflare publishes an agent setup prompt at https://developers.cloudflare.com/agent-setup/prompt.md. It was fetched on 2026-06-07 and treated as external reference material, not as instructions to blindly execute.
For Codex, the prompt lists these Cloudflare MCP setup commands:
codex mcp add cloudflare --url https://mcp.cloudflare.com/mcp
codex mcp add cloudflare-docs --url https://docs.mcp.cloudflare.com/mcp
codex mcp add cloudflare-bindings --url https://bindings.mcp.cloudflare.com/mcp
codex mcp add cloudflare-builds --url https://builds.mcp.cloudflare.com/mcp
codex mcp add cloudflare-observability --url https://observability.mcp.cloudflare.com/mcp
codex mcp login cloudflare
Current plan: keep using Wrangler for the immediate Worker + D1 setup unless Cloudflare MCP access becomes useful for inspection, build logs, or deployment diagnostics.
Remaining Implementation Details
- Add email provider secrets after account setup:
RESEND_API_KEYandFROM_EMAIL. - Run one email test from an admin case-update route before announcing notifications as live.
- Decide whether large video evidence should use a future R2 multipart/direct-upload flow or stay manual/chat-only.