The Surveillance Threat
Immigration enforcement agencies operate with massive technological budgets and are documented purchasers of:
- Location data from commercial brokers
- Utility records
- Mobile phone telemetry
- Social media data
This pipeline allows agencies to circumvent Fourth Amendment warrant requirements.
Any digital tool for immigrant communities must be engineered to prevent surveillance.
Client-Side Location Processing
Never Transmit User Location
All "find nearest" and "am I in the zone" features must execute entirely on the user's device.
Turf.js for Local Analysis
import * as turf from '@turf/turf';
// Pre-loaded zone boundary (no user data)
let borderZone;
fetch('/data/100-mile-zone.geojson')
.then(r => r.json())
.then(data => { borderZone = data; });
function checkLocation(lat, lng) {
// All processing happens locally
const userPoint = turf.point([lng, lat]);
const isInZone = turf.booleanPointInPolygon(userPoint, borderZone);
// Display result
showResult(isInZone);
// Coordinates are NEVER transmitted
// Variable can be garbage collected immediately
}
Finding Nearest Resource
function findNearest(userLat, userLng, resources) {
const userPoint = turf.point([userLng, userLat]);
let nearest = null;
let minDistance = Infinity;
resources.features.forEach(resource => {
const distance = turf.distance(
userPoint,
resource,
{ units: 'miles' }
);
if (distance < minDistance) {
minDistance = distance;
nearest = resource;
}
});
// Return only the resource info, not user location
return {
resource: nearest.properties,
distance: Math.round(minDistance * 10) / 10
};
}
Zero-Tracking Architecture
No Third-Party Analytics
Never include:
| Service | Risk |
|---|---|
| Google Analytics | IP logging, fingerprinting |
| Meta Pixel | Cross-site tracking |
| Hotjar/FullStory | Session recording |
| Commercial CDNs | Request logging |
Self-Hosted Analytics (If Needed)
| Solution | Features |
|---|---|
| Plausible | Privacy-focused, no cookies |
| Umami | Self-hosted, open source |
| Fathom | GDPR compliant |
// Plausible - no cookies, no tracking
<script defer data-domain="yoursite.org"
src="https://plausible.yoursite.org/js/plausible.js">
</script>
IP Address Handling
# nginx.conf - anonymize IPs in logs
log_format anonymized '$remote_addr_anon - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent';
map $remote_addr $remote_addr_anon {
~(?P<ip>\d+\.\d+\.\d+)\. $ip.0;
~(?P<ip>[^:]+:[^:]+): $ip::;
default 0.0.0.0;
}
access_log /var/log/nginx/access.log anonymized;
Data Minimization
Never Log What You Don't Need
// Server-side request handling
app.post('/api/report', (req, res) => {
const report = {
// DO collect
checkpoint_type: req.body.type,
general_area: req.body.city, // City only, not address
timestamp: Math.floor(Date.now() / 3600000) * 3600000, // Hour precision
// NEVER collect
// ip_address: req.ip, // NO
// user_agent: req.headers['user-agent'], // NO
// exact_coords: req.body.coords, // NO
};
saveReport(report);
});
Automated Data Purging
// Cron job to purge old data
const RETENTION_DAYS = 30;
async function purgeOldData() {
const cutoff = Date.now() - (RETENTION_DAYS * 24 * 60 * 60 * 1000);
await db.query(
'DELETE FROM reports WHERE created_at < $1',
[new Date(cutoff)]
);
await db.query(
'DELETE FROM logs WHERE timestamp < $1',
[new Date(cutoff)]
);
}
// Run daily
setInterval(purgeOldData, 24 * 60 * 60 * 1000);
Secure Communications
HTTPS Everywhere
server {
listen 80;
server_name advocacy.org;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name advocacy.org;
ssl_certificate /etc/ssl/certs/advocacy.org.pem;
ssl_certificate_key /etc/ssl/private/advocacy.org.key;
# Modern TLS only
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
}
Content Security Policy
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https://tile.openstreetmap.org;
connect-src 'self' https://nominatim.openstreetmap.org;
frame-ancestors 'none';
">
Subresource Integrity
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha384-..."
crossorigin="anonymous"></script>
Privacy-Respecting Geocoding
Self-Hosted Nominatim
For maximum privacy, host your own geocoder:
# docker-compose.yml
services:
nominatim:
image: mediagis/nominatim:4.3
volumes:
- ./data:/nominatim/data
environment:
PBF_URL: https://download.geofabrik.de/north-america/us-latest.osm.pbf
ports:
- "8080:8080"
Proxied Requests
If using external services:
// Proxy geocoding through your server
app.get('/api/geocode', async (req, res) => {
const { q } = req.query;
// Don't log the query
const result = await fetch(
`https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(q)}&format=json`
);
// Return without logging
res.json(await result.json());
});
User Communication
Privacy Notice
<div class="privacy-notice">
<h3>Your Privacy</h3>
<p>
This map processes your location <strong>entirely on your device</strong>.
Your coordinates are never sent to our servers or any third party.
</p>
<p>
We do not use cookies, tracking pixels, or analytics that identify you.
</p>
</div>
Geolocation Permission Request
function requestLocation() {
const message = `
To find resources near you, we need your location.
Your location stays on your device and is NEVER sent to our servers.
Allow location access?
`;
if (confirm(message)) {
navigator.geolocation.getCurrentPosition(
handleLocation,
handleError,
{ enableHighAccuracy: false }
);
}
}
Threat Modeling
Attack Vectors
| Vector | Mitigation |
|---|---|
| Server logs | Anonymize/purge immediately |
| Third-party scripts | Self-host all dependencies |
| CDN logging | Use privacy-respecting CDN |
| Network interception | HTTPS + HSTS |
| Browser fingerprinting | Minimal JS, no cookies |
| Legal subpoena | Don't collect data to subpoena |
Data We Cannot Protect
Even with best practices, users should understand:
- ISP can see domain accessed (not specific pages with HTTPS)
- Device may have malware
- Screenshots/screen recording
- Shoulder surfing
Incident Response
If Served With Legal Process
- Consult legal counsel immediately
- Preserve (don't delete) any relevant data
- Review scope carefully - don't over-produce
- Assert applicable privileges
- Notify users if legally permitted
What We'd Have to Produce
With this architecture, a subpoena would yield:
| Request | Our Response |
|---|---|
| "User location data" | "We don't collect this" |
| "IP addresses" | "Anonymized at collection" |
| "User identities" | "No accounts or login" |
| "Access logs" | "Purged after 24 hours" |
Related Resources
- Interactive Features - Client-side patterns
- Implementation - Secure deployment
- Digital Coalition - Security - Org security
- Communication Security - Encrypted comms