CrowdPM is a TypeScript/Firebase platform for crowd-sourced PM2.5 measurements. It pairs sensor nodes with Firebase users, accepts DPoP-bound ingest batches, stores raw payloads for audit, processes measurements into Firestore, and renders public or owned batches on a React/WebGL map.
The project has two supported environments:
local development: Vite plus the Firebase Emulator Suite using the local project IDcrowdpm-local.deployed: Firebase Hosting, Cloud Functions, Firestore, Storage, and Auth in the configured Firebase project.
| Area | Path | Responsibility |
|---|---|---|
| Frontend | frontend/ |
React 19 + Vite app, Google Maps/deck.gl map, activation UI, dashboards, moderation UI. |
| Functions | functions/ |
Firebase HTTPS Functions, Fastify REST API, pairing, DPoP validation, ingest processing, admin routes. |
| Shared types | shared-types/ |
Workspace package consumed by the frontend and functions for API/data contracts. |
| Scripts | scripts/ |
Local device emulator, deployed-device helpers, and workspace packaging utilities. |
| Firebase config | firebase.json, firestore.rules, storage.rules |
Emulator ports, Hosting rewrites, deploy targets, and security rules. |
CrowdPM Platform originated as an Oregon State University EECS Capstone project proposed by Jaron Rosenau / Denuo Web LLC. The project is now maintained and operated by Denuo Web LLC.
The repository includes contributions from multiple project contributors. See AUTHORS.md and the Git commit history for attribution.
Use Node 24 before running project commands:
source ~/.nvm/nvm.sh && nvm use 24
corepack enable
corepack prepare pnpm@10.18.1 --activate
pnpm installCreate local environment files:
cp frontend/.env.example frontend/.env.local
cp functions/.env.example functions/.env.localEdit frontend/.env.local with a Google Maps JavaScript API key, vector map ID, Firebase web app config, and the local API base:
VITE_API_BASE=http://127.0.0.1:5001/crowdpm-local/us-central1/crowdpmApi
VITE_FIREBASE_AUTH_EMULATOR_HOST=127.0.0.1:9099Edit functions/.env.local with a real Ed25519 PKCS8 private key for DEVICE_TOKEN_PRIVATE_KEY. The local emulator also supports generated ephemeral keys, but a stable key is required when testing device registration and repeat ingest.
Start the full local stack:
pnpm devLocal URLs:
- Frontend:
http://localhost:5173 - API health:
http://127.0.0.1:5001/crowdpm-local/us-central1/crowdpmApi/health - Firebase Emulator UI:
http://localhost:4000
pnpm dev # frontend, functions emulator, TS watch
pnpm lint # workspace ESLint
pnpm --filter crowdpm-functions test # functions Vitest suite
pnpm build # build all workspaces
pnpm --filter crowdpm-frontend build # frontend only
pnpm --filter crowdpm-functions build # functions onlyDevice emulator examples:
pnpm device:pair -- --key .device-key.json --interval 3
pnpm device:pair -- --mode ingest --key .device-key.json
pnpm device:simulate:osu -- --count 20 --minutes 36Primary Fastify routes are exported through the crowdpmApi HTTPS Function. The ingest gateway is a separate HTTPS Function because it preserves raw payloads before processing.
- Pairing:
POST /device/start,/device/token,/device/register,/device/access-token - Activation UI support:
GET /v1/device-activation,POST /v1/device-activation/authorize - User APIs:
/v1/devices,/v1/measurements,/v1/batches,/v1/user/settings - Public data:
/v1/public/batches - Admin and moderation:
/v1/admin/* - Ingest gateway:
POST /ingestGateway
Raw ingest batches are stored in Cloud Storage at ingest/{deviceId}/{batchId}.json. Processed measurements are stored in Firestore under devices/{deviceId}/measures/{hourBucket}/rows/{doc}, with batch metadata under devices/{deviceId}/batches/{batchId}.
docs/README.md- documentation index and conventionsdocs/development.md- local setup and daily workflowdocs/deployment.md- deployed Firebase release processdocs/hardware-builder.md- hardware pairing and ingest contractdocs/openapi-swagger-ui.md- local Swagger UI forfunctions/src/openapi.yamlfrontend/README.md,functions/README.md,shared-types/README.md,scripts/README.md- package-specific notes
Use docs/deployment.md for the deployed environment. Always deploy with an explicit Firebase project ID and never deploy the local fake project:
FIREBASE_PROJECT_ID=crowdpmplatform
pnpm lint
pnpm build
firebase deploy --only hosting,functions --project "$FIREBASE_PROJECT_ID"Deploy Firestore indexes and Firestore or Storage rules only when those files changed and have been reviewed.
CrowdPM Platform is licensed under GNU AGPLv3-or-later unless otherwise stated.
Copyright © 2025–2026 Denuo Web LLC and contributors.
The hosted service, hardware node sales, support, fulfillment, and related commercial services are operated by Denuo Web LLC.
Commercial licensing may be available from Denuo Web LLC for portions of the project owned by, assigned to, or otherwise licensed to Denuo Web LLC for that purpose. Contributor-owned portions remain subject to their applicable license terms unless separate written permission has been obtained.
See LICENSE.md, NOTICE.md, AUTHORS.md, CONTRIBUTING.md, and CLA.md.