The Problem
When Typhoon Emong hit La Union in 2025, volunteers self-organized across municipalities to move meals, water, and medical supplies to the people who needed them. Coordination lived in group chats. Tracking lived in spreadsheets, when it happened at all. There was no shared, real-time picture of what was needed, what had already been sent, or where the gaps were, so aid clustered in some barangays while others waited.
The Solution
Kapwa Help turns that scramble into one coordinated system: a real-time relief map of field-reported needs, a transparency dashboard of donations and distribution, and a multilingual report form anyone on the ground can use. At its core it is a matching engine. Reported needs (demand) are verified, matched against the goods sitting in deployment hubs (supply), then dispatched and confirmed, with every step visible to donors and coordinators.
How It Works
The app is a client-side PWA designed for disaster relief areas with unreliable connectivity.
- Offline-first by default. Reports are composed and queued in an IndexedDB outbox with a client-minted UUID, so a volunteer can submit with no signal. When the device reconnects, the queue replays into atomic database calls. A duplicate replay hits a unique constraint and is dropped silently, so a double submission offline never creates double records.
- Built for bad networks. A service worker precaches the app shell, serves data network-first and map tiles cache-first, so the map still opens when the connection does not.
- Auth at the database, no server. With no backend to gate writes, Row Level Security becomes the auth boundary. The browser uses an anonymous key, sensitive writes are gated by an
is_admin()Postgres check, and the public reads everyone sees come through PII-stripped views rather than the base tables. - Atomic multi-table writes. A need and its aid categories (a parent row plus its junction rows) are written inside a single transaction, so a partial write is impossible. That matters most when the client is replaying a queued submission.
From Report to Relief
A need is not just a row. It moves through a lifecycle, and a different person acts at each stage.
The status lives in a Postgres need_status enum, and transitions are enforced in the database layer rather than the client. Marking a need as in transit and creating its deployment is one atomic operation, so a hub commitment and the need's status can never drift apart.
Built to Run Anywhere
- Trilingual. English, Filipino, and Ilocano. New strings are bootstrapped by a machine-translation script that preserves interpolation variables, then sent for human review.
- Zero-budget by design. Supabase, Vercel, and OpenStreetMap free tiers, with no paid services, so a volunteer-run project can survive without funding.
- Measured where it matters. Performance is checked against throttled 2G/3G and a low-end Android profile, not desktop scores, ensuring consistent performance across areas.
It is a working demo today, not yet launched. But it is built so that when the next storm comes, the response can start from a shared, transparent system instead of a group chat and a spreadsheet.
