A humanitarian aid coordination platform connecting displaced people in Lebanon with the NGOs and resources they need, in real time.
- What is Nasna?
- Why Nasna?
- Contributors
- Features
- Users & Personas
- Tech Stack
- Workflows
- Security
- How to Start
Nasna (ناسنا) is an Arabic word meaning "Our People." It is a full-stack humanitarian aid coordination platform built specifically for the displacement crisis in Lebanon.
The platform operates as a three-sided network:
- Displaced families register their needs either through field agents on the ground or through self-service public forms. A WhatsApp intake flow also exists in the codebase and can be enabled/configured separately.
- NGOs and aid initiatives receive a live feed of cases that match their coverage area and aid types, claim them, and record aid delivery.
- Admins oversee the entire pipeline, from intake to fulfillment, with real-time dashboards, dispatch tools, and an operations map of Lebanon.
On the public side, Nasna also serves as a community resource hub where anyone can browse available housing, view emergency contacts and hotlines, offer housing to displaced families, or donate.
Everything is built with one principle in mind: this platform handles real data about real people in a real crisis. Every feature, security rule, and architectural decision reflects that weight.
In the autumn of 2024, Lebanon experienced one of its most devastating waves of displacement. Hundreds of thousands of people, families, elderly, children, were forced from their homes in a matter of days. Aid was available. NGOs were mobilized. Volunteers were ready. But the coordination layer was missing.
People didn't know which shelter had capacity. NGOs didn't know which families needed what. Field agents were collecting data on paper. Housing offers weren't reaching the people who needed them. Emergency contacts were scattered across WhatsApp groups and Facebook posts.
Nasna was built to be that coordination layer.
| Problem | What Nasna does |
|---|---|
| Field agents registering families on paper or ad-hoc tools | Structured multi-step form with offline support, works even without internet |
| NGOs flooding into the same areas, duplicating effort | Coverage profiles match each NGO to the specific areas and needs they serve |
| No visibility into case status after intake | Live case pipeline: pending → assigned → in progress → completed |
| Displaced people having no way to register independently | Public self-service intake is available today, and the WhatsApp intake flow can be enabled for zero-install registration |
| Housing offers not reaching those who need them | Public housing marketplace with admin moderation |
| No single source of truth for emergency contacts | Verified, categorized, searchable emergency directory |
| Admins unable to see the full picture | Live operations map showing every case, center, and NGO across Lebanon |
| No accountability for case fulfillment | Aid delivery recording, case timelines, and stale case detection |
Nasna doesn't replace the people doing the work. It gives them the infrastructure to do it better.
| Name | Role |
|---|---|
| Abed El-Fattah Amouneh | Product Manager & Frontend Developer |
| Mohammad Homsi | Full Stack Developer & Tech Lead |
| Rami Kronbi | AI Engineer & Developer |
| Lynn El Solh | Multimedia Designer |
We welcome contributions from anyone who wants to help. Whether you're a developer, designer, translator, field worker, or someone with domain knowledge in humanitarian aid, reach out and let's talk.
📬 Get in touch: open an issue on GitHub or contact the team directly through nasna.world.
- Centers Map: Interactive Leaflet map showing all displacement centers, displacement sites (with pulsing teardrop markers), and available housing across Lebanon. Layer controls, popups with contact info, directions links.
- Housing Directory: Browse available housing listings with filters by governorate, price type, and capacity. Each listing shows amenities, availability dates, and a direct WhatsApp contact link.
- Offer Housing: Public form to list an available property. Submitted as pending review and goes to admin moderation before going live.
- Emergency Contacts: Verified directory of emergency numbers (government, health, NGOs, security, legal, utilities) searchable by category and region.
- Hotlines: Quick-access emergency hotline numbers.
- Impact Dashboard: Live public statistics: families registered, cases completed, people helped, active NGOs, housing available.
- Donate: Stripe-powered donation flow (support a family, a center, or an NGO).
- Offer Help: Form for individuals and organizations to register as volunteers or aid providers.
- Resources: Curated external links and useful information for displaced people.
- Feedback: Anonymous feedback form.
- Case Registration: A carefully designed multi-step form that collects:
- Personal info (name, phone, gender, email)
- Current location (with family or at a displacement center)
- Household composition (size + age ranges for children, adults, elderly)
- Needs (food, water, shelter, medical, clothing, baby supplies, psychosocial, legal documents)
- Urgency level and special needs (medical conditions, wheelchair access, pregnancy, etc.)
- Explicit consent (always required, cannot be bypassed)
- Offline Support: If the agent loses connectivity, the form saves to IndexedDB. On reconnect, all queued submissions sync automatically.
- Duplicate Detection: Before submitting, the system checks if the phone number is already registered and warns the agent.
- Bulk Upload: CSV batch registration for on-site center intake.
- My Submissions: Paginated, live-updated list of all cases the agent has registered.
- Coverage Profile: Each NGO defines exactly where they operate (by governorate, by center, or both) and what aid they provide. They also set their maximum case load and delivery mode (pickup/delivery/both).
- Case Feed: A live-updated queue of pending cases that exactly match the NGO's coverage area and aid types, nothing irrelevant, nothing outside their scope. The displaced person's WhatsApp number is never exposed in this view.
- Case Management: Claim cases, move them through the pipeline (Assigned → In Progress → Completed), and record each aid delivery with date, type, and notes.
- Case Timeline: Full audit trail of everything that's happened on a case.
- Dispatch Center: The operations hub. Live queue of all pending submissions with urgency indicators, time-since-submission, stale case highlighting (24h+ without update), and auto-suggested NGOs for each case.
- Operations Map: Full Lebanon map showing every active submission (color-coded by urgency), all displacement centers with live capacity bars, available housing areas, and NGO coverage zones. Filterable by date, status, urgency, and need type.
- Submissions Management: Full table of all submissions with search, filter, edit, and status management.
- NGO Management: Review and validate NGO member accounts. View each NGO's coverage profile and current case load.
- Agent Management: List of all field agents with the ability to deactivate accounts.
- Center Management: Full CRUD on displacement centers: capacity tracking, contact info, aid services offered, operating hours, intake open/closed toggle, coordinates.
- Housing Moderation: Review queue of pending housing submissions. Approve to make public, or reject.
- Emergency Contacts Management: Add, edit, and verify emergency contacts. Set display order by category.
- Impact Dashboard: Live statistics with charts (cases over time, by governorate, by need type). CSV export of all submission data.
- Feedback Management: Review, mark as read, and archive public feedback.
- WhatsApp Intake & Notifications: The codebase includes a WhatsApp webhook flow plus outbound messaging helpers. Production use depends on Meta/WhatsApp configuration and approved templates.
- Email Notifications: The codebase includes SendGrid-backed email helpers. Production delivery depends on SendGrid credentials and sender setup.
Who they are: A family forced from their home, they may be with relatives, staying at a school turned shelter, or without a fixed location. They likely have no laptop, may be in a stressful or dangerous situation, and need help immediately.
How they interact with Nasna: They do not need to install an app or create an account. They can be registered by a field agent, use the public intake flow directly, or use the WhatsApp intake flow when that channel is enabled. Their privacy is treated as a non-negotiable design requirement: phone number, location, and household data are protected at every layer.
Who they are: A social worker, volunteer coordinator, or community leader working on the ground, often at a school, community center, or in the field. They may have intermittent connectivity, are registering many families quickly, and need a form that doesn't lose their data.
What they need from Nasna: A fast, reliable intake form that works offline, catches duplicate registrations, and lets them track what they've submitted. They see only their own cases, nothing else.
Who they are: A registered organization, community initiative, or volunteer network that provides specific types of aid (food distribution, medical support, shelter, legal assistance, etc.) in specific parts of Lebanon. They have capacity limits and specific delivery capabilities.
What they need from Nasna: To see only the cases they can actually help with, filtered by their area and what they offer. To claim cases and not have them claimed by someone else simultaneously. To record what they delivered. To not be overwhelmed by irrelevant requests.
Who they are: A platform operator or humanitarian coordinator overseeing the entire pipeline. They need to see the big picture, intervene when cases stall, ensure NGOs are validated and appropriate, and report on impact.
What they need from Nasna: A real-time view of everything, the dispatch queue, the map, the statistics. Tools to manually assign cases, manage NGO access, moderate housing, and export data for reporting.
Who they are: Anyone, a landlord with a spare apartment, a diaspora member looking to donate, a journalist covering the crisis, a person needing emergency contact information.
What they need from Nasna: Clear, public-facing information with no barrier to entry. The ability to offer housing, donate, or get emergency numbers without creating an account.
| Technology | Version | Purpose |
|---|---|---|
| React | 19 | UI framework |
| Vite | 7 | Build tool & dev server |
| TypeScript | 5.9 | Type safety |
| Tailwind CSS | 4 | Utility-first styling |
| shadcn/ui (Radix UI) | Latest | Accessible component primitives |
| React Router | 6 | Client-side routing |
| Zustand | 5 | Global auth/session state |
| React Hook Form | 7 | Form state management |
| Zod | 4 | Schema validation |
| react-leaflet / Leaflet | 5 / 1.9 | Interactive maps |
| Recharts | 3 | Charts and data visualization |
| i18next + react-i18next | 25 / 16 | Internationalization (AR, EN, FR) |
| Sonner | 2 | Toast notifications |
| Motion / Lottie | 12 / 2 | Animations |
| date-fns | 2 | Date formatting |
| idb | 8 | IndexedDB (offline support) |
| Lucide React | Latest | Icons |
| Technology | Version | Purpose |
|---|---|---|
| Firebase Firestore | 12 | Real-time NoSQL database |
| Firebase Auth | 12 | Authentication (email/password + Google) |
| Firebase Cloud Functions | Node.js 22 | Server-side logic & triggers |
| Firebase Hosting | - | Static asset delivery + CDN |
| Stripe | - | Donation payments |
| SendGrid | - | Email notifications (pending) |
| Twilio | - | WhatsApp bot (in development) |
| Tool | Purpose |
|---|---|
| pnpm | Package management |
| Prettier | Code formatting |
| ESLint | Linting |
| Jest + Testing Library | Unit and integration tests |
| GitHub Actions | CI/CD (format check, translation parity, typecheck, tests, dependency review, CodeQL, deploy) |
- Firestore as source of truth: All data lives in Firestore. No secondary database.
- Cloud Functions for sensitive logic: Case matching, stats aggregation, scheduled stale-case detection, and all PII-sensitive operations run server-side.
- Field masking at the API layer: The WhatsApp phone number of displaced people is stripped by Cloud Functions before any data is returned to NGO members. Firestore rules alone cannot mask individual fields, so this is enforced in code.
- Offline-first for agents: Forms are saved to IndexedDB if connectivity is lost. A sync queue flushes automatically on reconnect.
- Real-time where it matters, snapshots where it is safer: Operational dashboards and queues use Firestore listeners, while capped or map-style views use explicit snapshot reads with limits and manual refresh when appropriate.
- Cursor-based pagination for large collections: Large admin and operational list views use the
usePaginatedQueryhook. Unbounded reads on large collections are avoided. - Consent is a hard constraint:
consentGivendefaults tofalse, is enforced by Zod asz.literal(true), and cannot be removed or made optional anywhere in the codebase. - RTL-aware UI: Arabic is the primary language. Document direction switches automatically with the selected locale. All layouts must be tested for RTL compatibility.
Nasna handles real personal data about real people in a crisis. The security model reflects that:
- No PII in logs: Console logs never include submission objects, user objects, or anything containing personal data. Only document IDs.
- No PII in URLs: Phone numbers, names, and identifiers never appear in query strings or route parameters.
- No auth tokens in localStorage: Firebase Auth manages sessions natively. No manual token storage.
- Role-based Firestore rules: Agents see only their own submissions. Members see only cases matching their coverage area. The public sees only approved or available non-PII data, depending on the collection. Admins have full access.
- wa_sessions is Cloud Functions only: The WhatsApp bot session state collection is never read or written by client code under any circumstances.
Agent logs in
└─► Multi-step form (personal → location → household → needs → consent)
├─ No connectivity? → Saved to IndexedDB queue
│ └─ On reconnect: auto-sync flushes queue
└─ Connected? → Duplicate phone check (Cloud Function)
├─ Duplicate found → Agent warned, can confirm to proceed
└─ Clear → Submit to Firestore as status: 'pending'
└─► Cloud Function: onNewSubmission
└─ Match NGOs by coverage + aid types
└─ Write notifications to matched NGOs
Member sets coverage profile (governorate/center + aid types + max case load)
└─► Case feed loads (Cloud Function strips whatsappPhone)
└─ Member reviews case → clicks Claim
└─► claimMemberCase (atomic Cloud Function)
├─ Case claimed by someone else? → Conflict returned
└─ Success → status: 'assigned', assignedTo: member.uid
└─ Member manages case in My Cases
└─ Records aid delivery (type, date, notes)
└─ Marks status: 'completed'
└─► Cloud Function: onCaseCompleted
└─ Decrement member case load
└─ Increment stats/global
Public visitor fills /offer-housing
└─► Submitted as status: 'pending_review'
└─► Admin reviews at /manage/housing
├─ Approve → status: 'available' (goes public)
└─ Reject → status: 'rejected'
Public visitor browses /housing
└─► Filtered query: status === 'available'
└─ HousingCard grid: type, area, capacity, price, amenities
└─ "Contact via WhatsApp" → opens wa.me/[listerPhone]
(listerName and listerPhone never shown in the UI, admin only)
Admin opens /manage/dispatch
└─► Live queue: pending cases ordered by urgency + time
├─ Stale cases (24h+ unassigned) highlighted in red
└─ Admin selects case → system suggests matched NGOs
└─ Admin assigns → status: 'assigned'
└─► NGO notified, case enters their My Cases
Admin opens /manage/operations-map
└─► Cloud Function: getOperationsMapData returns:
├─ Submission clusters (color-coded: red=high, yellow=medium, green=low/assigned)
├─ Displacement center markers (capacity bar, intake open/closed)
├─ Approved housing areas
└─ NGO coverage zones
└─ Filters: date range, status, urgency, need type
└─ Click any marker → popup with full details + action link
Anyone opens /centers-map
└─► Loads aid centers + displacement sites + housing from Firestore
├─ Aid center pins (teal, with capacity + services popup)
├─ Displacement site pins (pulsing orange teardrop, person icon + contact + directions)
└─ Housing area circles (scaled by listing count)
└─ Layer controls: toggle each layer on/off
└─ Mobile: full-screen map + bottom sheet layer controls
Nasna handles real personal data about real people in a crisis. Security is not a feature, it is a foundation. The following principles are enforced at every layer of the platform and are non-negotiable regardless of urgency or convenience.
Personal data in Nasna, names, phone numbers, locations, household compositions, and medical needs, belongs to people in vulnerable situations. It is treated accordingly.
- No PII in logs.
console.log(submission)orconsole.log(user)is never acceptable. Only document IDs are ever logged. - No PII in URLs. Phone numbers, names, and personal identifiers never appear in query strings or route parameters.
- No auth tokens in localStorage. Firebase Auth manages sessions natively. No manual token storage anywhere in the codebase.
whatsappPhoneis never exposed to NGO members. Firestore rules alone cannot mask individual fields on a document, so this is enforced at the Cloud Functions layer. The field is stripped server-side before any data is returned to a member-facing query.listerNameandlisterPhoneon housing listings are admin-only. Public housing views never include the lister's personal information.
Every role sees exactly what they need, nothing more.
| Who | What they can access |
|---|---|
| Public (unauthenticated) | Approved housing listings, emergency contacts, centers map, public stats |
| Field Agent | Create submissions, read only their own submissions |
| NGO Member (validated) | Pending cases matching their coverage area and aid types, no other cases, no PII fields |
| Admin | Everything |
This is enforced by Firestore security rules, not just frontend routing. Bypassing the UI does not bypass the rules.
consentGiven on every submission defaults to false. It is enforced by Zod as z.literal(true), meaning the schema literally rejects anything other than an explicit true. It cannot be removed, made optional, or set as a default true anywhere in the codebase. This is a legal and ethical requirement.
Sensitive operations that cannot be secured by Firestore rules alone are moved to Cloud Functions running under the Firebase Admin SDK:
- Case matching: The logic that determines which NGO sees which case runs server-side, not in the browser.
- Phone deduplication: Checking if a phone number already exists in the database is done via Cloud Function, not by querying submissions directly from the client.
wa_sessionscollection: The WhatsApp bot session store is entirely off-limits to client code. No reads, no writes, no exceptions.
All data written to Firestore passes through Zod validation on the client before it is ever sent. Firestore security rules provide a second layer of enforcement on the server. Neither layer is considered sufficient on its own.
firebase.json enforces strict CSP headers on the hosted app, restricting resource loading to self, Firebase APIs, Google APIs, and Stripe. This mitigates XSS vectors.
If you discover a security issue, especially one involving access to submission data or personal information, please do not open a public GitHub issue. Contact the team directly through nasna.world so it can be addressed privately.
Nasna is live at nasna.world. No installation required. Here is how each type of user gets started.
You don't need an account for any of this.
- Find housing → go to nasna.world/housing. Browse available listings, filter by governorate and price type, and contact the lister directly via WhatsApp.
- Offer housing → go to nasna.world/offer-housing. Fill in your property details and submit. The team reviews and approves listings before they go public.
- Find emergency contacts → go to nasna.world/emergency. Search by category (health, legal, shelter, etc.) or region.
- See displacement centers on the map → go to nasna.world/centers-map. View all active centers, displacement sites, and available housing across Lebanon.
- Donate → go to nasna.world/donate. Choose to support a family, a center, or an NGO.
Field agents register displaced families on the platform.
- Request an account: contact the Nasna team through nasna.world to have an agent account created for you.
- Log in at nasna.world/auth/login.
- Register a family → go to the New Submission form. Walk through the steps: personal info, location, household, needs, and consent. Submit when done.
- Working offline?: the form saves automatically if you lose connectivity. Once you're back online, your submissions sync automatically.
- Track your submissions → your personal submissions list shows the status of every case you've registered.
NGOs use Nasna to receive a filtered feed of cases that match exactly what they offer and where they operate.
- Register your organization → go to nasna.world/auth/register and complete the registration form.
- Wait for validation: a Nasna admin reviews and approves your account before you can access cases. This ensures displaced people's data is only shared with verified organizations.
- Set your coverage profile → after logging in, define your coverage area (governorates or specific centers), the types of aid you provide, your maximum case load, and your delivery mode (pickup, delivery, or both). This is what the system uses to match cases to you.
- Browse your case feed → you'll see only the cases that match your profile, nothing irrelevant. Review each case, and click Claim to take ownership.
- Manage your cases → track progress in My Cases, update the status as you work, and record each aid delivery when complete.
Admins have full access to the platform and manage the entire pipeline.
- Admin accounts are created directly by the team, contact nasna.world to request access.
- Log in and you'll be taken to the admin dashboard at
/manage. - Dispatch Center (
/manage/dispatch), your primary daily view. Review the live queue of pending cases, see urgency and time since submission, and assign cases to NGOs. - Operations Map (
/manage/operations-map), the full-Lebanon view. Every active case, center, and NGO coverage zone on one map. Filter by date, status, urgency, or need type. - Validate NGOs (
/manage/ngo), review and approve incoming NGO registrations before they can access case data. - Manage centers (
/manage/centers), add, edit, and update displacement centers including capacity, services, and intake status. - Moderate housing (
/manage/housing), review pending housing submissions from the public and approve or reject them. - Impact Dashboard (
/manage/impact), live stats on families registered, cases completed, people helped, and more. Export to CSV for reporting.
If you want to run Nasna locally or contribute to the codebase:
Prerequisites: Node.js 22+, pnpm 10+, Firebase CLI
git clone https://github.com/voxire/nasna.git
cd nasna
pnpm install
cp .env.example .env # fill in your Firebase credentials
pnpm start # runs at http://localhost:5173Local development with Firebase Emulators (recommended — no production data touched):
# Terminal 1 — start emulators (data persists between sessions)
pnpm emulate
# Terminal 2 — seed test users and sample data (run once)
pnpm emulate:seed
# Terminal 3 — start the app pointed at emulators
VITE_USE_EMULATOR=true pnpm startOpen http://localhost:4000 for the Emulator UI and http://localhost:5173 for the app.
If you fork the project, update the Firebase auth domain in src/firebase.ts to match your own Firebase/Auth setup before testing Google sign-in.
| Account | Password | Role |
|---|---|---|
admin@nasna.test |
Test1234! |
Admin |
ngo@nasna.test |
Test1234! |
NGO member |
agent@nasna.test |
Test1234! |
Field agent |
Before every commit:
pnpm format # auto-fix Prettier
pnpm translations:check
pnpm tsc # TypeScript must exit 0Deploy:
pnpm build
firebase deploy --only hosting
firebase deploy --only firestore:rulesBuilt with care for the people of Lebanon.