feat: Guest WiFi, network intelligence, firmware orchestration, platform automation, api-ssl (v0.16.3-beta)

Guest WiFi (Hotspot): one-click captive-portal wizard that creates the guest
SSID (virtual AP per radio), VLAN/bridge segregation + L3 VLAN interface, pool,
DHCP, portal, rate-limited guest profile, and masquerade; vouchers with limits +
printable sheets; live guest sessions; walled garden.

Network intelligence: per-client device fingerprinting (vendor/hostname →
category) with a persisted per-client override on the detail page; rogue/neighbor
AP detection from stored AP scans (evil-twin flagging); Mist-style anomaly
insights (client-count/CPU vs same-hour 14-day baseline + error bursts) in the
Operations engine.

Firmware orchestration: staged wave-based RouterOS rollouts (canary first) with
pre-upgrade backup, reboot-ride-out, version verification, halt-on-failure, and
scheduling; live per-device progress + cancel.

Platform & automation: scoped API tokens (mtm_, read/write, hashed at rest,
Bearer auth in requireAuth); outbound webhooks (12 events, HMAC-signed) fired
from the alert pipeline + firmware orchestrator; scheduled email reports.

api-ssl: RouterOSClient negotiates TLS on port 8729 and accepts RouterOS's
default self-signed certs (rejectUnauthorized:false), so SSL-secured API devices
connect without manual cert trust. Fixes #57.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2GT-Rich
2026-07-02 21:58:02 -07:00
co-authored by Claude Opus 4.8
parent efb430d934
commit 9dfb8a5333
38 changed files with 3517 additions and 20 deletions
@@ -1,4 +1,5 @@
import * as net from 'net';
import * as tls from 'tls';
import * as crypto from 'crypto';
import { EventEmitter } from 'events';
@@ -34,15 +35,21 @@ export class RouterOSClient extends EventEmitter {
private tagCounter = 0;
private readonly useTls: boolean;
constructor(
private readonly host: string,
private readonly port: number = 8728,
private readonly username: string,
private readonly password: string,
private readonly connectTimeoutMs: number = 15000,
private readonly readTimeoutMs: number = 30000
private readonly readTimeoutMs: number = 30000,
useTls?: boolean
) {
super();
// api-ssl runs on 8729 by RouterOS convention (the same heuristic the
// security-posture code uses); callers can override explicitly.
this.useTls = useTls ?? port === 8729;
}
async connect(): Promise<void> {
@@ -54,9 +61,7 @@ export class RouterOSClient extends EventEmitter {
this.socket?.destroy();
}, this.connectTimeoutMs);
this.socket = net.createConnection({ host: this.host, port: this.port });
this.socket.on('connect', async () => {
const onReady = async () => {
clearTimeout(timer);
try {
await this.authenticate();
@@ -67,7 +72,21 @@ export class RouterOSClient extends EventEmitter {
this.socket?.destroy();
reject(err);
}
});
};
if (this.useTls) {
// api-ssl: RouterOS devices ship self-signed certificates by default,
// so certificate verification is intentionally disabled — the same
// trust model as the plaintext API this replaces, but with the
// credentials and session encrypted in transit.
this.socket = tls.connect(
{ host: this.host, port: this.port, rejectUnauthorized: false },
() => { void onReady(); }
);
} else {
this.socket = net.createConnection({ host: this.host, port: this.port });
this.socket.on('connect', () => { void onReady(); });
}
this.socket.on('data', (data: Buffer) => {
this.buffer = Buffer.concat([this.buffer, data]);