README.md
July 10, 2026 · View on GitHub
RetroMatic is used for real-time retrospectives. See how to leverage Angular and Firebase for syncing data across all the participants. Retrospective's are held at the end of sprints in Agile software development. The team reflects on what happened in the sprint and determines actions for improvement.
Local Development
To clone and run this application locally, you'll need Git and Node.js 20+ (which comes with npm) installed on your computer.
Create a free Firebase project. In the Firebase console, enable Email/Password, Google, and Anonymous sign-in under the Sign-in method tab of the Auth section, and create a Realtime Database.
Create a .env file at the repo root with your project's config (find these under Firebase Console > Project settings > General > Your apps):
FIREBASE_API_KEY=
FIREBASE_AUTH_DOMAIN=
FIREBASE_DATABASE_URL=
FIREBASE_PROJECT_ID=
FIREBASE_STORAGE_BUCKET=
FIREBASE_MESSAGING_SENDER_ID=
npm install --legacy-peer-deps
npm run config -- --environment=prod
npm start
Note:
npm run configwith no flags currently does nothing useful for local dev (it's a known bug — see issue #77). Passing--environment=prodis what actually generates thesrc/environments/environment.tsfile thatnpm startreads; it does not put the app in production mode. This will be simplified once #77/#78 are fixed.
Firebase Structure
$ are Firebase-generated unique IDs.
├── retroboards
│ └── $retroboardId
│ ├── creator (username)
│ ├── creatorId ($userId)
│ ├── noteCount
│ ├── dateCreated
│ ├── name
│ └── timeZone
├── buckets
│ └── $bucketId
│ ├── retroboardId ($retroboardId)
│ ├── creator (username)
│ ├── creatorId ($userId)
│ └── name
├── notes
│ └── $noteId
│ ├── creator (username)
│ ├── creatorId ($userId)
│ ├── retroboardId ($retroboardId)
│ ├── bucketId ($bucketId)
│ ├── message
│ ├── voteCount
│ └── votes
│ └── $userId
├── users
│ └── $userId
│ ├── favorites
│ │ └── $retroboardId (true, or absent if unfavorited)
│ ├── md5hash
│ └── displayName
└── bucketTemplates
└── $templateId
├── name
├── bucketNames (array of strings)
└── creatorId ($userId)
Users can save a reusable set of bucket names as a template from the create-retro dialog; templates are listed there for quick reuse on future boards.
Firebase Security Rules
These rules are currently documentation only — they aren't version-controlled or auto-deployed yet (tracked in issue #86). Treat the Firebase console as the source of truth for what's actually enforced in production, and update this section if you change it there.
{
"rules": {
"retroboards": {
".read": "auth != null",
".indexOn": ["creatorId"],
"$retroboardId": {
".write": "(auth != null && !data.exists()) || data.child('creatorId').val() === auth.uid",
".validate": "newData.hasChildren(['creator', 'creatorId', 'noteCount', 'dateCreated', 'name', 'timeZone'])",
"creator": {
".validate": "newData.isString()"
},
"creatorId": {
".validate": "auth.uid === newData.val() && root.child('users/' + newData.val()).exists()"
},
"noteCount": {
".validate": "newData.isNumber()"
},
"dateCreated": {
".validate": "newData.isString()"
},
"name": {
".validate": "newData.isString()"
},
"timeZone": {
".validate": "newData.isString()"
}
}
},
"notes": {
".read": "auth != null",
".indexOn": ["bucketId", "retroboardId"],
"$noteId": {
".write": "(auth != null && !data.exists()) || (data.child('creatorId').val() === auth.uid || root.child('retroboards/' + data.child('retroboardId').val()).child('creatorId').val() === auth.uid)",
".validate": "newData.hasChildren(['creator', 'creatorId', 'retroboardId', 'bucketId', 'message', 'voteCount'])",
"creatorId": {
".validate": "auth.uid === newData.val()"
},
"votes": {
".write": "auth != null"
},
"voteCount": {
".write": "auth != null"
}
}
},
"buckets": {
".read": "auth != null",
".indexOn": ["retroboardId"],
"$bucketId": {
".write": "(auth != null && !data.exists()) || data.child('creatorId').val() === auth.uid",
".validate": "newData.hasChildren(['creator', 'creatorId', 'retroboardId', 'name'])",
"creatorId": {
".validate": "auth.uid === newData.val()"
}
}
},
"users": {
"$userId": {
".read": "$userId === auth.uid",
".write": "$userId === auth.uid",
".validate": "newData.hasChildren(['displayName', 'md5hash'])",
"displayName": {
".validate": "newData.isString()"
},
"md5hash": {
".validate": "newData.isString()"
}
}
},
"$other": { ".validate": false }
}
}
Firebase Authentication
To set up users, from your Firebase dashboard:
- Click Authentication
- Click Sign-in method
- Enable Email/Password, Google, and Anonymous
License
MIT.