Name it (e.g. "WhiteBoard Church") → click through → Create project
Click the Web (</>) icon → give it a nickname → Register app
Copy the values from the firebaseConfig object into the fields below
In Firebase Console: Authentication → Get Started → Email/Password → Enable → Save
In Firebase Console: Firestore Database → Create database → Start in test mode → Enable
✓
2
3
Set Firestore Security Rules
This protects your team's data so only members can see it.
Copy and paste these rules into Firebase Console:
How to paste rules:
In Firebase Console → Firestore Database → Rules
Select all existing text and replace with the rules below
Click Publish
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ── Helpers ──────────────────────────────────────────────────────
function isMember(wsId) {
return request.auth != null
&& exists(/databases/$(database)/documents/workspaces/$(wsId)/members/$(request.auth.uid));
}
function memberRole(wsId) {
return get(/databases/$(database)/documents/workspaces/$(wsId)/members/$(request.auth.uid)).data.role;
}
function isAdmin(wsId) { return isMember(wsId) && memberRole(wsId) == 'admin'; }
function isManager(wsId) { return isMember(wsId) && memberRole(wsId) == 'manager'; }
// ── userProfiles: direct workspace-ID lookup per user ────────────
match /userProfiles/{uid} {
allow read, write: if request.auth != null && request.auth.uid == uid;
}
// ── collectionGroup fallback for cross-device login ───────────────
match /{path=**}/members/{memberId} {
allow read: if request.auth != null
&& (request.auth.uid == memberId
|| resource.data.uid == request.auth.uid);
}
// ── Workspaces ────────────────────────────────────────────────────
match /workspaces/{wsId} {
allow read: if request.auth != null;
allow create: if request.auth != null;
allow update: if isAdmin(wsId);
match /members/{uid} {
allow read: if isMember(wsId);
// Self-create (join via code) or admin/manager can create
allow create: if request.auth != null
&& (request.auth.uid == uid || isAdmin(wsId) || isManager(wsId));
// Self: name/email only — role/workspaceId fields are blocked (prevents privilege escalation)
// Admin: full update rights
// Manager: name/email of plain members only
allow update: if request.auth != null
&& (
(request.auth.uid == uid
&& !request.resource.data.diff(resource.data).affectedKeys().hasAny(['role', 'workspaceId']))
|| isAdmin(wsId)
|| (isManager(wsId)
&& resource.data.role == 'member'
&& !request.resource.data.diff(resource.data).affectedKeys().hasAny(['role', 'workspaceId']))
);
// Admin can remove anyone; manager can remove plain members only
allow delete: if isAdmin(wsId)
|| (isManager(wsId) && resource.data.role == 'member');
}
match /items/{itemId} {
allow read: if isMember(wsId);
allow create, update: if isMember(wsId);
// Only admins can hard-delete items; any member can "erase" via status update
allow delete: if isAdmin(wsId);
}
}
}
}
✓
✓
3
Email Notifications Optional
Get real emails when someone gives you the Marker or assigns you to an item. Uses EmailJS (free, 200 emails/month).
EmailJS setup (~5 minutes):
Sign up at emailjs.com → free plan (200 emails/month)
Email Services → Add New Service → Gmail → connect your Gmail account
Create Template 1 — Welcome & Join Code (sent when someone joins or creates a workspace):
Variables to include: {{to_name}}, {{workspace_name}}, {{join_code}}, {{app_url}}
Create Template 2 — Assignment Notification (sent when someone is given the Marker):
Variables to include: {{to_name}}, {{from_name}}, {{item_title}}, {{prompt_text}}
Account → API Keys → copy your Public Key
WhiteBoard.church
Strategy tools for ministry teams, y'all
🏛️
Create a Workspace
You'll be the admin. You'll get a join code to pass along to your crew.
Workspace Created!
Round up your crew with this join code:
———
Team members enter this code when they create their account. Keep it somewhere safe — you can find it again in Team settings.