docs(mojo): AGENTS.md with the rule and tenancy declaration

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by claude agent, operated by paul@keymail.dev 9226c245a93d68576d683caae15df567f17c7b78 parent 2eeda2a
1 file changed, +28 −0
  • apps/mojo/AGENTS.md +28 −0
diff --git a/apps/mojo/AGENTS.md b/apps/mojo/AGENTS.md
new file mode 100644
index 0000000..47d9a9a
--- /dev/null
+++ b/apps/mojo/AGENTS.md
@@ -0,0 +1,28 @@
+# 🤖 AGENTS.md: todo-mojo
+
+The one rule: **the server owns the truth, and the page must work without
+JavaScript.** Every change is a plain form POST answered with a 303; the
+JavaScript only replays those same POSTs in the background and swaps in
+the server's own rendering. Nothing lives only in the browser.
+
+## Tenancy and trust
+
+- **One instance for the whole app.** It runs as `todo-mojo` on account
+ `bfj`, host `todo-mojo.bfj.oncarlos.com`, channel `edge`.
+- **Each visitor's list is kept separately by an anonymous cookie.** The
+ cookie holds 256 random bits from `getrandom(2)`, and is `HttpOnly;
+ SameSite=Lax; Path=/`, plus `Secure` over https. A value the app
+ doesn't recognise gets a fresh, empty list. Every query is scoped to
+ the caller's list, so an id from someone else's list answers 404.
+- **No sign-in.** There are no accounts; losing the cookie loses the list.
+- **Trust class C, chosen on 2026-10-23.** Todo text is stored as plain
+ text in SQLite, readable by the server and its operator. That's
+ acceptable for a demo todo list and is written down here and in the
+ README so nobody mistakes it for private storage.
+
+## Working here
+
+- Build, run, test and deploy with the `Makefile` targets (see README).
+- SQLite stays in WAL mode, one file at `--db`. Migrations only add.
+- Bind `127.0.0.1` only when running with `--listen`.
+- Commits end with `Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>`.