Community-Powered Intelligence
Community defense networks rely on crowdsourcing to identify:
- Sudden workplace raids
- Temporary "roving" checkpoints
- ICE vehicle sightings
- Enforcement activity patterns
Aggregating, verifying, and acting on this data involves significant technical and ethical complexity.
Report Collection Architecture
Data Points to Collect
| Field | Purpose | Privacy Note |
|---|---|---|
| Type | Checkpoint, raid, vehicle | Required |
| General area | City/neighborhood | Not exact address |
| Time | Hour precision | Not exact minute |
| Description | Activity observed | Free text |
| Duration | Still active? | Time decay |
Data NOT to Collect
| Field | Risk |
|---|---|
| ❌ Reporter identity | Retaliation |
| ❌ Exact GPS coords | Location tracking |
| ❌ IP address | Identification |
| ❌ Device fingerprint | Tracking |
| ❌ Photos of individuals | Privacy/safety |
Anonymous Submission
Frontend Form
<form id="report-form" action="/api/report" method="POST">
<select name="type" required>
<option value="">Select type...</option>
<option value="checkpoint">Checkpoint</option>
<option value="raid">Raid/Operation</option>
<option value="vehicle">ICE Vehicle</option>
<option value="patrol">Roving Patrol</option>
</select>
<input type="text" name="area"
placeholder="City or neighborhood"
required>
<select name="highway" id="highway-select">
<option value="">Highway (if applicable)</option>
<option value="I-8">I-8</option>
<option value="I-10">I-10</option>
<!-- etc -->
</select>
<textarea name="description"
placeholder="What did you observe?"></textarea>
<label>
<input type="checkbox" name="still_active">
Still active when reporting
</label>
<button type="submit">Submit Report</button>
</form>
Backend Processing
app.post('/api/report', async (req, res) => {
const report = {
id: generateUUID(),
type: sanitize(req.body.type),
area: sanitize(req.body.area),
highway: sanitize(req.body.highway),
description: sanitize(req.body.description),
still_active: req.body.still_active === 'on',
// Coarse timestamp (hour precision)
timestamp: Math.floor(Date.now() / 3600000) * 3600000,
// Initial status
status: 'unverified',
confidence: 0.3
};
// DO NOT LOG: req.ip, req.headers
await saveReport(report);
// Trigger verification workflow
await queueForVerification(report);
res.json({ success: true, id: report.id });
});
Verification Workflows
Multi-Source Corroboration
async function verifyReport(report) {
const nearbyReports = await findReportsNear(
report.area,
{ radius: 5, timeWindow: '2h' }
);
// Bayesian confidence update
let confidence = report.confidence;
nearbyReports.forEach(other => {
if (other.id !== report.id) {
// Corroborating reports increase confidence
const similarity = calculateSimilarity(report, other);
confidence = bayesianUpdate(confidence, similarity);
}
});
// Check against known patterns
const historicalMatch = await matchHistoricalPattern(report);
if (historicalMatch) {
confidence = bayesianUpdate(confidence, 0.8);
}
return confidence;
}
Confidence Levels
| Level | Confidence | Display |
|---|---|---|
| Unverified | < 0.3 | Gray, small |
| Possible | 0.3 - 0.6 | Yellow, medium |
| Likely | 0.6 - 0.8 | Orange, full size |
| Confirmed | > 0.8 | Red, prominent |
Human Review Queue
// Flag reports for human review
if (confidence < 0.3 && description.length > 200) {
await flagForReview(report, 'Long description, low confidence');
}
if (isAnomaly(report)) {
await flagForReview(report, 'Anomalous location/time');
}
Time Decay
The Problem
Enforcement actions are highly temporal. A checkpoint reported 3 hours ago may no longer exist.
Visual Decay Implementation
function getReportStyle(report) {
const ageHours = (Date.now() - report.timestamp) / (1000 * 60 * 60);
// Reports fade over time
const opacity = Math.max(0.2, 1 - (ageHours / 24));
const scale = Math.max(0.5, 1 - (ageHours / 48));
return {
opacity,
scale,
label: getAgeLabel(ageHours)
};
}
function getAgeLabel(hours) {
if (hours < 1) return 'Just reported';
if (hours < 2) return '1-2 hours ago';
if (hours < 6) return 'Few hours ago';
if (hours < 24) return 'Earlier today';
return 'Yesterday or older';
}
CSS Animation
.report-marker {
transition: opacity 0.3s, transform 0.3s;
}
.report-marker.fading {
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 0.6; }
50% { opacity: 1; }
}
Spam and Abuse Prevention
Rate Limiting
const rateLimit = require('express-rate-limit');
const reportLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 reports per window
message: 'Too many reports. Please try again later.',
// Don't log IPs
keyGenerator: (req) => hashIP(req.ip)
});
app.post('/api/report', reportLimiter, handleReport);
Anomaly Detection
function isAnomaly(report) {
// Check if location has any enforcement history
const historicalActivity = await getHistoricalActivity(report.area);
if (historicalActivity.count === 0) {
return true; // Unusual location
}
// Check if time is unusual for this location
const typicalTimes = historicalActivity.typicalHours;
const reportHour = new Date(report.timestamp).getHours();
if (!typicalTimes.includes(reportHour)) {
return true; // Unusual time
}
return false;
}
Content Moderation
const BLOCKED_PATTERNS = [
/personal\s+information/i,
/license\s+plate/i,
/name\s+of/i,
/photo\s+of\s+person/i
];
function moderateContent(description) {
for (const pattern of BLOCKED_PATTERNS) {
if (pattern.test(description)) {
return {
allowed: false,
reason: 'Content may contain personal information'
};
}
}
return { allowed: true };
}
Legal Framework
What You CAN Report
- Public observation of enforcement activity
- Checkpoint locations on public roads
- General descriptions of activity
- Time and general area
What You CANNOT Include
| Content | Risk |
|---|---|
| Photos of individuals | Privacy violation |
| Names of officers | Potential harassment |
| License plates | Privacy violation |
| Tactics to evade | Potentially illegal |
| Instructions to interfere | Federal charges |
Terms of Service
## User Agreement
By submitting a report, you agree:
1. Your report is based on personal observation
2. You will not include personal identifying information
3. You will not provide instructions for evading enforcement
4. You understand reports are for informational purposes only
5. [Organization] is not liable for report accuracy
Rapid Response Integration
Alert Triggers
async function processVerifiedReport(report) {
if (report.confidence > 0.7 && report.still_active) {
// Trigger rapid response notification
await notifyRapidResponse({
type: report.type,
area: report.area,
description: report.description,
link: `https://map.org/reports/${report.id}`
});
}
}
Notification Channels
async function notifyRapidResponse(alert) {
// Signal group
await signalBot.send(RAPID_RESPONSE_GROUP, formatAlert(alert));
// Telegram channel
await telegramBot.send(ALERT_CHANNEL, formatAlert(alert));
// SMS (if critical)
if (alert.type === 'raid') {
await smsService.broadcast(COORDINATORS, formatSMS(alert));
}
}
Existing Platforms
Ushahidi
Battle-tested crisis mapping platform:
- Anonymous submission
- Verification workflows
- Map visualization
- API access
Recommended for organizations without dev resources.
Custom Implementation
For specific needs:
| Approach | Effort | Flexibility |
|---|---|---|
| Ushahidi | Low | Medium |
| Modified OSM | Medium | Medium |
| Custom build | High | Full |
Data Retention
Aggressive Purging
// Daily cleanup job
async function cleanupOldReports() {
const cutoffs = {
unverified: 24 * 60 * 60 * 1000, // 24 hours
verified: 7 * 24 * 60 * 60 * 1000, // 7 days
archived: 30 * 24 * 60 * 60 * 1000 // 30 days
};
for (const [status, maxAge] of Object.entries(cutoffs)) {
const cutoff = Date.now() - maxAge;
await db.query(
'DELETE FROM reports WHERE status = $1 AND timestamp < $2',
[status, new Date(cutoff)]
);
}
}
What to Preserve
For historical analysis, retain only:
- Aggregated statistics
- Geographic patterns
- Time trends
- No individual reports after 30 days
Related Resources
- Privacy Features - Protecting reporters
- Rapid Response Networks - Alert coordination
- Communication Security - Secure channels
- Legal Observer - Documentation protocols