TC$mC$ on tC$ydellinen arkkitehtoninen suunta. Kun rakennamme OOB-sillan
(Out-of-Band Bridge) tC$ysin konfiguraatio-ohjatuksi ja geneeriseksi, luot
kC$ytC$nnC6ssC$ Mesh-verkon ydinreitittimen (Core Router).
TC$llC$ mallilla yksi kevyt Node.js-prosessi voi istua risteyskohdassa ja
yhdistC$C$ TCP/UDP-verkot, sarjaportit (serial), IRC-kanavat,
HTTP-pollausrajapinnat ja tC$ysin ilmaraolla (airgap) eristetyt
USB-tiedostojC$rjestelmC$t toisiinsa saumattomasti.
Koska viestiformaatti on aina se sama riippumaton JSON-array ja deduplikointi
hoituu MsgId:n avulla, monisolmureititys (A -> B -> C) toimii tC$ysin
automaattisesti ilman, ettC$ reitittimien tarvitsee tietC$C$ koko verkon
topologiaa.
TC$ssC$ on suunnitelma ja prototyyppi tC$lle geneeriselle OOB-reitittimelle.
1. Reitittimen Konfiguraatio (router_config.json)
MC$C$ritellC$C$n konfiguraatio, joka on helppo lukea lennosta. Se jakaantuu
kahteen osaan: Transports (fyysiset/loogiset vC$ylC$t) ja Routes
(reititystaulu).
{
"bridgeName": "Main-OOB-Router-1",
"transports": {
"airgap_vault": {
"type": "fs",
"inbox": "/media/usb_drive/in",
"outbox": "/media/usb_drive/out",
"poll_ms": 5000
},
"remote_site_b": {
"type": "irc",
"channel": "#mesh-site-b-link",
"botName": "LinkBotA"
},
"legacy_serial": {
"type": "serial",
"port": "/dev/ttyUSB0",
"baud": 115200
}
},
"routes": {
"airgap-*": "airgap_vault",
"site-b-*": "remote_site_b",
"hardware-controller": "legacy_serial",
"default": "remote_site_b"
}
}
2. Transport-Rajapinta (Plugin-arkkitehtuuri)
Jotta reititin pysyy geneerisenC$, jokainen OOB-vC$ylC$ (FS, Serial, IRC jne.)
kirjoitetaan omana pienenC$ plugininaan, jolla on tC$smC$lleen sama rajapinta.
TC$ssC$ esimerkkinC$ tuo mainitsemasi hakemistopohjainen (FS)
airgap-transport:
transports/fs_transport.js
import fs from 'fs';
import path from 'path';
export default class FSTransport {
constructor(config, onIncomingOOB) {
this.inbox = path.resolve(config.inbox);
this.outbox = path.resolve(config.outbox);
this.onIncomingOOB = onIncomingOOB; // Callback reitittimelle pC$in
[this.inbox, this.outbox].forEach(d => { if (!fs.existsSync(d))
fs.mkdirSync(d, { recursive: true }); });
// Pollaa Inboxia airgapin vastauksien varalta
setInterval(() => this._poll(), config.poll_ms || 5000);
}
// Reititin kutsuu tC$tC$, kun verkosta tulee airgapille osoitettu paketti
send(msgString, msgId) {
const fileName = path.join(this.outbox, `msg_${msgId}_${Date.now()}.jso
n`);
fs.writeFileSync(fileName, msgString, 'utf8');
}
_poll() {
const files = fs.readdirSync(this.inbox);
for (const file of files) {
if (file.endsWith('.json')) {
const filePath = path.join(this.inbox, file);
try {
const rawData = fs.readFileSync(filePath, 'utf8');
this.onIncomingOOB(rawData); // Injektoidaan viesti
takaisin Meshiin!
fs.unlinkSync(filePath);
} catch (e) { /* KC$sittele virhe (esim. tiedostoa vielC$
kirjoitetaan) */ }
}
}
}
}
3. Ydin: Geneerinen OOB Bridge (oob_bridge.js)
TC$mC$ on ohjelman sydC$n. Se lataa konfiguraation, kC$ynnistC$C$ tarvittavat
Transport-pluginit, ja alkaa siirtC$C$ paketteja lokaalin IP-verkon ja
OOB-vC$ylien vC$lillC$ sC$C$ntC6jen mukaisesti.
import fs from 'fs';
import { HybridMeshNode } from './mesh.js';
/ Importoidaan dynaamisesti (tai staattisesti) plugin-luokat
import FSTransport from './transports/fs_transport.js';
const TRANSPORT_PLUGINS = { 'fs': FSTransport };
const config = JSON.parse(fs.readFileSync('router_config.json', 'utf8'));
const node = new HybridMeshNode(config.bridgeName);
const activeTransports = {};
async function main() {
await node.start();
console.log(`[OOB Router] ${config.bridgeName} kytketty lokaaliin
Mesh-verkkoon.`);
// 1. Alustetaan kaikki konfiguroidut OOB-vC$ylC$t
for (const [tName, tConfig] of Object.entries(config.transports)) {
const PluginClass = TRANSPORT_PLUGINS[tConfig.type];
if (PluginClass) {
activeTransports[tName] = new PluginClass(tConfig, (rawString) =>
{
// KUN OOB-VCYLCSTC TULEE VIESTI, INJEKTOIDAAN SE LOKAALIIN
MESH-VERKKOON
console.log(`[OOB -> MESH] Vastaanotettu viesti vC$ylC$stC$:
${tName}`);
node._handleIncoming(rawString, null, 'OOB');
});
console.log(`[OOB Router] Transport kytketty: ${tName}
(${tConfig.type})`);
}
}
// 2. Kuunnellaan lokaalin Mesh-verkon viestejC$ ja reititetC$C$n ne
eteenpC$in OOB-vC$yliin
for await (const msg of node.inbox) {
if (msg.type === 0) continue; // Ei reititetC$ Gossip-viestejC$
OOB-linkkien yli (sC$C$stC$C$ kaistaa)
let routed = false;
// EtsitC$C$n sopiva reitti TargetID:n perusteella
if (msg.targetId) {
for (const [routePattern, transportName] of Object.entries(config.r
outes)) {
if (routePattern === 'default') continue;
// Yksinkertainen wildcard-match (esim. "airgap-*")
const match = routePattern.endsWith('*')
? msg.targetId.startsWith(routePattern.slice(0, -1))
: msg.targetId === routePattern;
if (match && activeTransports[transportName]) {
forwardToOOB(transportName, msg);
routed = true;
break;