Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
PORT=5555
# Listen address. Loopback by default (the dashboard exposes SSH + power controls);
# set to the host LAN IP or 0.0.0.0 to allow access from another machine.
BIND_HOST=127.0.0.1
LLM_PORT=8888
POLL_INTERVAL_GPU=2000
POLL_INTERVAL_CPU=2000
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ Format: version sections are listed newest first.

---

## [Unreleased]

### Security
- **`BIND_HOST` now defaults to `127.0.0.1` (loopback) instead of `0.0.0.0`** — the dashboard is unauthenticated and can SSH into and power off Sparks, so it is no longer reachable on the LAN by default. Set `BIND_HOST` to the host's LAN IP (or `0.0.0.0`) to opt in to remote access. **Migration:** if you access sparkDash from another machine via bare-metal `npm start`, set `BIND_HOST` explicitly. Docker is unaffected — `docker-compose.yml` already sets `BIND_HOST=0.0.0.0`. Startup now also warns when bound to a non-loopback address.

---

## [1.4.7] — 2026-08-02

### Added
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ Copy `.env.example` to `.env` if needed:

| Variable | Default | Description |
|----------|---------|-------------|
| `BIND_HOST` | `0.0.0.0` | HTTP and WebSocket listen address |
| `BIND_HOST` | `127.0.0.1` | HTTP and WebSocket listen address. Loopback by default — the dashboard exposes SSH + power controls, so set a LAN IP (or `0.0.0.0`) to allow remote access. |
| `PORT` | `5555` | HTTP + WebSocket listen port |
| `LLM_PORT` | `8888` | Default LLM probe port |
| `POLL_INTERVAL_GPU` | `2000` | GPU poll (ms) |
Expand All @@ -243,8 +243,11 @@ Copy `.env.example` to `.env` if needed:
| `HOST_SYS_PATH` | `/host/sys` | Host sys mount |
| `HOST_ROOT_PATH` | `/host/root` | Host root mount |

> When using Docker's default bridge network, keep `BIND_HOST=0.0.0.0`.
> With `network_mode: host`, use `BIND_HOST=127.0.0.1` to restrict access to the local host or a reverse proxy.
> The listener defaults to `127.0.0.1` (loopback) so the dashboard — which can SSH into and
> power off your Sparks — isn't reachable on the LAN by default. Set `BIND_HOST` to the host's
> LAN IP (or `0.0.0.0`) to reach it from another machine. The provided `docker-compose.yml`
> (`network_mode: host`) sets `BIND_HOST=0.0.0.0` explicitly; restrict access at the network
> layer, or set `127.0.0.1` when running behind a reverse proxy.

### Adding a Spark

Expand Down
12 changes: 11 additions & 1 deletion server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const ROOT = path.resolve(__dirname, "..");

const BIND_HOST = process.env.BIND_HOST || "0.0.0.0";
// Default to loopback: the dashboard exposes SSH and remote power controls, so it
// should not be reachable on the LAN unless explicitly opted in. Set BIND_HOST to the
// host's LAN IP (or 0.0.0.0) to expose it; the provided docker-compose.yml sets it.
const BIND_HOST = process.env.BIND_HOST || "127.0.0.1";
const PORT = parseInt(process.env.PORT || "5555", 10);
const LLM_PORT = parseInt(process.env.LLM_PORT || "8888", 10);

Expand Down Expand Up @@ -1164,6 +1167,13 @@ startBroadcast();
server.listen(PORT, BIND_HOST, () => {
console.log(`[sparkDash] server listening on http://${BIND_HOST}:${PORT}`);
console.log(`[sparkDash] WebSocket endpoint ws://${BIND_HOST}:${PORT}/ws`);
const isLoopback =
BIND_HOST === "localhost" || BIND_HOST === "::1" || /^127\./.test(BIND_HOST);
if (isLoopback) {
console.log(`[sparkDash] localhost-only; set BIND_HOST=<lan-ip> (or 0.0.0.0) to allow remote access`);
} else {
console.warn(`[sparkDash] WARNING: bound to ${BIND_HOST} — reachable on the LAN. This dashboard is unauthenticated and can SSH into and power off your Sparks; restrict access at the network/firewall layer.`);
}
startAllMonitors();
});

Expand Down