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

  1. Unset ECC_DASHBOARD_HOST to use the default 127.0.0.1, then access via localhost or an SSH tunnel
  2. Set ECC_DASHBOARD_HOST to exactly '127.0.0.1', 'localhost', or '::1'
  3. For remote access, use SSH port forwarding (ssh -L 3456:127.0.0.1:3456 user@host) rather than widening the bind address
  4. 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

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


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/8c13ba3fc9ebb22c. Report an issue: GitHub.