Admin Handbook · v2

How to run Faraan Alumni Network

Every admin task, end-to-end. Search the index above, or scroll through.

No matches. Try a different word — or clear the search to see everything.

1 Getting started

The first 5 minutes — open the panel, recognise the layout, know what you can do.

Where is the admin panel?

Two ways to open it

  1. From any device, visit /admin/ on the live URL. You'll see a phone-OTP login screen unless you're already signed in.
  2. If you're already signed in on the main app, tap the dark Admin pill in the home tab's top-right (visible only when your account has the admin or superAdmin claim).

The layout, top to bottom

Left sidebar
Section nav — Dashboard, Alumni, Verification, Batches, Groups, Initiatives, Memories, Causes, Shoutouts, Reports, Blocked users, Volunteers, Broadcast, Site content, Audit log, Super admins (super-admin only).
Top bar
Page title on the left; View site → link on the right to jump back to the public app.
Bottom-left
Your phone + role + Sign out.
Mobile tip On phones the sidebar hides behind a hamburger button in the top-left. Tap to open, tap outside to close.

2 Roles & powers

Who can do what. Roles are stored as Firebase custom claims — server-enforced, not just UI.

RoleWhat they can doHow it's granted
Super admin Everything below + add/remove other admins, manage config/super_admins, hard-delete users. Phone must be in config/super_admins.phones. claimAdminIfEligible elevates them on next sign-in.
Admin Verify alumni, moderate all content, manage batches/groups/initiatives/causes, broadcast, block users, edit any profile, set Conveners. Cannot add other admins. Set by a super admin via the Role button on the alumni row.
Staff Moderate memories/shoutouts/reports, run verification queue. Cannot delete users, manage admins, broadcast. Set by an admin via the Role button.
Verified alumni Post memories, comments, shoutouts, join groups, contribute to initiatives. Set when an admin approves their verification (queue or direct).
Alumni Browse, edit own profile, view shoutouts (no posting). Default for any signed-in user.

Convener (separate concept)

A Convener leads one of the 12 causes. This is not a custom claim — it's stored in Firestore (causes/{key}.convenerUid + users/{uid}.convenerOf[]). A Convener has no extra system powers by default; the role is operational, not technical.

Why this matters A super admin can also be a Convener. An admin can be a Convener too. Convener-ness rides on top of whatever role you have.

3 Sign in / sign out

Sign in

  1. Open /admin/.
  2. Enter your phone in the field (10 digits — country code +91 is prepended automatically).
  3. Tap Send OTP. You'll get a 6-digit code via SMS.
  4. Enter the 6 digits (or use your phone's SMS autofill — the first input has autocomplete="one-time-code").
  5. Tap Verify & enter. If your phone is on the admin or super-admin list, you'll land on the Dashboard. If not, you'll be signed out with a "Not a super admin" toast.

Sign out

Bottom-left of the sidebar → Sign out. Your phone stays remembered for next time (Firebase persistence), but your auth tokens are cleared.

When you're added or your role changes Firebase ID tokens are cached for an hour. To pick up a new role or superAdmin claim immediately, sign out and sign back in. Otherwise it'll auto-refresh within an hour.

4 All users — search & filter

Sidebar → Alumni

The Alumni tab is the master list of every signed-in user — alumni, supporters, staff, admins, and anyone still pending the role fork. Up to 500 are loaded into memory and filtered as you type.

Filter chips (top of the table)

Four chips let you slice the list by user type:

All
Everyone except soft-deleted users (default).
Alumni
Users with role: 'alumni' — or legacy users created before the role field existed (anyone with batch / SSLC / PUC info is implicitly an alumnus).
🤝 Supporters
Well-wishers, parents, family, donors, ex-staff — anyone who picked the supporter track on the role-fork screen. They're auto-active and never enter the verification queue.
Pending verification
Brand-new users who signed in but haven't yet picked alumnus or supporter. Usually empty — useful for catching anyone stuck mid-fork.

Searching

The row at a glance

Photo + name
Either their uploaded avatar or an initials chip. Phone (or email) underneath.
Auth column
How they signed in — 📱 Phone (OTP-verified by Firebase) or ✉️ Google (Google-verified email). A faded chip means the data was inferred for users created before this field was captured. Every user has passed one of these first-stage identity checks — including supporters.
Institution / Relationship
For alumni: Faraan College / Faraan High School / Lilly Rose.
For supporters: their declared relationship (🤝 Parent, 🤝 Donor, 🤝 Family friend, etc.).
Batch
SSLC year for alumni. for supporters.
City
Free-text or one of the city pills.
Role chip
admin / staff / alumni / supporter / pending.
Status chip
For alumni: Verified / Unverified.
For supporters: Active (auto-active, no verification queue).
For everyone: Blocked / Deleted if applicable.
Actions
View · Verify · Edit · Role · Delete/Block — Verify only appears for unverified rows.

The View modal

Tap View for a read-only summary. The header line shows "Signed in via 📱 Phone OTP (verified)" or similar so you can see first-stage verification at a glance. For supporters specifically:

Phone (call) and WhatsApp are listed as separate rows — alumni who live abroad often have an Indian WhatsApp number and a Gulf calling number. The View modal surfaces both.

4b Supporters — the well-wisher track

A parent, donor, family member, or friend who arrives via a shared WhatsApp link doesn't have to fake being an alumnus to join. The supporter track gives them a slim, read-and-contribute view.

What is a supporter?

Anyone who, after signing in with phone OTP or Google, picks "🤝 I'm a well-wisher / supporter" on the role-fork screen. They go through a 60-second quick form (name + relationship + optional message) and land straight in the app — no verification queue.

The 6 relationship buckets

What supporters can do

What supporters cannot do

First-stage verification still applies

Supporters are not anonymous. Before they ever reach the role fork, Firebase has either verified their phone number (real SMS OTP challenge) or their Google email. You'll see this in the Auth column of the user list.

If a supporter is actually an alumnus

  1. Open their row, tap Role.
  2. Change to alumni, Save.
  3. They'll need to fill in batch info next time they open Edit Profile (or you can do it from the Edit modal).
  4. Click Verify on the same row to grant posting access.

If an alumnus is actually a supporter

Open their row → Role → set to supporter. Their verified claim stays as-is but client-side gates immediately hide posting UI on their next sign-in.

Why no admin approval for supporters? Supporters can't post, can't comment, can't browse the alumni directory — so the abuse surface is tiny. Auto-active means parents and donors don't get stuck in a queue and lose interest.

5 Verifying alumni

Two ways: directly from the Alumni row (you recognise them) or through the Verification queue (they uploaded proof).

Path A — Direct verify (you know them)

Sidebar → Alumni → find the row → Verify
  1. Click the green Verify button (visible only for unverified rows).
  2. Read the warning. Optionally type an internal note ("Saw them at the 2023 reunion", "Friend of Ayesha", etc.).
  3. Tap Verify now.

What happens server-side

Path B — Verification queue (they submitted proof)

Sidebar → Verification

The red badge next to "Verification" in the sidebar shows pending count. Each row shows:

  1. Click the proof thumbnail to enlarge it.
  2. Cross-check name, institution, batch against the proof.
  3. For batchmate references: tap the reference phone link to WhatsApp / call them and confirm.
  4. Tap Approve or Reject.

Approve does this

Reject does this

When to use which? Direct verify = trusted personal recognition. Queue = standard, auditable trail.

Editing an already-verified user

Verification is sticky once set. If you need to revoke it, use the Role button or contact a super admin — there is no one-click "Unverify" in the UI (intentional, to prevent admin foot-guns). To remove the claim manually, run npm run set-admin with the appropriate flags from the dev machine.

6 Editing a profile

Sidebar → Alumni → row → Edit

What you can change

What you can't change here

Audit Every edit writes to audit_log with action user.updated, your UID, and the patch contents.

7 Assigning roles

Sidebar → Alumni → row → Role

Tap Role on any alumni row. A dropdown shows the three roles you can assign.

The rules baked in

What happens after a role change

The target user gets a fresh custom claim on their Firebase Auth user. They need to sign out and back in for the change to take effect (or wait ~1 hour for the auto token refresh).

Don't promote casually Admin powers include hard-delete and content moderation. Promote only people you'd trust with WhatsApp admin powers on a community group.

8 Delete vs Block vs Soft delete

Sidebar → Alumni → row → Delete / Block

One button, three actions inside. Pick the one that matches your intent.

ActionWhat happensCan they sign back in?
Soft delete Sets deleted: true on the user doc. They disappear from the Alumni list but Firestore + Auth records survive. Yes — but they won't see content meant for verified alumni until restored.
Delete Removes the user doc and the Firebase Auth user entirely. Their memories/shoutouts stay (they become "ghost-authored"). Yes — they can register fresh with the same phone. New UID, blank profile.
Block Disables their Firebase Auth account. Adds their phone + email to config/blocked. Sign-in fails outright. Reversible via Unblock. No, until you unblock.
Hard-delete safety rails Only super admins can hard-delete. You can never delete your own account from here. Super admins can't be blocked. These checks are server-enforced.

Manage block (existing blocks)

If a user is already blocked, the button label becomes Manage block. You'll see an Unblock action that:

When to use what

9 Blocked users tab

Sidebar → Blocked users

Two distinct concepts share this tab:

  1. System-blocked accounts — users an admin has Blocked via the Alumni row (above). These are also in config/blocked.
  2. User-to-user blocks — a regular alumnus can block another alumnus via the ⋯ menu on a memory card. That mutes the other person from their feed only. This view lets admins see "who's muting whom" for moderation context.

This tab is read-only. To unblock at the admin level, use the Alumni tab → Manage block.

10 Merging duplicate accounts

When the same person has accidentally signed up twice — once with phone, once with Google.

The auto-merge UX (no admin action needed)

Users can self-merge through Edit Profile → Connect Google (or Add phone). The app detects the conflict and offers "Combine your profiles?". On tap, the server runs mergeAccountByPhone or mergeAccountByEmail depending on direction — moving memories, comments, contributions, group memberships, FCM tokens, inbox, blocked list, and verification claim from the orphan into the surviving account. The orphan is then deleted from Firestore + Auth.

When you may need to merge manually

Manual merge — admin SDK script

Run from the dev machine with the service-account JSON:

GOOGLE_APPLICATION_CREDENTIALS=./sa.json node -e "
const admin = require('./functions/node_modules/firebase-admin');
admin.initializeApp({ projectId: 'faraan-alumni-network' });
// ... callerUid + orphanUid + merge logic
// (see scripts/migrate-batch-schema.mjs for a similar pattern)
"

The pattern: copy orphan fields → patch caller doc → re-author memories/shoutouts/comments → move FCM tokens + inbox → delete orphan doc + Auth user → audit account.merged_admin_sdk.

Always audit Every manual merge must write to audit_log with action: 'account.merged_admin_sdk', fromUid, toUid, and a reason note. Future you will need this.

11 Memories — hide & delete

Sidebar → Memories

Table of the 50 most recent memory posts, newest first.

What each column shows

View — for full context

Shows the entire memory text + any attached photo. Has Hide/Restore + Delete buttons in the modal footer too, so you can act without dismissing the dialog.

Hide

Sets hidden: true on the memory doc. The post disappears from feeds and search but the doc + likes + comments + media are preserved. Reversible with Restore.

Delete

Hard-deletes the memory doc. Likes and comments are orphaned (still exist as subcollection docs, but unreachable from the UI). Photos and voice notes in Cloud Storage are NOT auto-deleted — use scripts/cleanup-orphan-media.mjs (TODO) for housekeeping.

Default to Hide If you're unsure whether the post crosses the line, hide it first. Delete is destructive.

12 Shoutouts moderation

Sidebar → Shoutouts

Identical UX to Memories — Type chip · Author cell · Text · State · View / Hide / Delete actions. The only difference is the underlying collection (shoutouts) and the CF (adminModerateShoutout).

Common reasons to moderate

13 Reported content

Sidebar → Reports

Anyone signed in can flag a memory, shoutout, comment, or user. Reports land here. The red badge next to "Reports" shows the open count.

Filter

Top-right dropdown: Open (default) · Resolved · Dismissed · All.

Per-row actions

Decision framework

Report saysWhat to checkLikely action
SpamAuthor's recent posts patternRemove content, possibly Block the user
Sensitive info exposedThe content itselfRemove content; DM author to repost
Wrong batch claimUser's profile vs proofResolve (no content action); review verification
Personal disputeContext of the conversationUsually Dismiss; encourage user-block instead
Comments are special Removing a reported comment doesn't have a one-click button — you have to open the parent memory and delete the comment manually via the memory's moderation flow. Modal will guide you.

14 Causes — the 12 pillars

Sidebar → Causes

The Trust runs 12 causes — Education Support, Orphan Care, Elderly Care, Healthcare Support, Marriage Assistance, Widow & Single-Mother Support, Needy Family Support, Skill Development & Employment, Awareness & Outreach, Religious & Spiritual Support, Disaster & Emergency Relief, Media & Documentation. Alumni opt into up to 3 from their Profile.

The table

Sort
The display order (1–12). Currently fixed by the seed script.
Cause
Icon + name + short description.
Active
Toggle chip. Inactive causes are hidden from new pickers but stay visible for alumni who already chose them.
Convener
Photo + name of the assigned alumnus, or "Unassigned".
Members
Live count of alumni who have this cause in their users.causes[] array.
Actions
Edit · Assign / Change convener · Unassign (when assigned)

Editing a cause

The Edit modal lets you change name, icon (emoji), color (hex picker), short description, and long description. active, convenerUid, and sortOrder are managed separately and not in this form.

Toggling active

Tap the Active chip in the row. No confirmation — it just flips. Use this when:

15 Assigning Conveners

Assigning

  1. On the Causes table, tap Assign convener (or Change convener if one exists).
  2. A modal opens with a search box. Type a name / phone / email — up to 50 matching alumni are shown.
  3. Tap a row → confirm → done.

What happens server-side

The setConvener CF runs in a single transaction:

Conveners are Firestore-only No custom-claim change. A convener has no admin powers unless they also have role: 'admin' separately. This is deliberate — Convener-ness is per-cause, not global.

Unassigning

Tap Unassign in the actions column. Confirm. Both sides of the link clear and the cause goes back to "Unassigned".

16 Initiatives — create, edit, close

Sidebar → Initiatives

An "initiative" is a specific fundraiser or programme — "Sponsor a Faraan student 2026", "Free health camp · Gulbarga", etc.

Creating a new initiative

  1. Tap + New initiative top-right.
  2. Fill: Title · Category · Cause (optional, picks one of the 12) · Goal in ₹ · Cover image URL · End date.
  3. Tap Create. Status defaults to live. raised: 0, supportersCount: 0.

Editing

The Edit modal lets you change everything except raised, supportersCount, and createdAt (those are immutable here — they're driven by the contribution flow). Status can be flipped between live / paused / completed. Cover image: upload directly or paste a URL.

Posting an update

Tap Post update. Add a short message + optional photo. This appears under the initiative on the public side and gets a push notification to supporters.

Close

Click Close on a live initiative. Sets status: 'completed', closedAt: now, closedBy: yourUid. The contribution ledger is preserved.

Delete

Permanent. Use only for accidental creations / test data. The contributions ledger is also lost. Confirm dialog warns about this.

Auto-rollover

The rolloverInitiatives CF runs daily at 00:30 IST. Any initiative past its endsAt gets auto-closed with status: 'completed'. No manual action needed.

17 Contribution ledger

Sidebar → Initiatives → row → Ledger

Per-initiative list of every contribution recorded: contributor name, amount, anonymous flag, timestamp, optional note.

How contributions are recorded today

The app's payment integration is not yet wired. Contributions are coordinated manually by the Finance & Fundraising lead (currently Dr Shakeel on +91 96634 14714). When money is received:

  1. Open the Initiative ledger
  2. (Future feature) Tap Record contribution → fill amount + contributor UID + anonymous flag
  3. The recordContribution CF writes a subcollection doc and increments raised + supportersCount atomically
Manual recording UI is on the roadmap Until then, contributions are tracked offline by the Finance & Fundraising lead and bulk-imported via a dev script when needed.

18 Batches

Sidebar → Batches

The batches collection is auto-seeded — SSLC batches 1985 to current year for Faraan High School + Lilly Rose Primary. A scheduled CF (addYearlySSLCBatch) runs every Jan 1 at 00:05 IST to add the new year's batches automatically.

What you can do

Member count maintenance

The onUserBatchChange Firestore trigger fires on any user-doc write. It diffs the user's SSLC/PUC year/institution before vs after, then updates the matching batch's membersCount. PUC batches are created lazily on first pick.

Don't touch membersCount manually Let the trigger maintain it. If it drifts, run npm run migrate-batch-schema to recount from the user collection.

19 Groups

Sidebar → Groups

Groups are community spaces — Batch reunion circles, Doctors Network, Gulf Alumni Circle, etc.

Creating a group

Tap + New group. Fields: Name · Emblem (1–4 chars) · Color (gradient palette pick) · Description · Cover image.

Editing / deleting

Same modal pattern. Delete is hard-delete (cascades members subcollection — Firestore doesn't auto-cascade, so the CF does it explicitly).

Member management

Tap a group row → Members to see who joined and when. Remove individual members via the row's danger button.

Private groups (Women's Wing, etc.) The current Groups module does NOT support private + admin-approved joining. All groups today are public + instant-join. A "private group" feature (with requiresApproval flag + pending-joins queue + approve action) is pending — it's Task 6 of the SOP v2.1 implementation plan. Until then, manage admission outside the app via WhatsApp + remove non-members manually.

20 Volunteers

Sidebar → Volunteers

Anyone who tapped the "Volunteer" pill in the app fills a short form (city + WhatsApp + interests). Each submission lands in volunteers/{uid}.

What the table shows

Coordination tips

21 Broadcasting announcements

Sidebar → Broadcast

Sends an in-app inbox notification + a push notification to every alumnus on the network. Use sparingly — too many broadcasts dull the signal.

Fields

The send action

  1. Fill the fields. The character counters help you stay short.
  2. Tap Send broadcast.
  3. The CF creates an announcements/{id} doc + sends FCM multicast to every fcmTokens doc across all users (batched 500 at a time).
  4. Toast confirms: "Sent · reached X devices".

Recent announcements

Below the form: a history table of every broadcast sent, with Delete action. Deleting an announcement doesn't unsend the push — it only removes the inbox record going forward.

Volume rules of thumb
  • Major news (verified launch, big event): max once a week
  • Initiative updates: better to post inside the initiative (auto-pushes to supporters only)
  • Personal greetings (Eid, Ramzan): yes, but craft them well

22 Site content editor

Sidebar → Site content

Every user-facing string in the app (hero title, onboarding copy, section labels, etc.) is editable here without redeploying. Changes hit Firestore content/strings and propagate within ~60 seconds via the live listener.

Organised by section

Save behaviour

Each section card has its own Save button. The CF validates that no key exceeds 500 chars + that the writer is admin/staff, then writes content/strings.{key}.

Preview before pasting Long strings can break layouts. Test on mobile first — many of these strings sit in tight headers.

23 Super admins

Sidebar → Super admins

Visible only to users with the superAdmin: true claim. Non-supers see "Access restricted".

Adding a super admin

  1. Confirm the person has signed in at least once with their phone OTP on the main app. If not, ask them to do that first.
  2. Open Super admins. Type their phone in strict E.164: +91XXXXXXXXXX. No spaces, no dashes.
  3. Tap Add super admin. Confirm the warning.
  4. Tell them to sign out and back in on their device to pick up the new claim.

Removing

Tap Remove on their row. Confirm. They keep alumni access but lose super-admin powers immediately on next token refresh.

Safety rails (server-enforced)

Lockout protection The config/super_admins doc is the allowlist. If it ever gets deleted or empty, no one can sign in as super admin again. To recover: run npm run seed-admins -- +91XXXXXXXXXX from the dev machine with the service-account JSON.

24 Audit log

Sidebar → Audit log

Every meaningful admin action writes a row here. The Dashboard tab also surfaces the latest 15.

What's logged

Reading a row

ColumnWhat it means
WhenUTC timestamp formatted for IST.
ActionNamespaced, e.g. user.verified_direct, cause.convener_set, account.merged_by_email.
ByFirst 10 chars of the actor's UID. Cross-reference in the Alumni tab if needed.
DetailJSON blob of additional context — target UID, fields changed, reason notes, etc.
The audit log is append-only No row can be edited or deleted from the UI. The audit_log collection has a write-once rule. If someone tries to tamper, the rule rejects.

25 Troubleshooting

Common failure modes + their fixes.

"User not found" when adding a super admin

The target phone doesn't have a Firebase Auth user yet. Ask them to sign in once with that phone (OTP flow) on the main app, then retry. The Auth user is created on first successful OTP.

"Not a super admin — signing out" when I try to enter /admin/

Your account has the admin role but not the superAdmin flag, OR your account isn't admin at all. Check config/super_admins.phones from the dev machine — your phone must be on the list. If you just got added, sign out and sign back in to refresh your token.

Email notifications aren't arriving

  1. Check the Gmail account (zaheerdv@gmail.com) Spam folder.
  2. Check Cloud Function logs: firebase functions:log --only onUserDocCreate. If you see "Onboarding email failed: Invalid login", the Gmail app password has expired. Rotate it (see Key rotation).
  3. Check throttle — only 30 onboarding emails go out per 24h. The 31st onwards is queued for the daily summary (09:00 IST).

Push notifications aren't delivering

Known limitation: the Firebase web vapidKey is empty in firebase-config.js. FCM token registration silently short-circuits. Browser push doesn't fire in production — only the in-app inbox works. Fix on roadmap. Native iOS / Android PWA installs also don't have a vapid path yet.

An alumnus' name shows as "Alumni" in the admin list

This used to happen for every phone-OTP signup (Firebase Auth provides no displayName for phone, and ensureUserDoc falls back to the literal string "Alumni"). It's fixed now — new signups are routed through batch onboarding which forces a real name. Existing "Alumni" rows get re-routed once on next sign-in. If a particular user is stuck, manually edit via Alumni → Edit.

Causes don't load in the Profile screen

Check that config/super_admins rules are deployed AND that /causes has the read rule. Run firebase deploy --only firestore:rules from dev. The signed-in alumnus needs the rule to read the collection.

"Permission denied" when an alumnus tries to post a memory

They're not verified. The Firestore rule for memories.create requires isVerified(). Verify them (Alumni → Verify) and ask them to sign out + back in to pick up the claim.

I see two profiles for the same person

Auto-merge should have caught this but if it didn't: the user can run the merge themselves via Edit Profile → Connect Google / Add phone — the conflict dialog will offer it. If they can't, do it manually (see Merging duplicate accounts).

A cause shows 0 members but I know people picked it

The members count is a live array-contains query, so it's never stale. If it's 0, double-check the cause key matches what's in users.causes[]. If they drift (rare), run npm run migrate-batch-schema as a one-off recount — it covers causes too.

26 Key rotation

Rotate these every 90 days. Calendar reminder recommended.

Gmail app password

  1. Sign in to myaccount.google.com/apppasswords as zaheerdv@gmail.com.
  2. Delete the existing "Faraan Alumni" app password.
  3. Create a new one named "Faraan Alumni" → copy the 16-char password.
  4. Update Secret Manager: gcloud secrets versions add GOOGLE_APP_PASS --data-file=- then paste → Ctrl+D. Or via the Cloud Console UI.
  5. Disable the previous version of the secret.
  6. Cloud Functions pick up the new password on their next cold start (~10 min) or redeploy: npm run deploy:fns.

Firebase Admin service account JSON

This is the faraan-alumni-network-firebase-adminsdk-*.json file used by the dev-machine scripts.

  1. Open Cloud Console → IAM → Service Accounts.
  2. Find firebase-adminsdk-*@faraan-alumni-network.iam.gserviceaccount.com → Keys tab.
  3. Delete the old key. Generate a new JSON key.
  4. Save it to the project root with the same filename pattern (it's gitignored + hosting-ignored).
  5. Distribute to teammates who run scripts.

App Check (reCAPTCHA Enterprise)

App Check secrets are managed by Google — they auto-rotate. No action needed. To verify enforcement: npm run enable-appcheck -- --enforce from dev. Currently App Check is configured but not yet enforced on the Firebase APIs.

What to do if a key leaks Treat any leaked service-account JSON or app password as a P0 incident. Revoke immediately via the consoles above. Then audit audit_log for any unauthorised admin actions in the window between leak and revocation.

27 Backups & exports

No automatic backups today. Manual procedures + recommended schedule.

Recommended schedule

CadenceWhatWhere
WeeklyFirestore export of users, memories, initiatives, causesGCS bucket
MonthlyFull Firestore exportGCS bucket → cold storage
QuarterlyAuth user export (UIDs + phone/email mappings)Encrypted local archive

How to do a one-off Firestore export

gcloud firestore export gs://faraan-alumni-backups/$(date +%Y%m%d) \
  --project=faraan-alumni-network

Requires the faraan-alumni-backups GCS bucket to exist (one-time setup) + the service account to have the Cloud Datastore Import Export Admin role.

Restore

gcloud firestore import gs://faraan-alumni-backups/20260601 \
  --project=faraan-alumni-network
Restore is destructive Importing overwrites the current Firestore with the snapshot. Use only in a true recovery scenario.

User export (GDPR-style)

For a single user requesting their data: pull their users/{uid} doc, their memories (where authorId = uid), their contributions (collectionGroup), and their notifications inbox. Email as a JSON file. There's no UI for this yet — use the dev SDK directly.

28 Glossary

UID
Unique ID assigned by Firebase Auth on first sign-in. Immutable. Used everywhere as the canonical reference to a user.
Custom claim
A key/value pair Firebase Auth carries inside the user's ID token. Server-enforced. role, superAdmin, verified are ours.
OTP
One-Time Password — the 6-digit SMS code Firebase sends during phone sign-in.
FCM
Firebase Cloud Messaging — Google's push-notification pipe. Each device that grants permission registers a token; we send pushes to those tokens.
App Check
Verifies that requests to Firebase APIs come from your real app, not bots. Backed by reCAPTCHA Enterprise SCORE. Configured but not yet enforced.
PWA
Progressive Web App. The site can be "installed" on a phone home screen and behaves like a native app (service worker, offline cache, push). Faraan Alumni is a PWA.
Service worker
A background JS file (sw.js) the browser runs to cache assets + intercept fetches. Bumping VERSION in sw.js forces installed apps to pick up new code on next load.
Convener
Operational lead for one of the 12 causes. Firestore-only, NOT a custom claim.
E.164
International phone-number format: + followed by country code and number, no spaces. +919876543210. Required everywhere phones are stored.
SSLC / PUC
Indian school levels — SSLC = Class 10 / Secondary School Leaving Certificate. PUC = Class 11–12 / Pre-University Course. The app distinguishes them as separate batch types.

Found something not covered here? Ask the Charity Head (currently Dr Shakeel — +91 96634 14714) or open a question with the dev team.