{"id":43067857,"url":"https://github.com/kpax2049/loramapr","last_synced_at":"2026-04-08T23:01:54.915Z","repository":{"id":332191493,"uuid":"1132395561","full_name":"kpax2049/loramapr","owner":"kpax2049","description":"LoRaMapr is a hardware-backed mapping and visualization project focused on collecting, storing, and displaying LoRa-based field data on an interactive map.","archived":false,"fork":false,"pushed_at":"2026-04-08T12:15:35.000Z","size":23974,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-08T14:24:03.856Z","etag":null,"topics":["fieldwork","lora","lorawan","map","mesh-networks","meshtastic","tracking"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kpax2049.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-11T22:00:18.000Z","updated_at":"2026-04-08T12:15:38.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kpax2049/loramapr","commit_stats":null,"previous_names":["kpax2049/loramapr"],"tags_count":41,"template":false,"template_full_name":null,"purl":"pkg:github/kpax2049/loramapr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpax2049%2Floramapr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpax2049%2Floramapr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpax2049%2Floramapr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpax2049%2Floramapr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kpax2049","download_url":"https://codeload.github.com/kpax2049/loramapr/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpax2049%2Floramapr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31577448,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fieldwork","lora","lorawan","map","mesh-networks","meshtastic","tracking"],"created_at":"2026-01-31T13:12:10.176Z","updated_at":"2026-04-08T23:01:54.909Z","avatar_url":"https://github.com/kpax2049.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LoRaMapr\n\n\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"frontend/src/assets/branding/loramapr-logo-dark.png\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"frontend/src/assets/branding/loramapr-logo-light.png\"\u003e\n    \u003cimg alt=\"LoRaMapr\" src=\"frontend/src/assets/branding/loramapr-logo-dark.png\" width=\"520\"\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n\u003e ✅ **Stable release (v1.2.0)**  \n\u003e LoRaMapr has reached **v1.2.0** and is ready for self-hosted use.  \n\u003e Development continues for new features and refinements, with changes tracked through normal release/version notes.\n\nLoRaMapr is an app for mapping real-world coverage around a fixed location with Meshtastic devices.\n\nPosition one node at that location, use a second field node to explore the surrounding area, and build coverage maps from real packet data.\n\nBy recording the same route multiple times, you can compare antennas, placement, and other setup changes under real-world conditions.\n\n### Who it is for\n\n- Meshtastic users testing coverage from a home, base, or relay location\n- People comparing antenna placement or node setup\n- Users who want real measured coverage, not just node positions on a map\n\n## What's in v1.2.0\n\n- Meshtastic-first ingest path via **Pi Forwarder**, plus supported **LoRaWAN** ingest via TTS webhooks.\n- Session-first workflow for manual capture and **Home Auto Session (HAS)** support for hands-free Meshtastic coverage runs.\n- Dedicated **Session Comparison** workflow for comparing repeated runs side by side with shared map overlays.\n- Decision-oriented compare metrics including **Max range**, **Edge RSSI**, **Edge SNR**, and farthest-point map markers.\n- Coverage workflow with **Bins + Heatmap** visualization and **Device vs Session** scope switching.\n- Production-ready self-hosting baseline with Docker Compose, reverse proxy, health/readiness checks, and startup migration flow.\n- Built-in operational tooling for API keys, database backup/restore, and retention safety defaults.\n- Expanded project documentation in the GitHub Wiki for quickstart, deployment, ingestion, troubleshooting, and operations.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"LoRaMapr session details and signal charts\" src=\"docs/assets/ui-session-details-signal.png?v=20260225\" width=\"980\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"LoRaMapr coverage mode view\" src=\"docs/assets/ui-coverage-map.png\" width=\"980\"\u003e\n\u003c/p\u003e\n\n## How coverage mapping works\n\nBest fit: fixed base + mobile field testing.\n\nMost commonly, that means a home node and a field node:\n\n1. Leave one node at home, at a relay site, or at another fixed base location.\n2. Carry another node through the field on a walk or drive.\n3. Let your receiver path send packets into LoRaMapr.\n4. Review sessions, playback, coverage views, and exports to see where packets were actually received.\n\nRun capture can be manual, or automated with Home Auto Session (HAS) for base-driven workflows.\n\n`Home node + field node` is the default pattern, but not the only valid setup.\n\n## Prerequisites (standard Meshtastic coverage workflow)\n\nFor the standard fixed-base + mobile-field workflow, LoRaMapr requires:\n\n- one node at a fixed location such as home/base/relay\n- one field node used while walking or driving\n- a Linux machine or Raspberry Pi at the fixed location to run the Receiver service (`Pi Forwarder`)\n- network access from that Receiver host to your LoRaMapr Cloud endpoint (or self-hosted LoRaMapr API endpoint)\n\nThe Receiver service ingests Meshtastic data from the fixed-location node and forwards it to LoRaMapr Cloud, where sessions, playback, and coverage maps are generated.\n\n### Receiver host baseline\n\nMinimum:\n\n- CPU: `1 vCPU`\n- RAM: `512 MB`\n- Free disk: `2 GB`\n- Network: stable LAN/internet path to your cloud endpoint\n- USB: one reliable USB data connection for the Meshtastic node (if using direct USB)\n\nRecommended:\n\n- CPU: `2 vCPU`\n- RAM: `1 GB`\n- Free disk: `4 GB` or more\n- stable power, networking, and storage for long-running receiver use\n\nRaspberry Pi baseline:\n\n- Minimum proven: Raspberry Pi Zero 2 W with `512 MB` RAM\n- Recommended: Raspberry Pi 3 or 4 class system with a quality power supply and SD card\n\nPractical note:\n\n- A minimal Linux VM can run the Receiver service in roughly the same class as a Pi Zero 2 W.\n- For better stability and upgrade/log/retry headroom, `1 GB RAM` and `2 vCPU` is the better baseline when available.\n\n## How data gets into LoRaMapr (supported ingest paths)\n\n### 1) Meshtastic (Forwarder -\u003e HTTP) — primary coverage-mapping workflow\n\n\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"docs/assets/readme/LoRaMapr_Meshtastic_ingest_diagram_dark.png\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"docs/assets/readme/LoRaMapr_Meshtastic_ingest_diagram_light.png\"\u003e\n    \u003cimg alt=\"LoRaMapr Meshtastic ingestion flow\" src=\"docs/assets/readme/LoRaMapr_Meshtastic_ingest_diagram_light.png\" width=\"980\"\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n**Real-world setup (typical)**\n\n- **You own**: one or more Meshtastic field nodes you carry while walking/driving\n- **You own**: a fixed receiver node (often at home) plus a small computer (often a Raspberry Pi)\n- Field node(s) and base node commonly run on a private channel for repeatable testing\n- **LoRaMapr**: your self-hosted backend + UI\n\n**How ingestion works**\n\n1. Field node(s) transmit packets into the mesh.\n2. Your fixed base node hears them.\n3. The **Receiver service** (**Pi Forwarder**) listens to Meshtastic packets locally and **POSTs them to LoRaMapr** over HTTP/HTTPS.\n4. LoRaMapr stores the events and normalizes GPS/radio fields into measurements attached to sessions.\n\nHome Auto Session (HAS) supports a home-driven coverage workflow: leave one node at your base location, carry another through the field, and let the base-side workflow automatically open and close coverage runs around real activity. This reduces manual session handling and makes repeated walks or drives easier to capture consistently.\n\nImportant: Meshtastic is **not limited to a home node**. The Receiver service can run on any Linux host that can read Meshtastic packets (Pi, mini PC, Linux laptop over USB, etc.).\n\n### 2) LoRaWAN (The Things Stack webhook) — supported secondary path\n\n\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"docs/assets/readme/LoRaMapr_LoRaWAN_ingest_diagram_dark.png\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"docs/assets/readme/LoRaMapr_LoRaWAN_ingest_diagram_light.png\"\u003e\n    \u003cimg alt=\"LoRaMapr LoRaWAN ingestion flow\" src=\"docs/assets/readme/LoRaMapr_LoRaWAN_ingest_diagram_light.png\" width=\"980\"\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n**Real-world setup**\n\n- **You own**: a LoRaWAN end device (your sensor/tracker)\n- **Gateways**: can be yours or community/public gateways (any gateway that hears your device helps)\n- **The Things Stack (TTS/TTN)**: the network server that receives gateway traffic for your application\n- **LoRaMapr**: your backend + UI\n\n**How ingestion works**\n\n1. Your device transmits an uplink over LoRa.\n2. One or more gateways receive it and forward it to The Things Stack.\n3. You configure a **Webhook integration** in The Things Stack by entering LoRaMapr's **HTTPS URL** (and a secret).\n4. The Things Stack automatically **POSTs each uplink event** to LoRaMapr.\n5. LoRaMapr stores the event, extracts GPS + radio metadata (RSSI/SNR, gateway IDs when available), and attaches the data to your sessions for visualization.\n\n## What users typically do with it\n\n- Record a walk/drive coverage run and replay it later.\n- Compare antennas, node placement, terrain, routes, and settings by repeating the same route over time.\n- Use Coverage **Bins** and **Heatmap** views to see where packets were actually received.\n- Use **Home Auto Session (HAS)** to capture repeated field runs with less manual session management.\n- Inspect reception details (especially strong with LoRaWAN where gateways report RSSI/SNR).\n- Export session tracks/points (GeoJSON) for external tools like QGIS.\n\n## Tech stack\n\n- Backend: Node.js + TypeScript + NestJS\n- Frontend: React + Vite + TypeScript\n- Data: PostgreSQL + Prisma\n- Supporting libs: RxJS, class-validator, class-transformer\n\n## Documentation\n\n- GitHub Wiki: https://github.com/kpax2049/loramapr/wiki\n\n## Quickstart (first-time users, working UI)\n\nStart the dev stack (postgres + backend + frontend):\n```bash\nmake keys\nmake up\n```\n\nNo manual `npm install` is required for runtime; containers install and run dependencies.\n\nDefault URLs/ports after startup:\n\n- Frontend UI: `http://localhost:5173`\n- Backend API: `http://localhost:3000`\n- Health: `http://localhost:3000/health`\n- Readiness: `http://localhost:3000/readyz`\n\nThese values are controlled by `.env` (`FRONTEND_PORT`, `API_PORT`).\n\n## What to expect\n\n- Backend listens on `http://localhost:3000`\n- Frontend listens on `http://localhost:5173`\n- Postgres runs as the `postgres` service\n- Migrations are applied automatically in the Docker backend flow (`docker compose up --build`)\n\n## Health check\n\n```bash\ncurl http://localhost:3000/health\ncurl http://localhost:3000/readyz\n```\n\n- `/health`: process-level liveness\n- `/readyz`: DB readiness (`503` when database is unreachable)\n\n## Running locally (contributors)\n\n```bash\nnpm install\ncp .env.example .env\ndocker compose up -d postgres\n# IMPORTANT: when backend runs on host (not in docker), edit .env and set:\n# DATABASE_URL=postgres://postgres:postgres@localhost:5432/loramapr\nnpm run db:migrate\nnpm run start:dev\n```\n\n## Full-stack dev (backend + frontend)\n\nRun both servers together:\n```bash\nnpm run dev:all\n```\n\nOr run them separately:\n```bash\nnpm run start:dev\nnpm --prefix frontend run dev\n```\n\n## See data in the map\n\n1) Run the simulator to ingest sample points:\n```bash\nnpm run simulate:walk -- --apiKey YOUR_KEY --deviceUid dev-1 --baseLat 37.77 --baseLon -122.43 --minutes 15 --intervalSec 5 --seed demo\n```\n2) Open the frontend dev server in your browser.\n3) Select the device in the dropdown to see points and track.\n\n### Meshtastic ingest (MVP)\n\nPost Meshtastic JSON payloads to:\n```bash\nPOST /api/meshtastic/event\n```\nUse an `X-API-Key` with `INGEST` scope. Meshtastic events create webhook events, and if GPS data is present, measurements will appear in the map.\n\n### Debug panels (QUERY key)\n\nThe LoRaWAN and Meshtastic debug panels require `VITE_QUERY_API_KEY` (QUERY scope) in `frontend/.env`.\n\n### Playback\n\nSession playback mode supports scrubber, keyboard shortcuts, and time-window slicing for deterministic replay.\n\n## Docker dev workflow (backend)\n\nUse the Quickstart above for the recommended flow.\n\n## API key generation\n\nFor local Docker-first setup, generate (or preserve existing) QUERY/INGEST keys:\n```bash\nmake keys\n```\n\nAdvanced/manual minting is also available:\n```bash\nnpm run apikey:mint -- --scopes INGEST --label \"dev ingest key\"\n```\n\nUse the printed key in the `X-API-Key` header.\n\n## Simulate measurement walk\n\nGenerate and ingest a synthetic walk (posts to `POST /api/measurements` in batches):\n```bash\nnpm run simulate:walk -- --apiKey YOUR_KEY --deviceUid dev-1 --baseLat 37.77 --baseLon -122.43 --minutes 15 --intervalSec 5 --seed demo\n```\n\n## Seed richer demo data (DB)\n\nUse the seed script when you want more than a single walk. It writes a larger test dataset directly to Postgres, including:\n\n- multiple devices and sessions\n- many measurements across several days\n- per-gateway Rx metadata\n- precomputed coverage bins\n\nRun:\n\n```bash\nnpx ts-node scripts/seed-data.ts --db\n```\n\nOptional controls:\n\n```bash\nSEED=1337 CENTER_LAT=37.7749 CENTER_LON=-122.4194 OWNER_USER_ID=\u003cuuid\u003e npx ts-node scripts/seed-data.ts --db\n```\n\nIf you only want the generated payload (no DB writes):\n\n```bash\nnpx ts-node scripts/seed-data.ts --json \u003e tmp/dummy.json\n```\n\n## Build and run\n\n```bash\nnpm run build\nnpm start\n```\n\n## Troubleshooting\n\n```bash\ndocker compose logs postgres --tail=200\ndocker compose logs backend --tail=200\ndocker compose down -v\ndocker compose up --build\n```\n\nProd compose equivalents:\n```bash\ndocker compose -f docker-compose.prod.yml logs postgres --tail=200\ndocker compose -f docker-compose.prod.yml logs api --tail=200\ndocker compose -f docker-compose.prod.yml logs reverse-proxy --tail=200\ndocker compose -f docker-compose.prod.yml down\ndocker compose -f docker-compose.prod.yml up -d --build\n```\n\nIf you see a Prisma engine mismatch (darwin vs linux), run:\n```bash\ndocker compose down -v\ndocker compose up --build\n```\n\nIf `npm ci` fails, ensure you are using the committed `package-lock.json` and rebuild.\n\nCommon ports:\n- Backend: 3000\n- Frontend dev server: 5173\n- Postgres: 5432\n\nIf API requests fail in dev, check that `frontend/.env` has `VITE_API_BASE_URL=http://localhost:3000` and restart the Vite dev server. In production, frontend requests use same-origin `/api/*` by default (leave `VITE_API_BASE_URL` empty).\n\n## Production smoke test\n\nStart production-style stack:\n```bash\nmake prod-up\n```\n\nIf your `docker-compose.prod.yml` maps proxy to default ports:\n```bash\ncurl -i http://localhost/healthz\ncurl -i http://localhost/readyz\n```\n\nIf proxy is mapped to custom host port (for example `8080:80`), use that port:\n```bash\nPORT=8080\ncurl -i \"http://localhost:${PORT}/healthz\"\ncurl -i \"http://localhost:${PORT}/readyz\"\n```\n\n## License\n\nLicense: AGPL-3.0\n\nThis project is licensed under the GNU Affero General Public License v3.0. See `LICENSE`.\n\n## Contributor note\n\n- Use `prisma migrate dev` only when changing schema; otherwise use `prisma migrate deploy` (the default in Docker).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkpax2049%2Floramapr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkpax2049%2Floramapr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkpax2049%2Floramapr/lists"}