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 3e1cb86700a5e96238d7a2f1
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
# When
When is a free group scheduling poll, and a free alternative to Doodle, When2meet and Rallly. It is an OpenApp in the OpenApps free apps store (https://free-apps.openapps.xyz/), owned by the OpenApps team.
## What it promises
- Free, with no limit on polls or participants, no ads and no paid tier. Never add a cap, an upsell or tracking. A poll holds up to 500 time options; that is a size limit, not an upgrade lever.
- No sign-up. Every visitor is a guest account created in the browser.
- Anyone with a poll's link can see it, answer and comment. The random poll id is the link's secret.
- Only owners can edit, close, pick the final time, remove answers and comments, and download the answers. The creator is an owner; anyone who opens the admin link (`/p/<id>/admin#k=<key>`) becomes one. The key travels in the URL fragment, so servers never see it.
- Times show in each viewer's own time zone, with the zone named.
- AI assistants can do the main job through the public API (`functions/api.ts`), documented in `public/llms.txt` and `public/openapi.json`. Keep both true whenever the API changes, and `public/index.md` true whenever the landing page changes.
## How it is built
- `index.html` is the static landing page that search engines and AI crawlers read. Keep its copy, comparison, FAQ and JSON-LD in sync with what the app does.
- `src/` is the React app. `/` mounts the poll maker (`Editor.tsx`) and "Your polls" (`Home.tsx`, from localStorage) into the landing. `/p/<id>` hides the landing and shows the poll (`Poll.tsx`): a grid on wide screens, one card per time on phones, then comments. `/p/<id>/edit` edits it; `/p/<id>/admin#k=<key>` claims ownership and drops the key from the address bar.
- The poll page and the Bounded SDK load on demand (`src/lib/bounded.ts` imports the SDK lazily), so the landing page stays light. The browser remembers which polls its guest already joined (`src/lib/polls.ts`), so a return visit skips those checks; joins and owners are never deleted, so that memory cannot go stale.
- `src/ui.tsx` holds the kit pieces: dialog (a sheet on phones), dropdown menu, toast, copy, share, download, and the poll skeleton.
- `functions/lib/when.ts` holds the logic shared by the app and the API: options, answers, counts, the best option, time zone math, labels, the .ics file, and the Google Calendar and Outlook.com links.
- A timed option is `{id, start, end?}` in UTC seconds. An all-day option is `{id, start, allDay: true}` where start is midnight UTC of its date, always read back in UTC so the date is the same for everyone. The poll stores the organizer's time zone for creating and editing times.
- `functions/lib/config.ts` holds the app id, the site URL used in links, and the API base.
- `policy.json` holds the data rules (Pattern B in the store's app guide). `policy-tests/` checks them: run `bounded tests run` after any policy change. Reads need a join record (`polls/<id>/joins/<userId>`), and creating it needs only the poll id. Owners are `polls/<id>/owners/<userId>`; the key lives in `pollkeys/<id>`, readable only by the public API function, which checks a presented key before any owner action. Votes (`polls/<id>/votes/<voteId>`) can be changed only by the account that wrote them, and only while the poll is open; owners can remove one. Comments (`polls/<id>/comments/<commentId>`) can be added by anyone who joined, never edited, and removed by their author or an owner.
- Every API caller shares one public principal, so the API makes a new participant row per call unless the caller passes back a `voteId` it got from the API.
## Working on it
```bash
npm install
npm run build # typecheck + build to dist/
bounded tests run # policy-tests/ against the local policy.json
```
When is an OpenApp, so nobody deploys it directly: changes ship through previews and releases as your runtime instructions describe.
Check a change by making a poll with timed and all-day options, answering it from a second browser in another time zone, editing your own answers, posting a comment, picking the final time, opening the calendar links, downloading the CSV, and calling the API with curl (including a wrong owner key). Check phones at 390px wide.
## Voice
Plain words, short sentences, no jargon, no em dashes. Say what happens, not how clever it is.
Change history
Quality pass: comments, CSV export, share the final time, calendar menu, fixes
Bounded source sync
Bounded source sync
Bounded source sync
Bounded source sync
Bounded source sync
Bounded source sync
Bounded source sync