{"id":50678743,"url":"https://github.com/n9bc/dxspider-docker","last_synced_at":"2026-06-08T17:03:33.175Z","repository":{"id":358521844,"uuid":"1240924495","full_name":"n9bc/dxspider-docker","owner":"n9bc","description":"Self-hosted amateur-radio DX cluster: DXSpider engine + Python FastAPI stats dashboard + Postgres + Caddy, as a Docker Compose stack","archived":false,"fork":false,"pushed_at":"2026-05-17T18:40:33.000Z","size":184,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-17T20:47:24.906Z","etag":null,"topics":["amateur-radio","docker","docker-compose","dx-cluster","dxspider","fastapi","ham-radio","self-hosted"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/n9bc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-16T18:36:50.000Z","updated_at":"2026-05-17T18:40:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/n9bc/dxspider-docker","commit_stats":null,"previous_names":["n9bc/dxspider-docker"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/n9bc/dxspider-docker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/n9bc%2Fdxspider-docker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/n9bc%2Fdxspider-docker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/n9bc%2Fdxspider-docker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/n9bc%2Fdxspider-docker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/n9bc","download_url":"https://codeload.github.com/n9bc/dxspider-docker/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/n9bc%2Fdxspider-docker/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34071661,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-08T02:00:07.615Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["amateur-radio","docker","docker-compose","dx-cluster","dxspider","fastapi","ham-radio","self-hosted"],"created_at":"2026-06-08T17:03:32.127Z","updated_at":"2026-06-08T17:03:33.166Z","avatar_url":"https://github.com/n9bc.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DXSpider Docker\n\n\u003e A self-hosted amateur-radio DX cluster running as a four-service Docker Compose stack.\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/)\n[![Tests](https://img.shields.io/badge/tests-171%20passing-brightgreen.svg)](stats-svc/)\n[![Made with FastAPI](https://img.shields.io/badge/Made%20with-FastAPI-009688.svg)](https://fastapi.tiangolo.com/)\n\n**DXSpider** provides the cluster engine (telnet on port 7300, PC-protocol inter-cluster peering in Phase 2). A Python sidecar ingests the live spot stream, stores spots in Postgres, and serves a statistics dashboard with real-time charts and a live connected-users panel. Caddy handles TLS termination and reverse-proxying.\n\nPublic repository: **https://github.com/n9bc/dxspider-docker**\n\n---\n\n## Status\n\n**v0.1.0** — initial release.\n\n- Python test suite (parsers, aggregation, API, WebSocket): **171 passing, 0 skipped**.\n- Container build and Compose integration are authored and peer-reviewed but **have not yet been brought up in a Docker environment** (the authoring host had no Docker). Bringing the stack up with `docker compose up -d` is the operator's first step; see the first-run checklist in [docs/deployment.md](docs/deployment.md) and [docs/troubleshooting.md](docs/troubleshooting.md). Docker-independent logic was fully tested locally with Python; container images pin Python 3.12.\n- See [VERIFICATION.md](VERIFICATION.md) for the full verification report.\n\n---\n\n## Features\n\n- **DXSpider telnet cluster node** — full DXSpider engine on port 7300; operators connect with any telnet client or logging software.\n- **Sysop web console** — ttyd wraps `console.pl` and is proxied by Caddy at `/cluster`; protected by HTTP basic auth.\n- **Live stats dashboard** — single-page HTML/ECharts dashboard served by FastAPI at `/`:\n  - Activity over time (spots per hour/day, up to last 168 h)\n  - Band and mode distribution (pie/bar charts)\n  - Geographic breakdown — top DX entities, top spotting entities, by continent\n  - Top spotters and most-spotted DX leaderboards\n  - Rare-DX highlights\n  - Per-callsign drill-down\n  - Human / RBN source filter on every chart\n- **WebSocket live ticker** — new spots and connected-users snapshots pushed to every open browser tab in real time.\n- **Spot ingestion** — persistent telnet monitor session; every spot tagged `source=human` or `source=rbn` at ingest time; frequency mapped to band + mode; callsign prefix resolved to DXCC entity + continent.\n- **Connected-users panel** — `show/users` polled every 20 s (configurable); live count displayed on the dashboard.\n- **Optional first-boot backfill** — reads existing CSV/TSV `*.spots` files from the shared `dxspider-data` volume on first start (`DX_BACKFILL_ON_START=true`). Note: DXSpider's native Perl/Data::Dumper spot-file format is not parsed in v1; backfill is a no-op on a fresh or native-format data volume (Phase 2 item). Charts populate from the live ingestor from the moment the stack starts.\n- **Automatic TLS** — Caddy obtains and renews Let's Encrypt certificates automatically when `DOMAIN` is set to a real FQDN.\n\n---\n\n## Architecture\n\nFour-service Docker Compose stack (`docker-compose.yml`):\n\n```\n                           ┌─────────────────────────────────────────┐\n                           │             Docker host                  │\n                           │                                          │\n  Internet / LAN           │  ┌─────────┐   /         ┌──────────┐  │\n ──────────────────────────┼──│  caddy  │─────────────▶│stats-svc │  │\n   port 80 / 443 (HTTP/S)  │  │  :80    │   /cluster   │  :8000   │  │\n                           │  │  :443   │─────────┐   └──────┬───┘  │\n                           │  └─────────┘         │          │       │\n                           │                      │          │ telnet │\n  Ham operators / nodes    │                      ▼          ▼       │\n ──────────────────────────┼─────────── port 7300 ──────────────────▶│\n   port 7300 (telnet)      │                  ┌──────────┐           │\n                           │                  │ dxspider │           │\n                           │                  │  :7300   │           │\n                           │                  │  :8080   │◀──────────┤ /cluster\n                           │                  └──────────┘           │  (ttyd)\n                           │                       │                 │\n                           │              shared dxspider-data       │\n                           │              volume (spots, read-only)  │\n                           │                       │                 │\n                           │                  ┌────▼─────┐          │\n                           │                  │ postgres  │          │\n                           │                  │  :5432    │          │\n                           │                  └───────────┘          │\n                           └─────────────────────────────────────────┘\n\nData flows:\n  caddy → stats-svc:8000          Dashboard and API  (all paths except /cluster)\n  caddy → dxspider:8080           Sysop web console  (/cluster* via ttyd)\n  stats-svc ingestor → dxspider:7300 → postgres      Live spot ingestion\n  stats-svc backfill ← dxspider-data (read-only)     First-boot history load\n  ham operators → host:7300 → dxspider               Telnet cluster access\n```\n\nServices at a glance:\n\n| Service | Image | Role |\n|---------|-------|------|\n| `dxspider` | Custom (Debian slim + Perl) | DXSpider engine + ttyd sysop console |\n| `stats-svc` | Custom (Python 3.12) | Telnet ingestor + FastAPI dashboard |\n| `postgres` | `postgres:16` | Durable spot and user store |\n| `caddy` | `caddy:2.8` | TLS termination + reverse proxy |\n\n---\n\n## Quick Start\n\n### Prerequisites\n\n- Docker Engine 24+ and Docker Compose v2\n- Ports 80, 443, and 7300 open in your host firewall\n- A registered amateur radio callsign\n\n### Steps\n\n```bash\n# 1. Clone\ngit clone https://github.com/n9bc/dxspider-docker.git\ncd dxspider-docker\n\n# 2. Create your local env file\ncp .env.example .env\n\n# 3. Edit .env — at minimum set:\n#    NODE_CALL, SYSOP_CALL, SYSOP_NAME, LOCATOR, NODE_QTH\n#    TTYD_PASSWORD, DX_MONITOR_PASSWORD\n#    POSTGRES_PASSWORD  (and update DX_DB_DSN to match)\n#    DOMAIN  (leave as \"localhost\" for local testing)\nnano .env   # or your editor of choice\n\n# 4. Build images and start the stack\ndocker compose up -d --build\n\n# 5. Watch logs until all services are healthy\ndocker compose logs -f\ndocker compose ps\n```\n\nThe first build clones DXSpider source from GitHub and may take 2–3 minutes depending on network speed.\n\n### URLs\n\n| What | URL |\n|------|-----|\n| Stats dashboard | `http(s)://DOMAIN/` |\n| Sysop web console | `http(s)://DOMAIN/cluster` |\n| Telnet cluster access | `telnet DOMAIN 7300` |\n\nReplace `DOMAIN` with the value set in `.env` (`localhost` for local testing, or your public FQDN when auto-TLS is enabled).\n\n### Quick smoke test\n\n```bash\n# All four services should show \"healthy\" or \"running\"\ndocker compose ps\n\n# API health check\ncurl http://localhost/api/health\n\n# Telnet\ntelnet localhost 7300\n```\n\n---\n\n## Container Images (GHCR)\n\nThe two custom services are published to the GitHub Container Registry on\neach tagged release, so you can run the stack without building locally:\n\n| Image | Pull |\n|-------|------|\n| DXSpider node | `ghcr.io/n9bc/dxspider-docker/dxspider:latest` |\n| Stats/dashboard | `ghcr.io/n9bc/dxspider-docker/stats-svc:latest` |\n\nRun from published images instead of `--build` using the provided override:\n\n```bash\ngit clone https://github.com/n9bc/dxspider-docker.git\ncd dxspider-docker\ncp .env.example .env          # edit as in Quick Start step 3\n\n# Pull and start the latest release (postgres/caddy are stock images)\ndocker compose -f docker-compose.yml -f docker-compose.ghcr.yml pull\ndocker compose -f docker-compose.yml -f docker-compose.ghcr.yml up -d\n\n# Pin a specific release instead of latest:\nIMAGE_TAG=0.2.0 docker compose -f docker-compose.yml -f docker-compose.ghcr.yml up -d\n```\n\nImages are `linux/amd64` (the bundled `ttyd` binary is x86_64-only; arm64\nusers build from source per `dxspider/Dockerfile`). Each image carries OCI\nprovenance labels linking it back to this repository and its license.\n\n### Cutting a release\n\nPublishing is automated by `.github/workflows/release.yml`. To release:\n\n```bash\ngit tag v0.2.0\ngit push origin v0.2.0\n```\n\nThe workflow builds both images and pushes `:0.2.0`, `:0.2`, and `:latest`\nto GHCR using the repository's `GITHUB_TOKEN` — no manual registry\ncredentials. New GHCR packages are private by default; set each package's\nvisibility to **Public** once (Repo → Packages) so others can pull without\nauthenticating.\n\n---\n\n## Documentation\n\nFull technical documentation lives under `docs/`. The files listed below are being written as part of this release; link here for orientation and detail.\n\n| Document | Contents |\n|----------|----------|\n| [docs/architecture.md](docs/architecture.md) | Container design, volumes, networking, data model |\n| [docs/configuration.md](docs/configuration.md) | All `.env` variables with defaults and guidance |\n| [docs/deployment.md](docs/deployment.md) | Step-by-step bring-up, TLS, firewall, first-run checklist |\n| [docs/operations.md](docs/operations.md) | Backup, restore, upgrades, log management |\n| [docs/development.md](docs/development.md) | Dev environment, running tests, project layout |\n| [docs/api.md](docs/api.md) | REST and WebSocket endpoint reference |\n| [docs/dashboard.md](docs/dashboard.md) | Dashboard views, filters, chart descriptions |\n| [docs/dxspider.md](docs/dxspider.md) | DXSpider configuration, source/version, ttyd console |\n| [docs/troubleshooting.md](docs/troubleshooting.md) | Common first-run problems and resolutions |\n| [docs/phase-2.md](docs/phase-2.md) | Partner peering, RBN aggregator — config-gated Phase 2 |\n\n---\n\n## Tech Stack\n\n| Layer | Technology |\n|-------|-----------|\n| Cluster engine | DXSpider (EA3CV `mojo` fork, Perl) |\n| Sysop console | ttyd 1.7.7 |\n| Ingestor + API | Python 3.12, FastAPI 0.115, asyncpg 0.30, uvicorn 0.34 |\n| Charts | Apache ECharts (browser, no build step) |\n| Database | PostgreSQL 16 |\n| Reverse proxy / TLS | Caddy 2.8 (automatic Let's Encrypt) |\n| Container runtime | Docker Engine 24+, Compose v2 |\n| Tests | pytest 8.3.4, pytest-asyncio 0.25, httpx 0.28 |\n\n---\n\n## Contributing\n\nContributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions, the TDD expectation, and PR conventions.\n\n---\n\n## Security\n\nSee [SECURITY.md](SECURITY.md) for the project's security posture, supported versions, and how to report a vulnerability privately.\n\n**Before exposing your node to the internet:** change every default password in `.env` (`TTYD_PASSWORD`, `DX_MONITOR_PASSWORD`, `POSTGRES_PASSWORD` / `DX_DB_DSN`).\n\n---\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n\n---\n\n## Acknowledgements\n\n- **DXSpider** by Dirk Koopman G1TLH and contributors. This project uses the [EA3CV `mojo` fork](https://github.com/EA3CV/dx-spider) (the primary HTTPS-accessible mirror carrying current development). The canonical upstream source is `git://scm.dxcluster.org/scm/spider` (port 9418, git protocol only) — see [docs/dxspider.md](docs/dxspider.md) for source-override instructions and production self-mirror recommendations.\n- **ttyd** by Shuanglei Tao — browser-based terminal emulator used for the sysop web console.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fn9bc%2Fdxspider-docker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fn9bc%2Fdxspider-docker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fn9bc%2Fdxspider-docker/lists"}