# BrewPage Page data: build, try locally, publish BrewPage Page data (API word `state`): a published page saves what visitors enter. No backend, no key in page code; not KV/JSON (your own data). 1) Build. Before your code (absolute URL, classic script, no async/import): bp.state() -> s|null (guard it); c=s.col("name"); c.set(key,value) c.get(key) c.remove(key) c.add(value) c.list({order,limit}) c.stats({countBy}) async function start() { // no top-level await/return const s = await bp.state(); // null -> reason in bp.state.reason, console if (!s) return showNote("Saving is not available on this page right now."); // always guard const book = s.col("guestbook"); await book.add({text: input.value}); // random key const docs = await book.list({order: "-createdAt", limit: 20}); // [{key, id, value, mine, createdAt}], .next|null -> list({cursor}) for (const d of docs) ul.append(bp.text(d.value.text)); const v = s.col("votes"); await v.set("me", {choice: "a"}); // key A-Za-z0-9_.-, no leading _, <=128; "me" = one per visitor await v.get("me"); // value or null; remove(key) -> true if it existed await v.stats({countBy: "choice"}); // {count, by: {a, b, other}} } start(); bp.storage.ready.then(() => bp.storage.setItem("k", "v")); // localStorage-like, synced; works where s is null 2) state (in the publish body, not the page): {} = all lists private (visitor sees own entries). Name a list to change its rule: {"collections":{"guestbook":{"mode":"shared","maxDocsPerVisitor":5}}} = all read, each writes own "scores":{"mode":"shared","index":"score","maxDocsPerVisitor":1} -> list({order: "-index"}) "votes":{"choices":{"choice":["a","b"]}} -> stats, other values count as other "menu":{"mode":"shared","write":"author"} = visitors read, author writes (Owner API, MCP) List names a-z0-9_- (letter first, <=32 chars), <=32 lists. A private list never turns shared: new name. 3) Owner view: s.role is "owner" where the page shows its owner gear, else "visitor"; s.onRole(fn(role)) runs now and on change. Owner: c.list({all: true}) = all entries + d.visitor label (paged like list); c.removeById(entry.id). No secrets in page. 4) Test locally: open from disk or http(s)://localhost, 127.0.0.1, [::1], *.localhost: bp.state() = local preview, s.local === true, console "[BrewPage] Local preview". Kept in this browser, nothing sent; clear: localStorage.removeItem("__bp_state_local"). One visitor; checks and limits as published, e.status 0. Not applied: state rules, rate limits, human check, cookies. ?bp-local=owner = owner view, ?bp-local=off = off. Same file works unchanged once published with state. 5) Publish: POST https://brewpage.app/api/html, Content-Type: application/json, body {"content":"...","state":{...}}; sites: multipart field "state". Raw HTML body cannot carry state (or PATCH /api/{ns}/{id}/state later). Own address https://