A machine that hosts its own broker
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
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 botmachine-site renders one bridge per printer:
connection site-1-print-1-p1s-01address <printer LAN address>:8883bridge_protocol_version mqttv311bridge_cafile /certs/bambu-ca-01P00A000000001.pembridge_insecure trueremote_username bblpremote_password <printer access code>remote_clientid line-bridge-site-1-print-1-p1s-01cleansession truetry_private falsenotifications truenotifications_local_only truenotification_topic line/v1/site-1/print-1/01P00A000000001/bridgetopic 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:
/** 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.
Related patterns
The physical world
Learned addresses, approved by a person
Step 4 of 12 · A factory line over MQTT
A station learns its machine from first contact; any later disagreement becomes a registration escalation a person approves or rejects.
The physical world
Offline, quiet and alarm escalations
Step 12 of 12 · A factory line over MQTT
A stopping alarm, a dropped machine and a silent one each become an escalation for the line operator, resolved when the machine recovers.
The physical world
One leader per line
Step 3 of 12 · A factory line over MQTT
Every replica runs the same config; one holds a Postgres advisory lock per line and runs its gateway, and the rest take over on loss.
