Rayhunter fleet dashboard and back-end for fixed + mobile IMSI-catcher detectors
  • TypeScript 97.9%
  • JavaScript 1.1%
  • CSS 1%
Find a file
Kish Galappatti 5cd014e548 Scaffold Rayhunter fleet dashboard and back-end
Operations dashboard + device-facing API for a ~50-unit fleet (fixed
and mobile Rayhunter detectors). Built on Next.js 16 App Router with
Neon Postgres (Drizzle), Vercel Blob, and Clerk; targeted at a
self-hosted Vercel + Marketplace stack.

- Drizzle schema: devices, heartbeats, recordings, detections, gps_points
- Device API (HMAC-token auth): /api/v1/heartbeat, /api/v1/gps,
  /api/v1/register, /api/v1/recordings, /api/v1/ntfy
- Cron: /api/cron/check-heartbeats marks units degraded/offline
- Clerk-gated dashboard: device list, detail (with stats), recording
  detail, detections, fleet map, and per-device GPS trail (MapLibre)
- Provisioning helper script (scripts/enroll-device.ts) and migrate
  runner; vercel.ts config registers the heartbeat cron
- Uses Next 16 conventions: proxy.ts replaces middleware.ts, Clerk v7
  Show component replaces SignedIn/SignedOut, awaited params Promises

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 17:18:10 -04:00
public Initial commit from Create Next App 2026-06-01 16:49:01 -04:00
scripts Scaffold Rayhunter fleet dashboard and back-end 2026-06-01 17:18:10 -04:00
src Scaffold Rayhunter fleet dashboard and back-end 2026-06-01 17:18:10 -04:00
.env.example Scaffold Rayhunter fleet dashboard and back-end 2026-06-01 17:18:10 -04:00
.gitignore Scaffold Rayhunter fleet dashboard and back-end 2026-06-01 17:18:10 -04:00
AGENTS.md Initial commit from Create Next App 2026-06-01 16:49:01 -04:00
CLAUDE.md Initial commit from Create Next App 2026-06-01 16:49:01 -04:00
drizzle.config.ts Scaffold Rayhunter fleet dashboard and back-end 2026-06-01 17:18:10 -04:00
eslint.config.mjs Initial commit from Create Next App 2026-06-01 16:49:01 -04:00
next.config.ts Initial commit from Create Next App 2026-06-01 16:49:01 -04:00
package-lock.json Scaffold Rayhunter fleet dashboard and back-end 2026-06-01 17:18:10 -04:00
package.json Scaffold Rayhunter fleet dashboard and back-end 2026-06-01 17:18:10 -04:00
postcss.config.mjs Initial commit from Create Next App 2026-06-01 16:49:01 -04:00
proxy.ts Scaffold Rayhunter fleet dashboard and back-end 2026-06-01 17:18:10 -04:00
README.md Scaffold Rayhunter fleet dashboard and back-end 2026-06-01 17:18:10 -04:00
tsconfig.json Initial commit from Create Next App 2026-06-01 16:49:01 -04:00
vercel.ts Scaffold Rayhunter fleet dashboard and back-end 2026-06-01 17:18:10 -04:00

Rayhunter Fleet

Operations dashboard and back-end for a fleet of Rayhunter IMSI-catcher detectors. Built for ~25100 units, split between fixed sites and mobile (vehicle) units, with the back-end hosted on Vercel.

Architecture

Rayhunter unit ──WebDAV PUT──▶ rclone-serve VPS ──webhook──▶ /api/v1/recordings
       │
       ├──HTTP POST──▶ /api/v1/heartbeat   (HMAC device token)
       ├──HTTP POST──▶ /api/v1/gps         (mobile only)
       └──ntfy POST──▶ ntfy server ──webhook──▶ /api/v1/ntfy

Operator UI    ──▶ /dashboard (Clerk-gated)
Cron           ──▶ /api/cron/check-heartbeats (every 5 min)

The dashboard reads from Neon Postgres; recording artifacts live in Vercel Blob (private). PCAP uploads from devices target a small external WebDAV endpoint (Rayhunter has no native S3/Blob target); a sidecar process there POSTs /api/v1/recordings after each file lands and uploads the blob into Vercel Blob.

Stack

  • Next.js 16 App Router (proxy.ts replaces middleware.ts, use cache directive available)
  • Neon Postgres via Drizzle ORM
  • Vercel Blob (private) for recording artifacts
  • Clerk for operator auth
  • MapLibre / react-map-gl for GPS visualization
  • Zod for input validation

First-run setup

  1. Provision infrastructure on Vercel:

    vercel link
    vercel integration add neon            # Database
    vercel integration add clerk           # Auth
    vercel blob store create rayhunter-recordings
    
  2. Create shared secrets:

    for v in DEVICE_REGISTRATION_SECRET WEBDAV_SYNC_SECRET NTFY_WEBHOOK_SECRET CRON_SECRET; do
      vercel env add $v production <<< "$(openssl rand -hex 32)"
    done
    
  3. Pull env locally and migrate the DB:

    cp .env.example .env.local
    vercel env pull .env.local
    npm run db:generate
    npm run db:migrate
    
  4. Run the dashboard:

    npm run dev
    

Enrolling a device

DEVICE_REGISTRATION_SECRET=... FLEET_BASE_URL=https://fleet.example.com \
  npm run enroll -- \
    --asset RH-001 \
    --label "Lobby" \
    --hardware "Orbic RC400L" \
    --mode fixed \
    --lat 37.7749 --lon -122.4194 \
    --site "HQ"

The response includes deviceToken (long-lived HMAC bearer for /api/v1/*), webdav.{username,password}, and ntfy.topic. Bake those into the device's Rayhunter config during flashing.

Device-facing endpoints

Endpoint Auth Used by
POST /api/v1/register DEVICE_REGISTRATION_SECRET Provisioning script (one-shot per device)
POST /api/v1/heartbeat Device token Each unit, every 60s
POST /api/v1/gps Device token Mobile units, batched
POST /api/v1/recordings WEBDAV_SYNC_SECRET rclone-serve sidecar
POST /api/v1/ntfy NTFY_WEBHOOK_SECRET (x-ntfy-secret header) ntfy server forwarding
GET /api/cron/check-heartbeats CRON_SECRET Vercel cron (every 5 min)

Things this repo does not do (yet)

  • Push firmware updates to units — handled by Rayhunter's own update mechanism; this dashboard just records which version each unit reports.
  • Replay PCAPs in-browser — link out to wireshark/mobile-sentinel.
  • Multi-tenant — one fleet per deployment. Use route groups + an orgs table if you need that.