Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ jobs:
- name: Pytest with coverage
run: |
uv pip install '.[test]'
python -W error::ResourceWarning -m pytest tests/ -m "not live" --cov=bambu_cli --cov-report=term-missing --cov-fail-under=86
python -W error::ResourceWarning -m pytest tests/ -m "not live" --cov=bambu_cli --cov-report=term-missing --cov-fail-under=90
- name: Syntax smoke
# Auto-discovers bambu_cli/**/*.py (see scripts/syntax_smoke.py).
run: python scripts/syntax_smoke.py
Expand Down Expand Up @@ -176,8 +176,8 @@ jobs:
#
# Pinned to 3.12 on purpose. The generator needs 3.10+ to evaluate the
# contracts' `X | None` annotations, and pydantic is only installed
# above that floor (see the marker in pyproject). The package itself
# still runs on 3.9 — that leg is covered by the test matrix.
# above that floor (see the marker in pyproject). The package requires
# Python >=3.10 — that floor is covered by the test matrix.
run: uv run --python 3.12 --with pydantic python scripts/gen_schemas.py --check
- name: layer boundaries (blocking)
# Directories alone never held: protocols/, slicer/ and download/ already
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ Published on PyPI as `platecli`; the installed command is `plate`.
| Gate | Command / note |
|------|----------------|
| Default tests | `uv run python -m pytest tests/ -q -m "not live"` — never contacts a printer |
| Coverage (CI) | `--cov-fail-under=86` (2026-08-13, PR #119: Linux 90.99% / Windows 90.68% / macOS passing; matrix 3.10/3.12/3.14; A+ target **92%** — see roadmap) |
| Coverage (CI) | `--cov-fail-under=90` (2026-09-16, PR #123: Linux 92.8% / Windows / macOS passing; matrix 3.10/3.12/3.14; A+ target **92%** — see roadmap) |
| Lint | `uvx ruff check bambu_cli` + `uvx ruff format --check bambu_cli` |
| Types | `uvx mypy -p bambu_cli` |
| Security lint | `uvx bandit -c pyproject.toml -r bambu_cli -ll` |
Expand Down
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,36 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version

## [Unreleased]

### Security

- **SSRF filter hardening**: `netsafety` now unwraps IPv4-mapped IPv6,
deprecated IPv4-compatible IPv6 (`::/96`), 6to4, and NAT64 (`64:ff9b::/96`)
addresses and checks the embedded IPv4, and explicitly rejects IPv4/IPv6
multicast (`224.0.0.0/4`, `ff00::/8`) and deprecated site-local IPv6
(`fec0::/10`), which Python stdlib `ipaddress` treats as global. Public hosts
reached through NAT64 on IPv6-only networks stay reachable.
- **FTP remote-path validation (defense in depth)**: `BambuPrinter` now rejects
remote paths containing CR, LF, or NUL before any `STOR`, `RETR`, `DELE`, or
`NLST`, returning a clean failure. `ftplib` already refuses CR/LF on the
control channel; this adds the NUL check and avoids an unhandled `ValueError`.
- **URL validation & DoS prevention**: download URL validators now reject
whitespace and ASCII control characters up front, and `downloader` catches
low-level `http.client.HTTPException` to prevent unhandled tracebacks.
- **Filename sanitization**: remote and local download filename sanitizers now
strip and reject ASCII DEL (`\x7f`).

### Changed

- README: the "Print something" first-run steps (which start with installing
OrcaSlicer) now come before the "Try it in 30 seconds" simulation section,
and that section says plainly that `plate --sim status` needs neither a
printer nor OrcaSlicer while a real print needs both. No CLI behaviour
changed; the empty-`HOME` first-run contract is now pinned by tests:
`plate preflight` reports the missing config, OrcaSlicer binary, and BBL
profiles as three separate checks, `plate setup --sim` without a TTY prints
the non-interactive `--printer-ip`/`--serial`/`--access-code-file` hint
instead of a traceback, and `plate --sim status` still exits `0`.

## [0.5.1] - 2026-08-28

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ uv run python -m pytest tests/ -q -m "not live"

# Match CI hardness (ResourceWarning as error + coverage floor)
uv run python -W error::ResourceWarning -m pytest tests/ -m "not live" \
--cov=bambu_cli --cov-report=term-missing --cov-fail-under=86
--cov=bambu_cli --cov-report=term-missing --cov-fail-under=90

# Smokes used in CI — all of them (see the "lint" job in .github/workflows/ci.yml).
# These are NOT part of pytest; a green suite says nothing about them.
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2026 platecli contributors
Copyright (c) 2026 Dylan Reed

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
102 changes: 75 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@
[![Python versions](https://img.shields.io/pypi/pyversions/platecli)](https://pypi.org/project/platecli/)
[![Downloads](https://static.pepy.tech/badge/platecli)](https://pepy.tech/projects/platecli)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
<!-- Tests/coverage are hand-updated static badges (no coverage service wired up).
Re-measure and edit both after any release:
uv run python -m pytest tests/ -q -m "not live" --cov=bambu_cli --cov-report=term -->
[![Tests](https://img.shields.io/badge/tests-1535%20passing-brightgreen)](#tested-and-hardened)
[![Coverage](https://img.shields.io/badge/coverage-91%25-brightgreen)](#tested-and-hardened)

[Install](#install) · [Try it in 30 seconds](#try-it-in-30-seconds) · [Print something](#print-something) · [User guide](https://github.com/DLANSAMA/platecli/blob/main/docs/manual.md) · [Troubleshooting](https://github.com/DLANSAMA/platecli/blob/main/docs/troubleshooting.md) · [For AI agents](#built-for-ai-agents)
[Install](#install) · [Print something](#print-something) · [Try it without a printer](#try-it-in-30-seconds) · [Quality](#tested-and-hardened) · [User guide](https://github.com/DLANSAMA/platecli/blob/main/docs/manual.md) · [Troubleshooting](https://github.com/DLANSAMA/platecli/blob/main/docs/troubleshooting.md) · [For AI agents](#built-for-ai-agents)

<sub>Built and maintained by **[Dylan Reed](https://github.com/DLANSAMA)** · CI on Linux, macOS, and Windows across Python 3.10–3.14</sub>

</div>

Expand All @@ -28,6 +35,13 @@ model URL or file → download → slice (OrcaSlicer) → upload → pri
one command: plate go
```

**Who it's for**

- **Makers who live in a terminal** — print without opening a slicer GUI or signing into Bambu's cloud.
- **Homelab and print-farm tinkerers** — every step is a scriptable subcommand with real exit codes.
- **AI agents and automation** — `--json` output against [published JSON Schemas](https://github.com/DLANSAMA/platecli/tree/main/docs/schemas/), plus a `--sim` fake printer so a pipeline can be built and tested with no hardware.
- **Anyone on a locked-down network** — nothing leaves your LAN; no account, no telemetry, no daemon.

**Supports:** any Bambu Lab printer with LAN mode — P1P, P1S, X1C, X1E, A1, A1 Mini. **Hardware-tested on the P1 series (P1P/P1S) only.** The rest speak the same LAN protocols and are expected to work, but are unverified on real hardware — treat them as best-effort and please [open an issue](https://github.com/DLANSAMA/platecli/issues) with what you hit. One caveat: `plate snapshot` grabs the camera directly (no extra software) on P1/A1-class printers. X1-series cameras need a locally-running Docker streamer, and that path is opt-in (`camera_allow_streamer` or `--allow-camera-streamer`) because the streamer does not honour `cert_fingerprint`.

## Install
Expand All @@ -52,30 +66,6 @@ pip install platecli

<sub>Previously published on PyPI as `bambu-local-cli` (yanked). The project is now `platecli`; the installed command is `plate`.</sub>

## Try it in 30 seconds

No printer needed — simulation mode fakes one so you can kick the tires right away:

```bash
plate --sim status
```

```
🖨️ Bambu Printer Status
State: IDLE
Bed: 25°C / 0°C
Nozzle: 25°C / 0°C
Fan: 0 | WiFi: -42dBm
AMS:
Unit 0 (humidity 5, 26.0°C)
▶ Slot 0: PLA #F2F2F2 | 90%
Slot 1: PETG #0A0AC8 | 60%
Slot 2: empty
Slot 3: TPU #000000 | 45%
```

<sub>Timestamps and log-level prefixes trimmed for brevity.</sub>

## Print something

Four steps, no flags to learn:
Expand Down Expand Up @@ -129,6 +119,30 @@ It is a front-end, not new machinery: it slices and builds the `job` request thr
<img alt="plate tui: a live printer dashboard — state, temperatures, progress and AMS trays — and the two-column prepare screen it starts prints from" src="https://raw.githubusercontent.com/DLANSAMA/platecli/main/docs/tui.gif">
</p>

## Try it in 30 seconds

No printer yet, or OrcaSlicer not installed yet? Simulation mode fakes a printer so you can kick the tires right away — it needs neither. (A real print still needs both; see [Print something](#print-something).)

```bash
plate --sim status
```

```
🖨️ Bambu Printer Status
State: IDLE
Bed: 25°C / 0°C
Nozzle: 25°C / 0°C
Fan: 0 | WiFi: -42dBm
AMS:
Unit 0 (humidity 5, 26.0°C)
▶ Slot 0: PLA #F2F2F2 | 90%
Slot 1: PETG #0A0AC8 | 60%
Slot 2: empty
Slot 3: TPU #000000 | 45%
```

<sub>Timestamps and log-level prefixes trimmed for brevity.</sub>

## Why platecli

- **One command, whole pipeline** — `plate go` asks the questions; `plate job <url> --confirm` takes flags. Both download, slice, upload, and print in one shot; or run `download` / `slice` / `upload` / `print` individually.
Expand All @@ -139,6 +153,28 @@ It is a front-end, not new machinery: it slices and builds the `job` request thr
- **Fixes itself findable** — `plate doctor` checks network, FTPS, and MQTT health and tells you exactly what's wrong.
- **Hardened where it counts** — TLS certificate pinning, SSRF-guarded downloads, and size-capped ZIP extraction.

## Tested and hardened

The numbers in the badges are measured on this repo, not aspirational:

| | |
|---|---|
| **Tests** | 1,535 passing (`pytest -m "not live"`, v0.5.1) — unit, contract, property-based (Hypothesis), and Textual pilot tests |
| **Coverage** | **91.1%** line+branch across the package; CI fails the build below **86%** |
| **Platforms** | Linux, macOS, and Windows in CI, on Python 3.10 / 3.12 / 3.14 |
| **Blocking gates** | `ruff`, `ruff format`, `mypy` (whole package, `check_untyped_defs`), `bandit`, `pip-audit`, plus layer-boundary and JSON-schema-drift checks |
| **Mutation testing** | Nightly, scoped to the safety-critical modules (download validation, SSRF guards, slicer options, job payload) |
| **Packaging** | Every CI run installs the built wheel *and* the sdist and runs the CLI from them |

Coverage is deliberately honest — `except Exception` / `pass` / `continue` are **not**
excluded from the report (see `[tool.coverage.report]` in `pyproject.toml`), so the
number is not inflated by hiding error paths.

Security posture is documented rather than implied: TLS certificate pinning for the
printer connection, SSRF-guarded model downloads, size-capped archive extraction, and
a written threat model with its known limitations in
[SECURITY.md](https://github.com/DLANSAMA/platecli/blob/main/SECURITY.md).

## How it compares

The cloud-free Bambu ecosystem is in good shape, and for many people one of
Expand Down Expand Up @@ -183,17 +219,29 @@ Every command emits machine-readable `--json` output backed by published [JSON S

## Support & expectations

platecli is maintained by one person in their spare time. Bug reports and pull requests are genuinely welcome — [open an issue](https://github.com/DLANSAMA/platecli/issues) with your `plate doctor` output attached and I'll get to it when I can. There is no response-time guarantee, and feature requests may sit or be declined to keep the tool small and local-only. If you need something faster than that, fork it — it's MIT.
platecli is built and maintained by one person — [Dylan Reed](https://github.com/DLANSAMA) — in my spare time. Bug reports and pull requests are genuinely welcome — [open an issue](https://github.com/DLANSAMA/platecli/issues) with your `plate doctor` output attached and I'll get to it when I can. There is no response-time guarantee, and feature requests may sit or be declined to keep the tool small and local-only. If you need something faster than that, fork it — it's MIT.

## Status & disclaimer

**Status:** Beta, pre-1.0 — APIs and config keys follow the stability policy in [docs/api.md](https://github.com/DLANSAMA/platecli/blob/main/docs/api.md). The current release is whatever the [PyPI badge](https://pypi.org/project/platecli/) at the top of this page shows; `plate --version` reports the copy you have installed.

> **Disclaimer:** platecli is an unofficial, community-developed tool. It is not affiliated with, endorsed by, or supported by Bambu Lab. "Bambu Lab" and product names are trademarks of their respective owners, used here only to describe compatibility. The printer protocols (MQTT/FTPS) are reverse-engineered; a firmware update may break functionality without warning — run `plate doctor` after printer updates.

## Author

**Dylan Reed** — [github.com/DLANSAMA](https://github.com/DLANSAMA) · <[email protected]>

Design, implementation, test suite, and release engineering. For anything about
the project itself, an [issue](https://github.com/DLANSAMA/platecli/issues) or a
[discussion](https://github.com/DLANSAMA/platecli/discussions) is the fastest
route, and security reports have their own channel in
[SECURITY.md](https://github.com/DLANSAMA/platecli/blob/main/SECURITY.md).
Email is open for work enquiries. Pull requests are welcome — see
[CONTRIBUTING.md](https://github.com/DLANSAMA/platecli/blob/main/CONTRIBUTING.md).

## License

MIT — Use freely, modify as needed.
MIT © 2026 Dylan Reed — use freely, modify as needed. See [LICENSE](https://github.com/DLANSAMA/platecli/blob/main/LICENSE).

---

Expand Down
Loading
Loading