diff --git a/CHANGELOG.md b/CHANGELOG.md index 17bd0ca..530817f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version ## [Unreleased] +### Fixed + +- User-facing docs now match shipped behaviour for `--confirm` on `job` / + `send` (upload still runs; exit `0` `uploaded_not_printed`), the fail-closed + camera streamer (opt-in, not auto-fallback), `doctor` fingerprint/`-v` + output, STEP/STP slice precedence, and sdist-relative links to repo-only + quality docs. + ### Removed - Python 3.9 support. The floor is 3.10 so every install resolves patched diff --git a/SECURITY.md b/SECURITY.md index 3b4ea01..5294f7b 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -127,5 +127,5 @@ already-compromised local machine or a malicious printer on your own LAN ## Related docs - [AGENTS.md](AGENTS.md) — agent safety checklist -- [docs/quality-roadmap.md](docs/quality-roadmap.md) — security scoreboard +- [docs/quality-roadmap.md](https://github.com/DLANSAMA/platecli/blob/main/docs/quality-roadmap.md) — security scoreboard (GitHub/repo only; not in the sdist) - [docs/api.md](docs/api.md) — agent JSON contracts diff --git a/docs/api.md b/docs/api.md index ec9bf21..c3d71f6 100644 --- a/docs/api.md +++ b/docs/api.md @@ -305,7 +305,7 @@ Validate includes `checks[]`, `ok`, `errors`, `warnings`, `exit_code`, and `stri Success / dry-run: [`job_ok.json`](schemas/job_ok.json). Failure: [`job_error.json`](schemas/job_error.json). -Print start requires `--confirm`; without it the job may upload but will not print. +Print start requires `--confirm`. Without it, download → slice → upload still runs and the command exits `0` with `"status": "uploaded_not_printed"`; only the print step is withheld. ### `go` @@ -441,5 +441,5 @@ Machine-checkable schemas live in `docs/schemas/`. Contract tests under - [AGENTS.md](../AGENTS.md) — agent architecture and safety - [SECURITY.md](../SECURITY.md) — threat model -- [quality-roadmap.md](quality-roadmap.md) — quality scoreboard -- [test-backlog.md](test-backlog.md) — remaining schema/coverage gaps +- [quality-roadmap.md](https://github.com/DLANSAMA/platecli/blob/main/docs/quality-roadmap.md) — quality scoreboard (GitHub/repo only; not in the sdist) +- [test-backlog.md](https://github.com/DLANSAMA/platecli/blob/main/docs/test-backlog.md) — remaining schema/coverage gaps (GitHub/repo only; not in the sdist) diff --git a/docs/manual.md b/docs/manual.md index 0ce6125..fd9a3b1 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -35,7 +35,7 @@ pip install . `--sim` (simulation mode) replaces the real printer with a **canned** local stub — fixed status, files, and camera bytes. It is not a protocol test of MQTT/FTPS. Use it to develop agents and scripts without hardware. -Destructive and physical actions — starting a print, pausing or resuming a print, stopping a job, deleting a file, or sending raw G-code — are gated behind an explicit `--confirm` flag. An agent that omits `--confirm` gets a refusal (exit code `5`, `"status": "confirmation_required"`) instead of a physical action, so accidental physical operations never happen. Note this is a gate against accidents, not an authorization boundary: anything that can run `plate` can also pass `--confirm`. +Destructive and physical actions — starting a print, pausing or resuming a print, stopping a job, deleting a file, or sending raw G-code — are gated behind an explicit `--confirm` flag. `print`, `stop`, `pause`, `resume`, `delete`, and `gcode` refuse without it (exit code `5`, `"status": "confirmation_required"`) and the printer is untouched. `job` / `send` without `--confirm` still download, slice, and upload, then exit `0` with `"status": "uploaded_not_printed"` — only the print step is withheld. Note this is a gate against accidents, not an authorization boundary: anything that can run `plate` can also pass `--confirm`. ```bash # Inspect printer state without hardware @@ -51,7 +51,7 @@ plate job --json --confirm - **Printables downloads** — platecli fetches files from Printables *on your behalf*, from your own machine and network — the same file you would get by clicking Download. It identifies itself honestly as `platecli/`, keeps at least one second between requests to the same host, and honors `Retry-After`. Your use is subject to [Printables' terms of service](https://www.printables.com/legal/terms-of-use) and to the individual model's own licence (often a Creative Commons variant with attribution, non-commercial, or no-derivatives conditions). platecli grants you no rights to any downloaded model — check the licence on the model page before printing, remixing, redistributing, or selling. The Printables API used for resolution is undocumented and may change or stop working without notice. - **Safe extraction** — ZIP archives containing model files are fully supported. Existing files are kept safe by creating a numbered sibling such as `model-1.stl`. URL downloads and ZIP extraction have a 2048 MB safety limit, adjustable via `--max-download-mb`. - **Modularity** — Run steps individually using `download`, `slice`, `upload`, or `print`. -- **Safety first** — One-shot and print flows will not start a physical print unless `--confirm` is present. Pause, resume, stop, delete, and raw gcode also require `--confirm`, and refuse with exit code `5` without it. +- **Safety first** — `print`, `pause`, `resume`, `stop`, `delete`, and raw `gcode` refuse without `--confirm` (exit code `5`). `job` / `send` without `--confirm` still upload and exit `0` with `"status": "uploaded_not_printed"`; only the print step is withheld. - **TLS pinning** — Pin the printer’s self-signed cert with `cert_fingerprint` (setup/doctor can capture it). Prefer this over `insecure_tls`. - **SSRF-hardened downloads** — Private/loopback targets are refused unless you pass `--allow-private-ips` for that invocation. - **Diagnostics** — Network, FTPS, and MQTT health checking with `doctor` and `preflight`. @@ -324,6 +324,8 @@ plate snapshot --json # machine-readable result Every `--json` response includes `captured_at` (ISO-8601 UTC timestamp) and `sha256` (hex digest of the JPEG bytes). Agents should compare these fields across captures to confirm a fresh frame was received before sending the image to a user. Use `--unique` when taking repeated snapshots — it inserts a UTC timestamp into the filename so successive captures never silently overwrite each other. +P1/A1-class printers are captured directly over TLS port 6000 — no extra software. X1-series cameras are RTSP and need the Docker streamer, which is **opt-in** (`camera_allow_streamer` or `--allow-camera-streamer`) because it does not honour `cert_fingerprint`. A failed direct grab aborts unless you opt in. `camera_port` defaults to loopback-only (`127.0.0.1:1985:1984`); set it to `0.0.0.0:1985:1984` only if you deliberately want the unauthenticated feed on the LAN. + ## Global flags | Flag | Description | @@ -336,7 +338,7 @@ Every `--json` response includes `captured_at` (ISO-8601 UTC timestamp) and `sha ## Slicing & AMS -`slice` accepts common mesh formats in the precedence order STL > STEP > OBJ > 3MF > G-code. When mapping filaments to AMS slots, mapping arguments take zero-or-positive slot indexes. +`slice` accepts common mesh formats in the precedence order STL > STEP/STP > OBJ > 3MF > G-code. When mapping filaments to AMS slots, mapping arguments take zero-or-positive slot indexes. To decide that mapping, read what is actually loaded first: `plate status` shows each AMS unit's trays (filament type, colour, and remaining %), and diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index f820b5b..a9eaf03 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -45,7 +45,7 @@ the `plate doctor` output — but check first that no access code is visible. ```bash plate preflight # local only: config, OrcaSlicer, profiles, gmsh, xvfb-run, docker, zeroconf -plate doctor # talks to the printer: config -> MQTT -> FTPS, and prints the TLS fingerprint +plate doctor # talks to the printer: config -> MQTT -> FTPS; reports cert-pin status (add -v for LAN IP and full fingerprint) plate config show # the effective config, with secrets redacted plate config validate --strict ``` @@ -130,15 +130,17 @@ control that makes LAN traffic trustworthy. is expected and benign. Re-pin it: ```bash -plate doctor +plate doctor --json # always includes certificate_fingerprint ``` -`doctor` prints the printer's certificate SHA-256 and warns when it does not -match the `cert_fingerprint` in your config. Copy the printed value into -`cert_fingerprint` in `config.json`, or re-run -`plate setup --cert-fingerprint `. Both the bare-hex and colon-separated -forms are accepted — the value is normalised (lowercased, colons and spaces -stripped) before comparison. +Once a pin is already set, human `plate doctor` is hex-free on a match unless +you pass `-v` (which then prints the live SHA-256; the LAN IP is also +`-v`-only). On a mismatch it warns and shows the first eight hex characters of +the live cert. `plate doctor --json` always includes `certificate_fingerprint` +— use that value (or `plate setup --cert-fingerprint `) to re-pin after a +firmware update. Both the bare-hex and colon-separated forms are accepted — +the value is normalised (lowercased, colons and spaces stripped) before +comparison. **If you did NOT just update firmware**, do not blindly re-pin — a mismatch is also what an on-path attacker looks like. Confirm you are on the network you @@ -313,19 +315,25 @@ Setup falls back to manual configuration, which works fine. The full message tells you to install Docker Desktop (Windows/macOS) or `docker-ce` (Linux) and retry. But check whether you actually need Docker at -all: +all — the streamer is **opt-in**, not an automatic fallback: - **P1 and A1 series:** a direct TLS grab from the printer on **port 6000** — **no Docker involved**. The `--json` output reports `"method": "direct"`. - **X1 series:** the camera is an RTSP stream, which `plate` cannot decode - itself. It falls back to a small streamer container, so **Docker is required - on X1 only**. + itself. Set `camera_allow_streamer: true` or pass `--allow-camera-streamer` + (the streamer does not honour `cert_fingerprint`). **Docker is then + required on X1 only**. -So if you are on a P1/A1 and you are *seeing* this message, the direct grab -failed first and fell through. Check that port 6000 is reachable -(`nc -vz 6000`), that LAN mode is on, and re-run with `-v` / -`--verbose` — the direct-path failure is logged at debug level as -"Direct camera grab unavailable ...; trying Docker streamer." +A failed direct grab **aborts by default**. A pin mismatch, and any +`ssl.SSLError` during the handshake when a pin is configured, hard-abort — +they never start the streamer, even if you opted in. `camera_direct_only` +forbids the streamer entirely. + +You only see "Docker not found" after the streamer has been allowed. On a +P1/A1 that usually means the direct grab failed *and* you opted in. Check +that port 6000 is reachable (`nc -vz 6000`), that LAN mode is +on, and re-run with `-v` / `--verbose` — the direct-path failure is logged +at debug level as "Direct camera grab unavailable ...". The streamer container is published to `127.0.0.1` only by default (`camera_port` defaults to `127.0.0.1:1985:1984`). Do not point @@ -341,9 +349,10 @@ and a mismatch **fails closed** rather than silently falling back to the unpinned Docker path. After a firmware update, re-pin with `plate doctor` as in [Certificate fingerprint mismatch](#certificate-fingerprint-mismatch). -If you have no pin at all, `plate` warns that no `cert_fingerprint` is pinned -for the camera connection and tells you to run `plate setup` to pin one. Pin it -— don't set `insecure_tls`. +If you have no pin at all and `insecure_tls` is unset, the direct grab +**refuses** rather than sending the access code over an unverified TLS +connection. The error names the missing pin and points at `plate setup`. Pin +it — don't set `insecure_tls`. ## Snapshot fails and mentions `camera_allow_streamer` @@ -582,10 +591,14 @@ Overrides still work in the meantime; unknown keys are warn-but-pass. ## Nothing happens when I run a print command -Working as designed. Anything that physically moves the printer — starting a -print, pausing or resuming one, stopping one, deleting a file, sending raw -G-code — requires an explicit `--confirm`. Without it the command refuses with -exit code `5` and the printer is untouched. Add `--confirm` once you're sure. +Working as designed. `print`, `stop`, `pause`, `resume`, `delete`, and +`gcode` require an explicit `--confirm`. Without it they refuse with exit +code `5` (`"status": "confirmation_required"`) and the printer is untouched. + +`job` / `send` are different: omitting `--confirm` still runs download → +slice → upload and exits `0` with `"status": "uploaded_not_printed"`. Only +the print step is withheld — a file is on the printer. Add `--confirm` once +you're sure you want it to print. If you want to rehearse the whole pipeline with no hardware at all, use `--sim`: diff --git a/tests/package_contents_smoke.py b/tests/package_contents_smoke.py index f909658..25f81c3 100644 --- a/tests/package_contents_smoke.py +++ b/tests/package_contents_smoke.py @@ -145,7 +145,7 @@ "docs/manual.md": { "`--json` | Emit JSON for commands that support it; may appear before or after the subcommand", "`plate --json --version` emits", - "STL > STEP > OBJ > 3MF > G-code", + "STL > STEP/STP > OBJ > 3MF > G-code", "--max-download-mb", "zero-or-positive slot indexes", "Runtime package used by the installed command",