affaan-m/ECC · critical · Error
[ECC] ECC_DASHBOARD_HOST must be loopback-only (127.0.0.1, l
Error message
[ECC] ECC_DASHBOARD_HOST must be loopback-only (127.0.0.1, localhost, or ::1).
What it means
The resolveDashboardHost() function in dashboard-web.js reads the ECC_DASHBOARD_HOST environment variable and validates it against LOOPBACK_HOSTNAMES, which is the set {'127.0.0.1', 'localhost', '[::1]', '::1'}. Any other value — including 0.0.0.0, LAN IPs, hostnames, or public addresses — is rejected. This is a deliberate security posture: the dashboard has no authentication and must never bind to a non-loopback interface.
Source
Thrown at scripts/dashboard-web.js:29
const fs = require('fs');
const path = require('path');
const http = require('http');
const {
LOOPBACK_HOSTNAMES,
buildAllowedHostnames,
isAllowedHostHeader,
isAllowedOrigin,
} = require('./lib/loopback-guard');
const { normalizeAgentTools } = require('./lib/agent-tools');
const DEFAULT_HOST = '127.0.0.1';
function resolveDashboardHost(env = process.env) {
const configured = String(env.ECC_DASHBOARD_HOST || '').trim().toLowerCase();
if (!configured) return DEFAULT_HOST;
if (!LOOPBACK_HOSTNAMES.has(configured)) {
throw new Error(
'[ECC] ECC_DASHBOARD_HOST must be loopback-only ' +
'(127.0.0.1, localhost, or ::1).'
);
}
return configured === '[::1]' ? '::1' : configured;
}
function parsePort(v) {
const n = parseInt(String(v), 10);
if (isNaN(n) || n < 1 || n > 65535) { console.error('[ECC] Invalid port: ' + v + ' — using 3456'); return 3456; }
return n;
}
const PORT = parsePort(process.argv[2] || process.env.ECC_DASHBOARD_PORT || '3456');
const HOST = resolveDashboardHost();
const ROOT = path.resolve(__dirname, '..');
function readFrontmatter(p) {
try {View on GitHub (pinned to 01e15490f0)
Solutions
- Unset ECC_DASHBOARD_HOST to use the default 127.0.0.1, then access via localhost or an SSH tunnel
- Set ECC_DASHBOARD_HOST to exactly '127.0.0.1', 'localhost', or '::1'
- For remote access, use SSH port forwarding (ssh -L 3456:127.0.0.1:3456 user@host) rather than widening the bind address
- Do NOT set ECC_DASHBOARD_HOST to 0.0.0.0 — the dashboard lacks authentication and exposing it is a security risk
Example fix
// before export ECC_DASHBOARD_HOST=0.0.0.0 node scripts/dashboard-web.js // after (option 1: unset) unset ECC_DASHBOARD_HOST node scripts/dashboard-web.js // after (option 2: explicit loopback) export ECC_DASHBOARD_HOST=127.0.0.1 node scripts/dashboard-web.js
Defensive patterns
Strategy: validation
Validate before calling
// Validate ECC_DASHBOARD_HOST before launching the server
const LOOPBACK = new Set(['127.0.0.1', 'localhost', '[::1]', '::1']);
const host = (process.env.ECC_DASHBOARD_HOST || '').trim().toLowerCase();
if (host && !LOOPBACK.has(host)) {
console.error(`[ECC] ECC_DASHBOARD_HOST='${host}' is not loopback-only.`);
console.error('Allowed values: 127.0.0.1, localhost, ::1. Unset the variable to use the default (127.0.0.1).');
console.error('For remote access, use SSH port forwarding instead of widening the bind address.');
process.exit(1);
} Type guard
// Type guard for a valid loopback hostname
const LOOPBACK_HOSTNAMES = new Set(['127.0.0.1', 'localhost', '[::1]', '::1']);
function isLoopbackHostname(value) {
return typeof value === 'string' && LOOPBACK_HOSTNAMES.has(value.trim().toLowerCase());
} Try / catch
try {
const host = resolveDashboardHost();
// start server...
} catch (error) {
if (error.message.includes('ECC_DASHBOARD_HOST must be loopback-only')) {
console.error(error.message);
console.error('For remote access use: ssh -L 3456:127.0.0.1:3456 user@remote-host');
process.exit(1);
}
throw error;
} Prevention
- Never set ECC_DASHBOARD_HOST to 0.0.0.0 or a public/LAN IP — the dashboard has no authentication
- Unset ECC_DASHBOARD_HOST to use the safe default of 127.0.0.1
- For remote access, use SSH port forwarding: ssh -L 3456:127.0.0.1:3456 user@host
- In Docker, bind to 127.0.0.1 inside the container and map ports via -p 3456:3456
When it happens
Trigger: Setting ECC_DASHBOARD_HOST=0.0.0.0 to expose the dashboard on all interfaces; setting it to a LAN IP like 192.168.1.100 for remote access; setting it to a hostname; or setting it to a public IP. Any of these trigger the guard before the HTTP server is created.
Common situations: Users trying to access the dashboard from another machine on the network; Docker containers where the host expects 0.0.0.0 binding; misconfigured environment from a deployment template that defaults to 0.0.0.0; SSH tunnel setups that expect a specific bind address.
Related errors
- Missing value for --target
- Unknown catalog command: ${options.command}
- Failed to read ${path.basename(filePath)}: ${error.message}
- The canonical ito-compute-cli is unpublished and ECC will no
- ${EXECUTABLE_OVERRIDE} must be an absolute path explicitly c
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/8c13ba3fc9ebb22c.
Report an issue: GitHub.