A centralized, self-hostable map for indi-allsky all-sky camera systems. Camera owners register their systems and periodically "ping" this server with their status and metadata, which is then displayed on an interactive Leaflet map.
- 🗺️ Interactive Leaflet map showing all registered cameras worldwide
- 🟢 Live online/offline status — cameras that haven't pinged in 24h are marked offline
- 📷 Live image thumbnails — served via a backend proxy to avoid CORS/mixed-content issues
- 🔒 Privacy-first — coordinates are fuzzed to 2 decimal places (~1 km) before being stored or served publicly
- 🔑 Hashed API keys — raw keys are never stored; only SHA-256 hashes are saved in the database
- 🛡️ Hardened backend — rate limiting, SSRF protection, payload size limits, and strict CSP headers
- 🐳 Docker-ready — ships with a full
docker-compose.ymlusing Traefik for TLS termination
indi-allsky camera host
└─ services/allsky-map-ping ──POST /api/ping──► FastAPI server ──► SQLite DB
│
GET /api/cameras
│
Leaflet frontend
git clone https://github.com/your-username/allsky-map.git
cd allsky-mapcp .env.example .envEdit .env and set your values:
DOMAIN_NAME=allsky-map.com
ACME_EMAIL=admin@example.com
TZ=Australia/Adelaidedocker compose up -dTraefik will automatically obtain a Let's Encrypt TLS certificate for your domain.
pip install -r requirements.txt
uvicorn app.main:app --reloadThe server will start at http://localhost:8000.
- Open the map in your browser and click "Register Camera".
- Click "Generate API Key" — copy and store the key securely. It is shown only once.
- Install the ping client on the machine running
indi-allsky(see below).
The services/ directory contains a systemd-based ping client that runs on the camera host.
sudo bash services/install.shThe installer will guide you through configuring your API key, camera name, coordinates, and site URL. It installs:
/usr/local/bin/allsky-map-ping— the ping script/etc/allsky-map/ping.conf— your camera's configuration (chmod 600)- A systemd timer that pings the server every 5 minutes
Generates a new API key. Returns the raw key once — store it immediately.
{ "api_key": "allsky_live_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }Updates a camera's data and marks it online. Called by the ping client.
Headers: X-API-Key: <your-api-key>
Body:
{
"name": "My Allsky Camera",
"owner": "Jane Smith",
"lat": -34.92,
"lng": 138.60,
"siteUrl": "https://my-camera-website.com",
"imageBase64": "<base64-encoded-image>"
}Returns all cameras that have pinged at least once.
[
{
"name": "My Allsky Camera",
"owner": "Jane Smith",
"lat": -34.92,
"lng": 138.6,
"siteUrl": "https://my-camera-website.com",
"imageUrl": "",
"status": "online",
"lastSeen": "2025-01-01T00:00:00+00:00"
}
]Returns the proxied live image for a camera (JPEG).
Returns an embeddable HTML status card for a camera.
The server runs two background tasks on an hourly schedule:
| Task | Behaviour |
|---|---|
| Reaper | Marks cameras as offline if they haven't pinged in over 24 hours |
| Pruner | Deletes API key registrations that were never activated (no name set) after 24 hours |
| Link checker | Validates siteUrl for all cameras; hides broken links from the public API |
- API keys are SHA-256 hashed before storage — the server never holds raw keys
- Coordinates are rounded to 2 d.p. (~1 km accuracy) before being stored or returned
- Outbound HTTP requests (image fetching) are validated against an SSRF blocklist (no private IPs, localhost, or link-local ranges)
- The Traefik reverse proxy drops the
X-API-Keyheader from access logs - Rate limiting is enforced on
/api/registerand/api/ping
GNU Affero General Public License v3.0 — see LICENSE for details.
This means: if you run a modified version of this software as a publicly accessible network service, you must make your modified source code available to your users.