Skip to content

A machine that hosts its own broker

The physical worldMQTTMosquitto bridgenotification_topicbridge_cafilebridge_insecure

A 3D printer on the lineStep 3 of 7The printer claims and reports

The printer runs its own MQTT broker on 8883. It will not connect to anyone else's.

← The file goes to the machine firstA codec for a device's own protocol →

The site broker bridges out to the printer's own MQTT broker, carrying reports in and requests out under the line's topics, and reads presence from the bridge.

The situation

Some machines publish to your broker. Others, like a Bambu printer, run a broker of their own and expect clients to connect to them. The line still needs the printer’s messages on its own topics, stored and replayable, and needs to know when the printer drops off. Built by hand, that is a polling client per printer. Here the site’s Mosquitto connects out with a bridge.

The setup

one printer, one site
P1S (its own MQTT broker, :8883, user bblp + LAN access code)
⇅ Mosquitto bridge: report in, request out, under line/v1/<site>/<line>/<serial>/
machine broker (Mosquitto) ──bridge──▶ site NATS: JetStream keeps every report
│
Long Tail: the gateway reads the stream, sends requests through the broker,
and resolves escalations as the printer's bot

machine-site renders one bridge per printer:

deploy/bambu-site/mosquitto/mosquitto.conf
connection site-1-print-1-p1s-01
address <printer LAN address>:8883
bridge_protocol_version mqttv311
bridge_cafile /certs/bambu-ca-01P00A000000001.pem
bridge_insecure true
remote_username bblp
remote_password <printer access code>
remote_clientid line-bridge-site-1-print-1-p1s-01
cleansession true
try_private false
notifications true
notifications_local_only true
notification_topic line/v1/site-1/print-1/01P00A000000001/bridge
topic report in 0 line/v1/site-1/print-1/01P00A000000001/ device/01P00A000000001/
topic request out 0 line/v1/site-1/print-1/01P00A000000001/ device/01P00A000000001/

The printer’s certificate is issued to its serial, not its address. The setup reads the printer’s CA from 8883 the first time it sees the printer, and the bridge checks the chain against it with the host name skipped:

examples/workflows/bambu-printer/site/setup.ts
/** The root of the chain the printer presents, as PEM. */
function readPrinterCa(printer: BambuPrinter): Promise<string> {
return new Promise((resolve, reject) => {
const socket = connect({ host: printer.host, port: PRINTER_ACCESS.MQTT_PORT, rejectUnauthorized: false, timeout: 8_000 }, () => {
let cert: DetailedPeerCertificate = socket.getPeerCertificate(true);
while (cert.issuerCertificate && cert.issuerCertificate !== cert) cert = cert.issuerCertificate;
socket.end();
const body = cert.raw.toString('base64').match(/.{1,64}/g)!.join('\n');
resolve(`-----BEGIN CERTIFICATE-----\n${body}\n-----END CERTIFICATE-----\n`);
});
socket.on('timeout', () => socket.destroy(new Error(`${printer.station} at ${printer.host}:${PRINTER_ACCESS.MQTT_PORT} did not answer`)));
socket.on('error', reject);
});
}

On the floor

Reports arrive at line/v1/site-1/print-1/<serial>/report and land in JetStream. Requests the gateway publishes at .../<serial>/request reach the printer. The bridge writes 1 to its notification topic when it connects and 0 when it drops. The codec reads that as presence, so a printer that loses Wi-Fi raises a machine-offline escalation for print-servicing.

The guarantee

The printer’s messages reach the line only through the bridge, under the printer’s own level of the line’s topics, and the bridge trusts only the CA it read from that printer. See Machines.