Static web app for scrumretrospective.org — run lightweight 4 Ls scrum retrospectives in the browser with no accounts.
Facilitators create a session, invite the team via a join link, and guide the retro through four phases: team assembly, item collection, voting, and results. When the facilitator ends the session, results export as a PDF and the room is removed from the sync server.
4 Ls columns: Liked · Learned · Lacked · Longed For
4 Ws columns: Went Well · Did Not Go Well · Learned · Should Change
Start, Stop, Continue columns: Start · Stop · Continue
Keep, Drop, Try columns: Keep · Drop · Try
DAKI columns: Drop · Add · Keep · Improve
Mad, Sad, Glad columns: Mad · Sad · Glad
Facilitators choose a template when creating a retrospective.
| Phase | Name | What happens |
|---|---|---|
| 1 | Team assembly | Facilitator creates the retro and shares the join link. Participants join with their name. |
| 2 | Retrospective | Facilitator starts the board. Everyone adds items to any column. |
| 3 | Voting | Facilitator opens voting. Participants vote up/down on other people's items. Vote totals stay hidden. |
| 4 | Results | Facilitator closes voting. Items show up/down counts, sorted by net vote in each column. Facilitator can export PDF and end the session. |
Facilitator (left) and participant (right) actions from team assembly through PDF export.
- No accounts — access via session URL; participant identity stored in
sessionStorage - Live sync — sync API + 1s polling keeps participants, items, and votes in sync across browsers
- Presence — online indicators in the participant sidebar
- Private voting — only your own votes are visible during Phase 3; totals appear after close
- PDF export — retro name, start/end time, duration, facilitator and participant names, and all items with net votes (columns stacked vertically, sorted by net vote)
- Ephemeral storage — in-memory on the sync server; deleted when the facilitator ends the retro
npm install
npm run devThis starts two processes:
- Web app — http://localhost:5173
- Sync API — http://localhost:8787 (proxied at
/api)
Join links work across Chrome, Safari, and other browsers on the same machine or network because room data lives on the sync server, not in each browser's localStorage.
The app splits into two parts:
| Part | Host | Role |
|---|---|---|
| Static UI | GitHub Pages | React app at scrumretrospective.org |
| Sync API | Railway | In-memory room sync (server/index.mjs) |
- In the repo Settings → Pages, set Source to GitHub Actions.
- Add a repository variable: Settings → Secrets and variables → Actions → Variables
- Name:
VITE_SYNC_API_URL - Value: your Railway sync API base URL, e.g.
https://your-app.up.railway.app/api
- Name:
- Push to
main— the workflow builds with that URL baked in and deploysdist/.
npm run buildA public/CNAME file is included for the custom domain scrumretrospective.org. Point DNS at GitHub Pages per GitHub’s custom domain docs.
Option A — Railway dashboard (recommended first time)
- Go to railway.app → New Project → Deploy from GitHub repo.
- Select
sanjuthomas/scrumretrospective.github.io. - Open the service Settings:
- Root Directory:
server - Start Command:
node index.mjs(or leave default ifserver/railway.tomlis picked up)
- Root Directory:
- Settings → Networking → Generate Domain (e.g.
scrum-retro-production.up.railway.app). - Confirm health:
https://YOUR-RAILWAY-DOMAIN/api/health→{"ok":true}. - Optional env var ALLOWED_ORIGINS — defaults to
https://scrumretrospective.org,https://www.scrumretrospective.org.
Option B — GitHub Actions (auto-deploy on server/ changes)
- Railway → your project → Settings → Tokens → create a Project token.
- GitHub repo → Settings → Secrets and variables → Actions → Secrets →
RAILWAY_TOKEN= that token. - Link the Railway service to this repo (dashboard deploy once), then pushes to
server/**run.github/workflows/railway-sync.yml.
Wire the live site to Railway
- GitHub repo → Settings → Secrets and variables → Actions → Variables:
VITE_SYNC_API_URL=https://YOUR-RAILWAY-DOMAIN/api(must end with/api)
- Re-run Deploy to GitHub Pages (Actions tab → workflow → Run workflow) or push any commit to
main.
Until VITE_SYNC_API_URL is set and Pages redeploys, the UI calls /api on GitHub Pages and retros will not sync.
Note: Room data is in-memory. Redeploying or restarting Railway clears active sessions.
npm install
npm run devNo VITE_SYNC_API_URL needed locally — Vite proxies /api to http://localhost:8787.
Restart the sync API after changing server/ (a stale local process can still validate only 4 Ls columns).
For a production-like local build:
VITE_SYNC_API_URL=https://your-app.up.railway.app/api npm run build
npm run previewnpm test # unit tests (runs in GitHub Actions)
npm run test:watch # unit tests in watch mode
npm run test:integration # local end-to-end sync API flowIntegration tests start server/index.mjs on a random local port and are intentionally not run in CI.
| Layer | Role |
|---|---|
| Sync API | Source of truth — rooms, participants, cards, votes (in-memory) |
| Browser cache | In-memory cache + 1s poll via subscribeRetro |
| sessionStorage | Current participant ID per retro tab |
Votes are stored server-side during the session. Clients receive aggregated counts only after voting closes (Phase 4).
Full reference: docs/api.md — request/response shapes, phase rules, templates, errors, and flow.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/health |
Health check |
GET |
/api/retrospectives/:id |
Fetch room (optional ?participantId= for own votes during voting) |
PUT |
/api/retrospectives/:id |
Create/update room (phase transitions) |
DELETE |
/api/retrospectives/:id |
End session (remove room) |
POST |
/api/retrospectives/:id/presence |
Presence heartbeat |
POST |
/api/retrospectives/:id/presence/leave |
Mark participant offline |
POST |
/api/retrospectives/:id/cards |
Add item (Phase 2 only) |
POST |
/api/retrospectives/:id/votes |
Cast vote (Phase 3 only) |
| Path | Purpose |
|---|---|
/ |
Landing |
/initiate |
Facilitator setup form |
/retro/:id |
Active session |
/join/:id |
Participant join form |
/terms |
Terms of Use |
/license |
MIT License |
- Source code: MIT License
- Hosted service: Terms of Use — as-is, no warranty, no liability; do not submit sensitive or confidential data
- React 19, TypeScript, Vite, React Router
- Node.js sync API (
server/index.mjs) - jsPDF for PDF export
