Update docs and prune examples superseded by real functionality

README/certs/README/.env.example/printers.example.toml were all still
describing this as a learning-stage stub with no real printer protocol
clients - rewritten to reflect the real Bambu MQTT client, cert
auto-pinning, and marlin serial connection that now exist.

- Remove examples/fetch_bambu_cert.rs: its manual fetch-and-save workflow
  is now what BambuPrinter::connect()'s ensure_trusted does automatically
  on every connect, so it has no remaining use case.
- Fix examples/printer_polymorphism.rs, which no longer compiled -
  dispatch_gcode was replaced by real methods earlier this session;
  swapped in print_status() as the vendor-only-extension example.
- Remove Config::bambu_ca_cert_overrides/bambu_require_valid_cert and the
  .env vars documenting them - confirmed dead (nothing read them),
  fully superseded by printers.toml's per-printer ca_cert_path/
  require_valid_cert plus automatic trust-on-first-connect pinning.
- Fix stale doc comments in printer/mod.rs and printer/bambu.rs that
  described certificate trust and MQTT as not built yet, or referencing
  the now-removed example.

No behavior change in the daemon itself - verified with cargo build
--examples and a live run of printer_polymorphism and auto_connect_bambu
against the real P1P/H2C printers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-07 16:17:15 +00:00
co-authored by Claude Sonnet 5
parent 11177db2ad
commit 94484bb190
11 changed files with 131 additions and 144 deletions
+5 -28
View File
@@ -1,6 +1,4 @@
use std::collections::HashMap;
use std::env;
use std::path::PathBuf;
#[derive(Debug, Clone)]
pub struct Config {
@@ -11,21 +9,12 @@ pub struct Config {
pub go2rtc_bin: String,
pub go2rtc_config: String,
/// Path to the fleet config file - see `fleet.rs` and
/// `printers.example.toml` for the shape.
/// `printers.example.toml` for the shape. Bambu certificate trust
/// (per-printer `ca_cert_path`/`require_valid_cert`, and automatic
/// trust-on-first-connect pinning otherwise - see
/// `BambuPrinter::ensure_trusted`) lives entirely in that file now,
/// not as a separate fleet-wide env-configured policy here.
pub printers_path: String,
/// Per-printer CA certificate overrides, keyed by printer ID. Most
/// Bambu printers chain to the bundled shared CA (`certs/bambu_ca2.pem`)
/// and don't need an entry here - but some models (P1P, at least) ship
/// their own certificate instead of one signed by that shared CA, with
/// no single file covering all of them. Look up a printer's own entry
/// (if any) when constructing it; there's no fleet-wide "the" override.
pub bambu_ca_cert_overrides: HashMap<String, PathBuf>,
/// When `false`, Bambu MQTTS connections skip certificate verification
/// entirely instead of checking against a CA. `true` (the default) is
/// the secure choice; only flip this fleet-wide policy if you have
/// printers you can't get to verify any other way.
pub bambu_require_valid_cert: bool,
}
impl Config {
@@ -38,22 +27,10 @@ impl Config {
go2rtc_bin: env_or("CONTINUUM_GO2RTC_BIN", "go2rtc"),
go2rtc_config: env_or("CONTINUUM_GO2RTC_CONFIG", "./go2rtc.yaml"),
printers_path: env_or("CONTINUUM_PRINTERS_PATH", "printers.toml"),
bambu_ca_cert_overrides: parse_cert_overrides(&env_or("CONTINUUM_BAMBU_CA_CERT_OVERRIDES", "")),
bambu_require_valid_cert: env_or("CONTINUUM_BAMBU_REQUIRE_VALID_CERT", "true") == "true",
})
}
}
/// Parses `"printer_id=path,printer_id2=path2"` into a lookup map. Empty
/// input (the common case - no overrides needed) yields an empty map.
fn parse_cert_overrides(raw: &str) -> HashMap<String, PathBuf> {
raw.split(',')
.filter_map(|entry| entry.split_once('='))
.map(|(id, path)| (id.trim().to_string(), PathBuf::from(path.trim())))
.collect()
}
fn require(key: &str) -> anyhow::Result<String> {
env::var(key).map_err(|_| anyhow::anyhow!("missing required env var {key}"))
}
+18 -14
View File
@@ -51,11 +51,13 @@ pub enum BambuTls {
impl BambuTls {
/// Resolves the trust mode for *one* printer. Call this per printer,
/// passing `config.bambu_ca_cert_overrides.get(printer_id)` - there's
/// no single override for the whole fleet, because not every Bambu
/// model chains to the same CA. `require_valid_cert = false` always
/// means `Insecure`, regardless of whether that printer also has an
/// override entry.
/// passing that printer's own `ca_cert_path`/`require_valid_cert` from
/// `printers.toml` (see `fleet.rs::build_bambu`) - there's no single
/// override for the whole fleet, because not every Bambu model chains
/// to the same CA, and most printers don't need this at all (see
/// `BambuPrinter::ensure_trusted` for the automatic path).
/// `require_valid_cert = false` always means `Insecure`, regardless of
/// whether that printer also has a `cert_path`.
pub fn resolve(cert_path: Option<&Path>, require_valid_cert: bool) -> anyhow::Result<Self> {
if !require_valid_cert {
return Ok(BambuTls::Insecure);
@@ -287,8 +289,8 @@ impl BambuPrinter {
if presented != pem_to_der(pinned_pem)? {
anyhow::bail!(
"{} presented a certificate different from the one pinned - could be a legitimate \
certificate rotation, could be something worse; re-run fetch_bambu_cert deliberately \
if you're sure it's the former",
certificate rotation, could be something worse; delete its file under certs/pinned/ \
and reconnect to re-pin deliberately if you're sure it's the former",
self.base.name
);
}
@@ -306,11 +308,14 @@ impl BambuPrinter {
/// presents, and returns it as PEM bytes.
///
/// This is meaningfully weaker than verifying against a CA you already
/// trust - anyone on the network at the exact moment you run this gets
/// trusted forever after. That's why it's a method you call once, by
/// hand (see `examples/fetch_bambu_cert.rs`), rather than something
/// `connect()` falls back to silently. Save the result to a file and
/// point that printer's `ca_cert_path` in `printers.toml` at it. */
/// trust - anyone on the network at the exact moment this runs gets
/// trusted forever after. `ensure_trusted` below is the only caller -
/// it only reaches this path when the bundled CA didn't verify, saves
/// the result under `certs/pinned/`, and every connection after this
/// one re-verifies against that pinned file instead of trusting the
/// network again. Not exposed as a manual step for the caller to run
/// ahead of time - there's no longer a reason to, now that `connect()`
/// does this automatically the first time a printer needs it. */
pub fn fetch_certificate(&self) -> anyhow::Result<Vec<u8>> {
let addr = resolve_addr(&self.base.host)?;
let stream = TcpStream::connect_timeout(&addr, Duration::from_secs(5))?;
@@ -327,8 +332,7 @@ impl BambuPrinter {
/* Makes sure this printer has a *working* trust configuration,
/// auto-pinning a certificate via trust-on-first-connect if needed -
/// the "smooth, no manual steps" version of the fetch_bambu_cert
/// workflow, built the way SSH handles host keys:
/// no manual step required, built the way SSH handles host keys:
///
/// - A pinned certificate already on disk (`cert_dir/<sn>.pem`) is used
/// directly. No new trust decision gets made on every run - that
+4 -4
View File
@@ -21,10 +21,10 @@
//!
//! Run `cargo run --example printer_polymorphism` to see it in action.
//!
//! Deliberately left out of this module for now, to keep it focused on one
//! idea at a time: real MQTT/HTTP/FTPS calls (that's `src/adapters/`) and
//! `Send`-bound futures (matters once this is wired into a multi-threaded
//! `tokio::spawn`, not yet).
//! Real MQTT is built now - `bambu.rs`/`bambu_mqtt.rs` for Bambu's
//! protocol, `marlin_serial.rs` for a real Marlin serial connection - see
//! each module's own doc comment. PrusaLink/Klipper's HTTP clients are
//! still stubs; that's the next piece to build past this module.
mod bambu;
mod bambu_commands;