The shape of it — architecture without code
Before any of it gets built, it is worth seeing the whole thing on one page. Five moving parts, each chosen for a reason you can argue with. If you disagree with a choice here, you will build something better later — but you will know what you are trading away.
The five parts
browser / phone an LLM assistant
| |
| HTML + fetch | MCP tools / OAuth
v v
+-----------------------------------------+
| Cloudflare Pages (static files) | index.html, dashboard, story page
| Cloudflare Functions (/functions) | the API + middleware + MCP server
+-----------------------------------------+
|
v
+------------+
| D1 | one table per data domain
+------------+
That is the whole system. No framework, no bundler, no build step, no server you have to keep alive.
1 · Zero-build static site
The dashboard is an index.html. The story page is a product.html. You edit a file, push, and Cloudflare serves it from the edge seconds later.
Why: every build step you add is a thing that can break between you and seeing your change. On a project whose purpose is learning, that feedback loop is the most valuable asset you have. A bundler buys you very little here and costs you the loop.
What it costs: no components, no JSX, hand-written HTML. Above a certain size that hurts. This project is below that size — and noticing when you cross the line is itself a lesson.
2 · Functions as the backend
Anything that is not a file is a Pages Function: a file under functions/api/… becomes a route automatically. functions/api/health/daily.js answers /api/health/daily.
One special file, functions/_middleware.js, runs before every request. That is where authentication lives — in one place, not sprinkled across twenty handlers.
3 · D1 as the only datastore
One SQLite database at the edge. One table per data domain — measurements, meals, workouts, sleep sessions, notes — rather than one giant events table with a type column.
Why separate tables: a weight and a sleep session have almost nothing in common. Forcing them into one shape means every query starts with a filter and every column is nullable. Separate tables let the schema say true things.
4 · An MCP server, so the agent has tools
Without this layer, "log my weight with an assistant" means: you tell the assistant, it writes some JSON, you copy the JSON, you paste it into a terminal. That is not automation, that is you being a courier.
MCP — Model Context Protocol — lets you expose named tools: log_weight, log_meal, get_wellbeing. The assistant calls them directly. You say "78.4 this morning" and the row exists.
The same surface is documented as ordinary REST in an openapi.yaml, for anything that does not speak MCP. One backend, two descriptions of it.
5 · Two authentication paths, one set of tools
| Caller | How it authenticates | Why |
|---|---|---|
| A chat assistant you connect as a client | OAuth 2.1, scoped | It is a third party. It should get a revocable, limited grant — not your master key. |
| Your own agent / scripts | Static bearer token | It is you, on your machine. A full OAuth dance buys nothing and costs a lot of moving parts. |
Both arrive at the same check in middleware and the same tools behind it. Two doors, one room.
The part most people skip: a public tier that is not a lie
A dashboard nobody can see is not much of a dashboard, and a health project has genuinely different sensitivities inside it. The pattern here is an allowlist of GET endpoints that are public — and those endpoints return real but redacted data.
- Public: trends, sleep duration, activity, a composite score.
- Behind a token: blood pressure, medications, clinical notes, documents.
Note what this is not: it is not "hide the page". The page is public and the numbers on it are true — there are simply numbers it never asks for. The redaction happens on the server, not in the browser.
What this costs you
Honestly: edge functions have short execution limits, D1 is SQLite with SQLite's concurrency story, and a static site means you write HTML by hand. If you wanted a hundred thousand users, you would choose differently. You want one user, on five devices, for years — and for that, this shape is close to ideal and costs about nothing to run.