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 334801de44cb0fe911d5a88b
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
# Polls Polls is free live polls and audience Q&A for meetings, classes and talks, and a free alternative to Slido and Mentimeter. It is an OpenApp in the OpenApps free apps store (https://openapps.free/), owned by the OpenApps team. ## What it promises - Free, with no cap on participants, polls, votes or questions, no ads and no paid tier. Never add a cap, an upsell or tracking. Size limits (300-character questions, 30 options) are not upgrade levers. - No sign-up for the host or the audience. Every visitor is a guest account created in the browser. - The audience joins with a 6-digit code at the site, the audience link (`/e/<id>`) or the QR code, which opens that link. - One vote per guest per poll, enforced by the data rules. The host decides whether people can change an answer while a poll is open. Be honest that a second device or a cleared browser votes again: this is not for elections. - Only an event's owners (the browser that made it and anyone who opens its admin link) can change it, see drafts, read individual votes, or see pending and hidden questions. - AI assistants can create an event with its polls in one call and read results through the public API (`functions/api.ts`), documented in `public/llms.txt` and `public/openapi.json`. Keep both true whenever the API changes. `public/index.md` is the landing page as plain markdown; keep it in step with `index.html`. ## How it is built - `index.html` is the static landing page that search engines and AI crawlers read. Keep its copy, comparison table, FAQ and JSON-LD in sync with what the app does. Competitor figures are labeled "as reported in 2026" and come from the store's research notes; update them when they change. - `src/` is the React app. `/` mounts the join box, the create box and "Your events" (from localStorage) into the landing. `/<6 digits>` looks a code up. `/e/<id>` is the audience view (`Audience.tsx`), `/e/<id>/admin` the host's page (`Admin.tsx`) and `/e/<id>/present` present mode (`Present.tsx`). Opening either host page with `#k=<key>` makes that browser an owner and drops the key from the address bar. - `functions/lib/polls.ts` is shared by the app and the API: poll types, poll and answer checks, results summaries, CSV, and the one batch that creates an event. Change validation there so both sides stay identical. - `functions/lib/config.ts` holds the app id and the site URL used in links. - `policy.json` holds the data rules (Pattern B in the store's app guide): - `events/<id>` is readable by anyone who joined it (`events/<id>/joins/<userId>`, which needs only the id) and writable by owners. `events/<id>/owners/<userId>` comes from the creating batch or from presenting the admin key (`eventclaims`); the key lives in `eventkeys/<id>`, readable only by the API's public principal. - `codes/<code>` maps a join code to its event. It is created in the same batch as the event and never reused or changed. Reading a code needs a recorded try (`codetries/<userId>/t/<code>`), capped at 10 different codes per device in 10 minutes, so nobody can list codes or scan the million of them quickly. The audience link and the QR code skip codes entirely, so large events do not touch the cap. - `events/<id>/polls/<pollId>`: drafts are readable only by owners. Votes are `events/<id>/polls/<pollId>/votes/<userId>`: one document per person per poll, created only while the poll is open and changed only if the event allows it. Only the voter and owners read votes; owners can delete them (Clear answers). - `events/<id>/questions/<questionId>`: the audience sees live and answered questions plus their own. Only the public API writes questions (`POST /events/<id>/questions`, which the web app calls too), because every stored document carries its writer's id in `_createdBy`, readable by anyone who can read the document. Who asked is kept in `events/<id>/asked/<userId>/q/<questionId>` (written by the asker just before the API call, readable only by the asker), so a question without a name stays anonymous to the host and the audience. Do not let people write questions from their own accounts or add the asker's id to questions: owners read every vote by user id, so it would tie names to answers. The upvote count rises by exactly one in the same batch as the voter's `events/<id>/upvotes/<userId>/q/<questionId>` record, which can exist once. - Results for the audience: an owner's device (the admin page or present mode) reads every vote, and while results are shown it writes a summary into the poll's `results` field, at most every 1.5 seconds. Audience phones read that one field instead of every vote. If no owner device is open, the audience sees the last summary published. The rules keep `results` empty while results are hidden, so a late publish from a second owner device cannot leave them readable. - Live reads return at most 1,000 rows, so `useAll` in `src/lib/event.ts` pages through when a poll has more votes; the API pages the same way. - The word cloud is laid out on the device (`src/Results.tsx`). The QR encoder is `src/lib/qr.ts`, copied from Links. - The policy mentions `@const.BOUNDED_PUBLIC_PRINCIPAL_API`, which exists only once the app exists. ## Working on it ```bash npm install npm run build # typecheck + build to dist/ ``` Polls is an OpenApp, so nobody deploys it directly: changes ship through previews and releases as your runtime instructions describe. `policy-tests/` covers every rule that guards votes, questions, codes and ownership. Run them with `bounded tests run` after any policy change. Check a change by making an event with every poll type, joining from a phone with the code and from a second browser with the QR link, voting, changing an answer, asking and upvoting questions with moderation on and off, presenting with the keyboard, exporting both CSVs, and calling every API route with curl, including a wrong owner key. ## Voice Plain words, short sentences, no jargon, no em dashes. Say what happens, not how clever it is.
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
Bounded source sync
Bounded source sync