Roadmap & Future Features
This page tracks planned enhancements, accepted feature requests, and future capabilities for CivicPulse.
π Planned β Next Sprintβ
Camera Capture with GPS Injectionβ
Epic: Media
Priority: P1
Allow users to take a photo directly within the app (no gallery needed). GPS coordinates are captured at the exact moment of shutter-press β more accurate than EXIF metadata from the camera hardware.
Approach:
- Custom camera UI using
navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } }) - Live GPS overlay on viewfinder via
geolocation.watchPosition() - GPS injected as
capturedGeoon the image object at capture time - Falls back to
<input type="file" capture="environment">on older browsers
Content Moderation β Phase 1 (Client-side NSFWJS)β
Epic: Safety
Priority: P0
Pre-upload image classification in the browser using TensorFlow.js + NSFWJS model (~7MB). Rejects images classified as Porn (>70%) or Sexy (>85%) before they ever leave the device.
Categories checked: Porn, Sexy, Hentai, Neutral, Drawing
Pros: Zero cost, zero latency, works offline, image never hits server
Cons: 7MB model on first load, can be fooled by edge cases
Content Moderation β Phase 2 (AWS Rekognition)β
Epic: Safety
Priority: P1
Backend moderation gate before S3 save. Every uploaded image passes through Rekognition.detectModerationLabels({ MinConfidence: 75 }). Returns structured labels with confidence scores.
Categories blocked: Explicit Nudity, Suggestive (high confidence), Rude Gestures, Drugs
Categories allowed with review: Graphic Violence (valid civic evidence), Visually Disturbing
Cost: ~$0.001 per image
Image Flagging β Phase 2 (Backend Persistence)β
Epic: Safety / Moderation
Priority: P1
Persist image flags to DynamoDB. Auto-blur after 2 flags. Admin moderation queue with Approve / Remove / Ban actions.
DynamoDB table: civicpulse-media-flags
PK: imageId
flags: [{ userId, reason, createdAt }]
flagCount: number
status: visible | blurred | removed
Issue Flagging β Phase 2 (Backend Persistence)β
Epic: Safety / Moderation
Priority: P1
Persist issue flags to DynamoDB. Auto-hide after 5 flags. Separate from image flagging. Admin queue shows full issue context for review.
Face & Licence Plate Blurringβ
Epic: Privacy
Priority: P2
Automatically detect and blur faces and licence plates in uploaded civic images before storage.
- AWS Rekognition
DetectFacesβ blur bounding boxes - Rekognition
DetectTextβ detect and blur plate patterns - Applied as a post-upload async Lambda job
π§ͺ Research Phaseβ
Duplicate Issue Detection (Geo + Image Hash + Text)β
Epic: Core
Priority: P2
Status: Parked β design complete, implementation deferred
The Problemβ
Users frequently report the same physical issue from different angles, with different wording, or at slightly different times. This creates noise in the dataset and dilutes upvote signal.
Multi-Signal Scoringβ
The system uses three independent signals combined into a weighted duplicate score:
duplicateScore =
geoProximityScore Γ 0.55 // strongest β same physical location
+ imageHashScore Γ 0.30 // medium β same photo or same scene
+ titleSimilarity Γ 0.15 // weakest β same wording
| Score | Action shown to user |
|---|---|
| β₯ 0.85 | "This issue has almost certainly been reported β upvote instead?" |
| 0.60β0.85 | "Very likely the same issue" |
| 0.35β0.60 | "Possibly related" |
| < 0.35 | No duplicate warning shown |
Signal 1 β Geo Proximity (Haversine Distance)β
When GPS is captured on the new issue, compute Haversine distance against all existing issues of the same category.
function haversineMetres(lat1, lng1, lat2, lng2) {
const R = 6371000;
const Ο1 = lat1 * Math.PI/180, Ο2 = lat2 * Math.PI/180;
const ΞΟ = (lat2-lat1) * Math.PI/180;
const ΞΞ» = (lng2-lng1) * Math.PI/180;
const a = Math.sin(ΞΟ/2)**2 + Math.cos(Ο1)*Math.cos(Ο2)*Math.sin(ΞΞ»/2)**2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
}
Configurable radius by category:
| Category | Radius |
|---|---|
| Road (pothole) | 50m |
| Traffic signal | 20m |
| Streetlight / Infrastructure | 30m |
| Waterlogging | 300m |
| Hygiene / Garbage | 100m |
| Healthcare / Systemic | N/A |
Signal 2 β Perceptual Image Hash (dHash)β
Compute a 64-bit perceptual hash of each uploaded image using the browser Canvas API (~5ms, no library needed). Compare Hamming distance against hashes of existing nearby issues.
Hamming distance 0β5 β Virtually identical (same photo)
Hamming distance 6β10 β Same scene, slight crop/resize
Hamming distance 11β20 β Similar location, possibly same incident
Hamming distance 20+ β Different image
The dHash is a dual-purpose computation β the same hash is used for both duplicate detection and the image blocklist (flagged/removed images are added to the blocklist, future uploads with Hamming < 8 are auto-rejected).
Signal 3 β Token-based Title Similarity (Jaccard)β
function jaccardSim(a, b) {
const ta = new Set(a.toLowerCase().split(/\s+/).filter(w => w.length > 3));
const tb = new Set(b.toLowerCase().split(/\s+/).filter(w => w.length > 3));
const intersection = [...ta].filter(w => tb.has(w)).length;
const union = new Set([...ta, ...tb]).size;
return union === 0 ? 0 : intersection / union;
}
UX β Duplicate Panel in New Issue Modalβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β οΈ Similar issues found near you (Road Β· Mumbai) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β Pothole causing accidents near Bandra 89 β² β
β 48m away Β· In Progress β
β [π 48m] [πΌ Image: 94%] [Aa Text: 61%] β
β [View Issue] [β Upvote instead] β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β [My issue is different β] β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Implementation Planβ
| Phase | What | Where |
|---|---|---|
| Phase 1 | Jaccard title match in NewModal, no backend | Frontend |
| Phase 2 | Geo proximity check when GPS captured | Frontend + existing geo data |
| Phase 3 | dHash on upload + in-memory hash comparison | Frontend |
| Phase 4 | Backend /api/issues/similar with TF-IDF | Backend |
| Phase 5 | Semantic embeddings (OpenAI / Bedrock) | Backend |
Backend Endpoint (Phase 4)β
POST /api/issues/similar
{
"title": "Deep crater on SV Road",
"city": "Mumbai",
"category": "road",
"geo": { "lat": 19.054, "lng": 72.842 }
}
β Response:
[
{ "id": "ISS-003", "title": "Pothole causing accidents near Bandra",
"score": 0.82, "distance": 48, "imageMatch": 0.94 },
{ "id": "ISS-001", "title": "Waterlogging near Andheri Station",
"score": 0.21, "distance": 4200 }
]
π± Mobile App β React Nativeβ
Epic: Mobile Priority: P1 Status: Planned
Overviewβ
A cross-platform iOS + Android app that brings CivicPulse to mobile-first users. The app shares all business logic and API contracts with the web platform β no separate backend needed.
Tech Stackβ
| Layer | Technology |
|---|---|
| Framework | React Native + Expo SDK 51 |
| Navigation | Expo Router (file-based, similar to Next.js) |
| State | Same Zustand/Context pattern as web |
| API | Shared /api/* endpoints (same backend) |
| Auth | Google Sign-In via expo-auth-session |
| Camera | expo-camera β native viewfinder with GPS overlay |
| Push | expo-notifications + existing VAPID backend |
| Offline | expo-sqlite for local issue cache |
| Maps | react-native-maps for issue heatmap |
| Distribution | Expo Application Services (EAS) β App Store + Play Store |
Key Features (Mobile-first)β
- Native Camera β shoot from within the app, GPS injected at capture time
- Push Notifications β native OS notifications for issue updates and community replies
- Offline Mode β browse and draft reports without connectivity; auto-sync when back online
- Civic Score Widget β iOS/Android home screen widget showing your contribution score
- Nearby Issues Map β native map view showing issues clustered near the user
- Haptic Feedback β upvote, flag, submit actions use vibration feedback
- Biometric Auth β Face ID / Fingerprint login for returning users
Project Structureβ
mobile/
βββ app/ # Expo Router file-based routes
β βββ (tabs)/
β β βββ index.tsx # Dashboard (same data as web)
β β βββ issues.tsx # Issues list with Near Me sort
β β βββ report.tsx # Report new issue (camera + form)
β β βββ profile.tsx # Civic score + settings
β βββ issue/[id].tsx # Issue detail with chat
β βββ _layout.tsx # Root layout + auth guard
βββ components/ # Shared UI components
βββ hooks/ # Shared hooks (useUserLocation, useFlags, etc.)
βββ api/ # API client (same endpoints as web)
βββ app.json # Expo config
Shared Code Strategyβ
Business logic in frontend/src/lib/ (pure JS/TS, no React imports) can be consumed by both web and mobile:
haversineMetres()β already portablefmtDistance()β already portable- API client functions
- Scoring algorithm
Implementation Phasesβ
| Phase | Milestone |
|---|---|
| Phase 1 | Expo scaffold + auth + issue list + issue detail (read-only) |
| Phase 2 | Report new issue (form + camera + GPS + upload) |
| Phase 3 | Push notifications + offline mode |
| Phase 4 | Heatmap + nearby issues map |
| Phase 5 | Civic score widget + biometric auth |
| Phase 6 | App Store + Play Store submission |
π² Growth & Marketingβ
Social Media Validation Kitβ
Epic: Growth Priority: P1 Status: TODO
Auto-generate shareable issue cards and viral mechanics so every report becomes a distribution channel.
Features:
- Open Graph images: auto-generated per issue using Satori β title + category icon + upvote count + city + status badge. No headless Chrome.
- One-click share: X/Twitter card, WhatsApp deep link, Instagram Stories image
- Dynamic routes:
GET /api/issues/:id/og-imagereturns PNG (cached 10min) - UTM tracking: every shared URL carries
?utm_source=civicpulse&utm_medium=share&utm_campaign=issuefor campaign attribution
Backend: backend/services/shareService.js β Satori rendering + S3 cache
Frontend: share sheet component with platform icons
Marketing Integrationsβ
Epic: Growth Priority: P2 Status: TODO
Instrument the funnel from landing β sign-in β first report β returning user.
| Tool | Purpose | Where |
|---|---|---|
| Mailchimp / Loops.so | Waitlist email capture on landing page | Landing CTA |
| PostHog | Product analytics alongside GA4 β funnel, retention, session recording | frontend/src/analytics.js |
| Hotjar | Heatmaps on issue list + landing page | Loaded async |
| UTM params | Track campaign effectiveness for every inbound link | App.tsx on mount |
Privacy policy must be updated before enabling Hotjar session recording.
Referral & Viral Loopβ
Epic: Growth Priority: P2 Status: TODO
Turn every reporter into a distribution channel.
- Refer-a-friend link: unique ref code tied to civic score
- City leaderboard badges: shareable PNG showing your rank β "#3 in Mumbai ποΈ"
- Social proof on issue detail: "47 people in Mumbai upvoted this"
- WhatsApp forward: pre-formatted message with issue URL + summary
Depends on: Social Media Validation Kit (row 65)
π‘ Backlogβ
| Feature | Epic | Priority | Notes |
|---|---|---|---|
| PhotoDNA / NCMEC hash matching | Safety | P0 (compliance) | Required if platform scales, for CSAM detection |
| Bulk issue import from CSV | Admin | P2 | For seeding city-specific data |
| Issue status API webhooks | Core | P2 | Notify municipal systems on status change |
| Multi-language support (Hindi, Marathi, Tamil) | UX | P2 | i18n with react-intl |
| Offline support (PWA) | UX | P2 | Service worker caching for offline browsing |
| Municipal authority dashboard | Admin | P1 | Separate portal for government stakeholders |
| Issue assignment workflow | Core | P1 | Assign to specific dept, track SLA |
| SMS notifications | Notifications | P2 | Twilio for non-smartphone users |
| Heatmap view | Discovery | P2 | City-level issue density map |
| Civic score API (public) | Scoring | P2 | Open API for third-party civic apps |
| Mobile App (React Native + Expo) | Mobile | P1 | See Mobile App section above for full spec |