Network looking glass platform with BGP route lookup, ping, traceroute and AS path diagnostics. Single Go binary — embeds the React frontend and SQLite database with no external dependencies.
- BGP route lookup — query routes from connected routers or an embedded GoBGP peer; community badges show rule descriptions on hover; AS path map visualises each hop
- Ping / Traceroute — run from any node; RTT stats and hops stream live via SSE (including remote lg-node agents)
- Hostname resolution — query targets that are hostnames are resolved to IP on the HopStat server (Go
net.DefaultResolver) before dispatch; private and link-local addresses are blocked - AS path map — visual hop-by-hop ASN breakdown with GeoIP enrichment on every BGP result (MaxMind first, Team Cymru DNS fallback)
- BGP Communities — define community string rules in admin; matched badges on BGP results; public
/communitiescatalogue linked from the footer - Quick queries — preset command/target shortcuts on the public home page, managed in Admin → Quick Queries
- Multi-node — direct router connections (SSH/Telnet) or remote agent deployment
- Vendor support — Cisco IOS/XR, Juniper JunOS, MikroTik RouterOS, Bird, Generic
- Responsive public UI — mobile-friendly query page with five locales (EN, TR, DE, FR, RU) and SEO metadata
- Cloudflare-aware — optional
behind_cloudflaremode shows real visitor IP viaCF-Connecting-IP - Admin panel — manage nodes, site branding, BGP neighbors, Communities rules, quick queries, GeoIP lookup, footer links and audit logs
- GeoIP — MaxMind ASN + City databases with interval-based downloads (timestamps stored in SQLite)
- Auto-update — self-updater checks GitHub releases and hot-swaps the binary (disable in Docker — use image pull instead)
- Single binary — React SPA, SQLite migrations and static assets are all embedded
# One-line installer (Linux, requires root)
curl -sSL https://raw.githubusercontent.com/HopStat/HopStat/main/install.sh | sudo bashOr manually:
curl -Lo hopstat https://github.com/HopStat/HopStat/releases/latest/download/hopstat-linux-amd64
chmod +x hopstat
./hopstat --mode=server
# config.yaml is auto-generated with random secrets on first start.
# Admin credentials are printed to the console — change them after first login.Download from Releases:
| Platform | Binary |
|---|---|
| Linux x86-64 | hopstat-linux-amd64 |
| Linux ARM64 | hopstat-linux-arm64 |
Build and run with Docker Compose (recommended):
cp .env.example .env # set LG_ADMIN_PASSWORD
docker compose up -d --buildThe container stores config and data under the hopstat-data volume (/data). On first start HopStat generates /data/config.yaml with random secrets unless you pin them via environment variables.
Admin credentials are written to the container log — check with docker logs <container>.
Docker notes:
- Set
LG_ADMIN_PASSWORDfor a known initial password (see.env.example). - Set
LG_UPDATE_ENABLED=falsein containers — self-update replaces the binary inside the image; pull a new image instead. NET_RAWandNET_ADMINcapabilities are required for ping and traceroute.- Optional GeoIP: uncomment
LG_GEOIP_LICENSE_KEYandLG_GEOIP_ACCOUNT_IDindocker-compose.yml.
To pin secrets across image rebuilds:
-e LG_SECURITY_JWT_SECRET=$(openssl rand -hex 32) \
-e LG_SECURITY_CREDENTIAL_KEY=$(openssl rand -hex 32)Or bootstrap with the installer:
bash install.sh --dockerSee docker-compose.yml for the full example.
Requires Go 1.25+ and Node.js 24+ (frontend build).
git clone https://github.com/HopStat/HopStat.git
cd HopStat
cd web/frontend && npm ci && npm run build && cd ../..
make build
./hopstat --mode=serverconfig.yaml is auto-generated on first start with every section from config.example.yaml — including flood control, GeoIP, BGP, query limits and TLS placeholders — plus random jwt_secret and credential_key values. No manual setup needed.
Site name, branding, query defaults and footer links are configured in Admin → Settings, not in YAML.
To customise, edit the generated file or override individual values with environment variables.
All config keys can be overridden with LG_ + the key path (dots → underscores, uppercased):
| Config key | Environment variable |
|---|---|
security.jwt_secret |
LG_SECURITY_JWT_SECRET |
security.credential_key |
LG_SECURITY_CREDENTIAL_KEY |
server.port |
LG_SERVER_PORT |
server.behind_cloudflare |
LG_SERVER_BEHIND_CLOUDFLARE |
database.path |
LG_DATABASE_PATH |
flood_control.enabled |
LG_FLOOD_CONTROL_ENABLED |
flood_control.http_rate_limit_per_min |
LG_FLOOD_CONTROL_HTTP_RATE_LIMIT_PER_MIN |
flood_control.query_rate_limit_per_min |
LG_FLOOD_CONTROL_QUERY_RATE_LIMIT_PER_MIN |
geoip.license_key |
LG_GEOIP_LICENSE_KEY |
geoip.account_id |
LG_GEOIP_ACCOUNT_ID |
geoip.update_interval |
LG_GEOIP_UPDATE_INTERVAL |
update.enabled |
LG_UPDATE_ENABLED |
query.max_concurrent |
LG_QUERY_MAX_CONCURRENT |
Note:
LG_ADMIN_PASSWORDis a special variable read directly at startup to set the admin password. It does not follow the viper key-path convention.
See config.example.yaml for the complete reference. Key sections:
server:
host: "0.0.0.0"
port: 8080
mode: "server" # server | agent
tls_cert: "" # optional TLS certificate path
tls_key: ""
behind_cloudflare: false # true when behind Cloudflare proxy
trusted_proxies: [] # optional extra reverse-proxy CIDRs
database:
path: "./lg.db"
security:
jwt_secret: "" # auto-generated; min 32 chars
credential_key: "" # auto-generated; 64 hex chars (AES-256-GCM for node credentials)
flood_control:
enabled: true
http_rate_limit_per_min: 100
query_rate_limit_per_min: 100
brute_force_max: 5
brute_force_ban_min: 15
audit:
retention_days: 90
async_write: true
query:
max_concurrent: 50
default_timeout_sec: 30
traceroute_timeout_sec: 60
geoip:
asn_db_path: ""
city_db_path: ""
license_key: "" # MaxMind account (free tier)
account_id: ""
db_dir: "./data/geoip"
update_interval: "72h" # downloads skipped until interval elapsed
update:
enabled: true
bgp:
listen_port: 11790
router_id: ""
local_as: 0
listen_addresses: []
add_path_receive: true # learn multiple paths per prefix from ADD-PATH peersAgent mode uses a separate template (agent.port, agent.token, etc.) — see Deployment Modes.
Runs the HTTP API, React SPA and query engine. Connects directly to routers via SSH/Telnet or delegates to remote agents.
./hopstat --mode=serverLightweight REST server deployed on remote POPs. The central server discovers it as a node. Agents expose SSE streaming endpoints for live ping/traceroute output.
./hopstat --mode=agent
# Default port: 9090 (agent.port in config)Auto-generated agent config includes a random agent.token used by the central server.
sudo ./hopstat --install-service
# Installs to /usr/local/bin, generates /etc/hopstat/config.yaml,
# writes and starts /etc/systemd/system/hopstat.service
journalctl -u hopstat | grep -A 10 HOPSTAT # view first-run credentialsAccess at /admin. On a fresh install, a random admin password is generated and shown once in the installer output or service log. Change it in Admin → Settings → Account after logging in.
From the panel you can:
- Add router nodes (SSH/Telnet credentials are encrypted with AES-256-GCM)
- Configure site name, logo, header colour, active languages and query defaults in Admin → Settings
- Configure footer links (website, contact, terms, privacy, PeeringDB)
- Configure standalone nodes with an agent token for embedded API queries
- Configure BGP neighbors and default route AS for the embedded GoBGP peer (Admin → BGP Neighbors)
- Manage Communities rules — community string, severity, description and active toggle
- Manage Quick Queries — preset shortcuts shown on the public query page
- Run GeoIP lookup — inspect MaxMind/Cymru enrichment for any IP (Admin → GeoIP)
- View audit logs
BGP community rules are configured in Admin → Communities. Each rule maps a community string (e.g. 65000:100) to a description and severity (info, success, warning, reject). Active rules are:
- Matched automatically on BGP route lookups — colour-coded badges with hover descriptions
- Listed on the public
/communitiespage (linked from the site footer) - Exposed via
GET /api/v1/communities(no authentication; active rules only)
When HopStat sits behind Cloudflare, enable real client IP detection:
server:
behind_cloudflare: trueOr set LG_SERVER_BEHIND_CLOUDFLARE=true. HopStat trusts Cloudflare proxy CIDRs and reads the visitor address from CF-Connecting-IP.
Obtain a MaxMind account and set in config.yaml:
geoip:
license_key: "..."
account_id: "..."
db_dir: "./data/geoip"
update_interval: "72h"Databases are downloaded automatically. Last-download timestamps are stored in SQLite so restarts within the interval do not trigger a new download.
HopStat prefers MaxMind for per-hop ASN and country enrichment (traceroute, AS path map). When databases are unavailable, Team Cymru DNS (origin.asn.cymru.com TXT lookups) is used as fallback.
See CONTRIBUTING.md for commit attribution (human authors only — no AI co-author trailers), repo hygiene, and release notes.
# Backend (restart manually after changes)
make run-server
# Frontend dev server with API proxy to backend
cd web/frontend && npm run dev
# Visit http://localhost:5173Run tests:
make test
make test-race
make test-cover # internal packages — 100% statement coverage required
make lint
cd web/frontend && npm testMIT — see LICENSE.