Source
This app’s code and revision history are public. Download a copy or explore the files below.
Public
The app’s code and revision history are public.
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone ee59501e91bae6d2c23a8370
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
AGENTS.md
# Lists Lists is a free shared grocery and to-do list app that updates live for everyone in the house, and a free alternative to AnyList, OurGroceries and Bring!. It is an OpenApp in the OpenApps free apps store (https://openapps.free/), owned by the OpenApps team. ## What it promises - Free, with no limit on lists, items or people, no ads and no paid tier. Never add a cap, an upsell or tracking. - No sign-up. Every visitor is a guest account created in the browser. A list lives in its link. - Anyone with a list's share link is on the list and sees every change live. The person who made it (and anyone with its admin link) can remove people, change the link and delete the list. - Checking off and adding work offline; changes sync when back online. When two changes touch the same item, the last one to reach the server wins. The landing page and FAQ say this plainly; keep them true. - AI and voice assistants can make lists, add items, check them off and read lists through the public API (`functions/api.ts`), documented in `public/llms.txt` and `public/openapi.json`. Keep both true whenever the API changes. ## How it is built - `index.html` is the static landing page that search engines and AI assistants read. Its first paragraph answers "free alternative to AnyList" in quotable sentences; the FAQ is phrased the way people ask assistants and matches the FAQPage JSON-LD word for word. Competitor figures come from research and are hedged "as reported in 2026" with an "Updated" month. `public/index.md` is the same page in plain markdown; keep the three in step. - `src/main.tsx` keeps `/` as the landing (the start card mounts into `#start`, and the landing never loads the Bounded SDK) and lazy-loads the app for `/lists` (your lists), `/l/<id>` (a list; `/l/<id>#<secret>` is the share link and joins) and `/l/<id>/admin#<owner key>` (the admin link). - `functions/lib/lists.ts` is shared by the app and the API: the store sections, the built-in grocery dictionary and `guessSection` (the head noun wins: "chocolate chip cookies" is a snack), word stems so "eggs" and "egg" match, `itemKey` (an item's id comes from its name, so the same item added on two offline phones is one document), parsing "2 lb chicken" and splitting "milk and eggs", and `groupItems` for display order. - `src/lib/sync.ts` is the outbox: every item change is saved in localStorage first and sent in order with `setMany`. A change the server refuses (an item someone cleared, a person who was removed) is dropped and shown; anything else waits and retries with backoff, again on `online` and when the page comes back. - `src/lib/data.ts` reads a list: live queries when online, a saved copy per list on the device (`lists:snap:<id>`) when not, with unsent changes laid on top. Making a list also goes through the outbox, so it works offline for a browser that has used Lists before. Joining, the admin link, removing people, changing the link and deleting need a connection. - `policy.json`: - Everything under `lists/<id>` is readable only by members (`lists/<id>/members/<userId>`). - Joining needs the list key: the joiner writes `lists/<id>/claims/<me>` with the secret in the same batch, and that write only succeeds when it matches `listkeys/<id>`. The admin link's owner key (`ownerkeys/<id>`, unreadable) works the same way through `ownerclaims`, and also lets the owner join on a new device. - Owners (`lists/<id>/owners/<userId>`, which also need membership) can remove members, replace the list key (the old share link and assistant key stop working; nobody is removed by it) and set `deleted` on the list. After that nobody can read or change its items; the owner's device then erases the items and history. The owner key never changes, so removing someone who has the admin link does not keep them out; the remove step in Share says so. - Items (`lists/<id>/items/<itemKey>`) and history (`lists/<id>/known/<itemKey>`, for suggestions and remembered sections) can be changed by any member. `addedAt` and `checkedAt` come from the device clock and only order the list, so no rule compares them with `@time.now`. - `@StringUtils.length` counts UTF-8 bytes, so every length cap in the policy is three times the character limit in `LIMITS` (`functions/lib/lists.ts`). Keep that ratio: a cap equal to the character limit refuses names in accented or non-Latin letters that the app accepted. - The public API joins lists as the route's public principal, named "Assistant", and writes a claim with the presented key on every request before it reads or writes anything. Every API caller shares that principal, so that check must never be skipped. The policy names no platform constant, so it deploys in one step. - `policy-tests/` covers each rule from both sides: `bounded tests run --app-id <app id>`. - `src/sw.js` becomes `/sw.js` at build (`vite.config.ts` fills in the file list) and keeps the app on the device, so it opens with no connection. `public/manifest.webmanifest` and `public/icons/` (crops of the card art) make it installable. - Live queries return at most 1,000 rows: a list with more than 1,000 items, or more than 1,000 remembered items, shows the first 1,000. ## Working on it `npm install && npm run build` typechecks and builds `dist/`. `npm run dev` serves it on port 4347. Ship changes through previews and releases as your runtime instructions describe; the app is an OpenApp, so nobody deploys it directly. Check a change by making a list, adding "milk, eggs and 2 lb chicken", correcting an aisle and re-adding the item, checking off and clearing (with undo), arranging aisles, opening the share link in a second browser and watching changes arrive live both ways, going offline in one browser, checking off and adding, coming back online, removing the second browser with "change the link", opening the admin link elsewhere, and calling the API with curl (no key, wrong key, add by text, check off by name, clear checked). ## Voice Plain words, short sentences, no jargon, no em dashes. Say what happens, and say plainly what Lists does not do (recipes, meal plans, prices, barcode scanning, watch apps).
Change history
Point OpenApps links to openapps.free
Bind agent address in policy constants
Use OpenApps service URLs and add contribution strip
Bounded source sync
Bounded source sync
Bounded source sync