The retro board that doesn't keep your retros.
No accounts, no history, no archive. Create a board, share the six-character code, and run the retro together: Write → Vote → Discuss. Cards, dot voting, drag-to-group, a shared timer, action items, Markdown export. Export what you want to keep, then delete the board yourself or let it go: every board is erased from the archives, every morning (6:00 AM Eastern by default).
Runs entirely on Cloudflare: one Worker serves the app, and each board is a SQLite-backed Durable Object that holds the data, pushes live updates over WebSockets, and wipes itself on an alarm.
- Boards and codes. Anyone can create a board. The six-character code (or the link, or the QR code in the share dialog) is the only invitation. Six templates: Went well / To improve, Start / Stop / Continue, Mad / Sad / Glad, 4Ls, Dagobah, Blank. Each column has a one-line prompt under its title. Columns and prompts are editable afterwards.
- Phases. A stepper moves everyone at once. In Write, other people's cards are blurred so nobody anchors on the first idea. In Vote, each person spends a budget of dot votes (default 5, several per card allowed). In Discuss, columns sort by votes.
- Cards. Enter posts, Shift+Enter breaks a line. Optional per-card
anonymity: an anonymous card leaves the server with no author id at all,
so nobody on the board, the owner included, can tell who wrote it. Edit
and delete your own from the
⋯menu on the card (or double-click to edit); the owner can delete any. - Reactions and comments. A fixed set of emoji reactions (👍 ❤️ 😂 🎉 🤔) that cost no votes, and short comment threads under each card for discussion context. Both are hidden on cards still blurred in Write and are included in the export.
- Grouping. Drag a card onto another to stack them; groups carry a combined vote count. Drag to an empty spot to move between columns.
- Timer. 1–15 minute presets, counted down on every screen. At zero a short chime plays and the tab title flags it (switchable in Preferences).
- Action items. Text, optional owner, done checkbox, and an optional
link to the card it came from (from the card's
⋯menu). Exported with the board. - Presence. Who is here, who stepped away, who owns the board. In Write, people mark themselves done ("3 of 5 done"); in Vote, each avatar shows how many votes that person has left, so the facilitator knows when to move on.
- Export. Download or copy in three formats: Markdown (columns, grouped
cards with votes and reactions, comments, action items), CSV of the action
items for a tracker import, and a short plain-text summary for chat. Same
output from the UI and the
/export.md,/export.csvand/export.txtendpoints. - Facilitation. By default only the owner can move the phase, set the timer, rename the board, add, rename or remove columns, and edit column prompts. The owner can open all of that up to everyone by turning off "Only I can facilitate".
- Analysis (owner only, in Discuss). A small language model (WebLLM; Qwen3 1.7B by default, 4B and 8B selectable) runs in the owner's browser on their GPU and offers themes with card citations, suggested groupings and draft action items. Accepting a suggestion sends an ordinary op; nothing else changes the board, and no card text leaves the browser. Opt-in: the model (1 to 4.6 GB depending on the pick) downloads from Hugging Face only after the owner agrees, and stays cached in that browser. Needs WebGPU.
- Ownership. The creator can change settings (vote budget, anonymity, blurring, the facilitation lock), delete any card or comment, and delete the board early. Ownership is a token in the creating browser; the server stores only its hash.
- Themes: System, Light, Dark (side), plus Dagobah, Tatooine and
Kamino, and motion and timer-sound switches in Preferences.
prefers-reduced-motionis a hard override. - In-app help at
/helpcovers everything above for participants.
- All boards are deleted daily at 6:00 AM America/New_York (the alarm follows daylight-saving changes). A board created at 11 PM lives until the next morning; one created at 5:50 AM lives ten minutes, and the UI warns on create. The board header shows exactly when it goes.
- The wipe is
storage.deleteAll()on the board's Durable Object: cards, votes, reactions, comments, names, action items, the owner hash, and the alarm. Nothing is archived. There are no backups to restore from, by design. - While a board is live, its data sits in that one Durable Object and is readable by anyone who has the code. Do not put secrets on a retro board.
- The server keeps no accounts and no analytics. Request logs follow your Cloudflare account's Workers observability settings; note that the participant id and secret, display name and (for the owner) the owner token travel in the WebSocket URL, so they can appear in those logs.
- Optional, off by default: a deployment can require a Cloudflare Turnstile check to create a board. Turnstile is a Cloudflare service; when it is on, the home page loads its script and the creator's browser is checked by Cloudflare. Joining a board never involves it. See docs/self-hosting.md.
- The browser stores: a random participant id and secret (the secret is what
stops anyone else connecting as you), your display name, owner tokens for
boards you created, theme, motion and timer-sound preferences, and a list of recent
boards (pruned as they expire). All under
localStoragekeys prefixedholocron:. Clearing site data removes them. - If the owner enables Analysis, that browser also caches the model weights
(1 to 4.6 GB depending on the model picked, in the browser's Cache Storage
under
webllm/*) and the pick itself (holocron:analysis-model) so the next retro does not download them again. The download is the only network request Analysis makes; no board data leaves the browser, and the suggestions are never stored. Clearing site data removes the weights.
Limits: 500 cards per board, 8 columns, 50 participants, 500 characters per card, 300 per comment (20 per card, 500 per board), 3,000 reactions per board, 20 ops per second per connection, 10 board creations per minute per IP.
See docs/self-hosting.md. Short version: a
Cloudflare account, bun install, npx wrangler login, npm run deploy.
Change the reset time with the RESET_TZ and RESET_HOUR vars in
wrangler.jsonc.
Also: docs/architecture.md for how it fits together, docs/security.md for the threat model and review, and docs/accessibility.md for the WCAG 2.2 AA conformance statement.
React 19 + TypeScript + Vite 7, Tailwind CSS v4, stock shadcn (base-lyra on
Base UI), Oxanium + JetBrains Mono, Phosphor icons, zod, Biome. Cloudflare
Workers + Durable Objects (SQLite storage, WebSocket hibernation) through
@cloudflare/vite-plugin. Tests with Vitest inside workerd via
@cloudflare/vitest-plugin. No animation library for the app's own motion:
View Transitions, the Web Animations API, @starting-style, and CSS
linear() springs. (The stock shadcn overlays ship with tw-animate-css
for their open and close transitions; nothing else uses it.)
bun install # npm's resolver currently trips on Vitest 4.1 peers
npm run dev # http://localhost:5173 with the Worker and DO running locally
npm test # type-gen + vitest (reducer, reset-time math, DO lifecycle, alarm wipe)
npm run e2e # playwright + axe: a live retro in two browsers, privacy, keyboard, a11y
npm run check # biome
npm run build # types + tsc + vite build (client + worker)
npm run deploy # build + wrangler deployCopy .dev.vars.example to .dev.vars and set DEV_BOARD_TTL_SECONDS=60 to
watch a board expire during development.
MIT.

