Note
This is a daily beta build (2026-04-10). It contains the latest fixes and improvements but may have undiscovered issues.
Docker users: Update by pulling the new image:
docker pull ghcr.io/maziggy/bambuddy:daily
or
docker pull maziggy/bambuddy:daily
**Tip:** Use [Watchtower](https://containrrr.dev/watchtower/) to automatically update when new daily builds are pushed.
Improved
- AMS Drying Support for P2S — Remote AMS drying and queue auto-drying now work on P2S printers with firmware 01.02.00.00 or later. Previously P2S was hard-blocked from the drying feature.
New Features
- Print Files Directly from Project View (#930) — The project detail page now lists the printable files from every linked library folder inline, with Play (Print Now) and CalendarPlus (Add to Queue) action buttons on each sliced file (
.gcodeand.gcode.3mf). No more round-tripping through File Manager to reprint project files. Prints triggered from the project view are automatically associated with the originating project, so the resulting archive shows up in that project's history without any manual assignment. Backend adds aproject_idquery parameter toGET /library/filesthat returns all files across linked folders in a single query (replacing the prior one-request-per-folder pattern) and validatesproject_idon both the direct-print and queue paths so a stale ID yields a 404 instead of a FK-constraint 500. Fully localized across all 7 UI languages. Thanks to legend813 for the contribution. - Printers Page Search and Filters (#852) — The Printers page now has a live search bar and two filter dropdowns (status and location) to make finding specific printers in large setups easier, especially on mobile where Ctrl+F is impractical. Search matches printer name, model, location, and serial number (case-insensitive, whitespace-trimmed) and has a clear button. The status filter covers All / Printing / Paused / Idle / Finished / Error / Offline and is reactive to WebSocket status updates via a React Query cache subscription — so a print finishing while "Printing" is selected immediately removes the printer from the filtered list. The location filter is only shown when at least one printer has a location configured. All three filters are combinable; the controls are hidden when no printers are configured yet; and an empty-state message appears when no printer matches the current search/filters. Fully localized across all 7 UI languages. Thanks to legend813 for the contribution.
- LDAP Default Fallback Group — Settings → Authentication → LDAP → Advanced now has a "Default group" selector. When an LDAP user authenticates but is not listed in any mapped LDAP group, they are automatically assigned to this fallback group instead of being left without permissions. Previously such users could log in successfully but landed on empty pages because every permission check failed. Leave the setting empty to preserve the old behavior. A warning is logged each time the fallback is applied so administrators can spot missing group assignments.
Fixed
- Virtual Printer "Synchronizing device information" Times Out in Orca (#927) — OrcaSlicer's "Send job" flow sat on "Synchronizing device information…" until it gave up, even though the FTP upload itself worked when the user clicked "Send job anyway". The virtual printer's MQTT server gated all incoming command handling on
f"device/{self.serial}/request" in topic— if the slicer's cached serial for the VP didn't exactly equal the VP's computedself.serial(which depends on model prefix + per-VPserial_suffix), everyget_version,pushall, andproject_filepublish was silently dropped. Nothing was logged past the initial "MQTT publish to …" line, so the slicer never received apush_statusorget_versionresponse on its subscribeddevice/{serial}/reporttopic and hit its sync timeout. Status pushes, version responses, and project_file acknowledgments were also being published ondevice/{self.serial}/report, so even when the incoming check happened to pass, replies targeted a topic the slicer wasn't listening on if its serial had drifted. Both directions are now serial-adaptive: the handler accepts any authenticated publish on adevice/*/requesttopic, extracts the serial the slicer is actually using from the topic, stores it per-connection, and uses it for every outgoing status report, version response, print acknowledgment, and periodic push so responses always land on the topic the slicer subscribed to. The client's serial is cleared when the connection closes and when the server stops. Regression tests cover the mismatched-serial publish path, the non-request-topic rejection path, the pushall→status_report routing, and the client-serial lifecycle. - External Sidebar Link Icon Not Showing (#878) — Custom icons uploaded for external sidebar links rendered correctly in the edit dialog but were missing from the sidebar itself, and opening the icon URL directly returned
{"detail":"Valid camera stream token required..."}. The sidebar<img>tag inLayout.tsxused a raw/api/v1/external-links/{id}/iconURL, but that endpoint is protected by a query-string stream token (the same mechanism used for camera streams and archive thumbnails, because<img>tags cannot send Authorization headers). The edit dialog already routed throughapi.getExternalLinkIconUrl(), which wraps the URL viawithStreamToken(); the sidebar now does the same, so icons appear when auth is enabled. - Shortest Job First Toggle Disappears After Clicking (#879) — The SJF toggle badge on the queue page was rendered inside the Pending Queue section header, which is only shown when there is at least one pending item and the list view is active. Clicking the toggle often coincided with the scheduler starting the only pending print, at which point the Pending section unmounted and the toggle vanished along with it — making it look like the button had disappeared after clicking. The toggle has been moved to the top of the queue page, next to the list/timeline view switcher, so it stays reachable regardless of pending-item count, active filters, or the selected view mode.
- SpoolBuddy Update Fails in Docker with "no user exists for uid 1000/1001" — The SpoolBuddy remote-update flow shelled out to the OpenSSH
ssh-keygenandsshbinaries for keypair creation and command execution. Both binaries callgetpwuid(getuid())at startup and abort withNo user exists for uid <N>when the container runs under an arbitrary PUID that is not listed in/etc/passwd(the stockpython:3.13-slimimage only has an entry for root, so running withuser: "1000:1000","1001:1001", or any non-root user tripped the same error). The entire SpoolBuddy update path is now subprocess-free: keypairs are generated in-process via thecryptographylibrary (already a dependency), SSH commands run through the pure-Pythonasyncsshclient, and git-branch detection reads.git/HEADdirectly instead of shelling out togit. asyncssh also callsgetpass.getuser()for local~/.ssh/confighost matching, which hit the same passwd lookup failure; the Docker image now setsLOGNAME=bambuddy,USER=bambuddy, andHOME=/appsogetpass.getuser()resolves via env vars before touching the passwd database, andasyncssh.connect()is called withconfig=[]so it does not attempt to load~/.ssh/configat all. Branch detection also now looks for.git/HEADin the application root rather thansettings.base_dir— in Docker the data directory is a separate volume (DATA_DIR=/app/data) that never contains.git. Finally, the Docker build now bakes.git/HEADinto the image (.dockerignoreallows this single 20-byte file through the context filter) so the production image knows which branch it was built from; previously the.gitdirectory was excluded from the build context entirely, leaving the container with no git metadata and causing the SpoolBuddy update flow to always pullmainon the remote device regardless of which branch Bambuddy itself was built from. Native installs behave identically — they already worked because the running user was always in/etc/passwdand.git/HEADwas readable from the project root. Regression tests assert that neither keypair creation nor command execution spawns any subprocess, and that branch detection reads from the application root even when a decoy.gitsits inside the data dir. - Camera Stream "6 of 5" Reconnect Counter + ffmpeg Log Flood (#925) — Two bugs surfaced while investigating camera reconnect behaviour. First, the camera page briefly displayed "Reconnecting attempt 6 of 5" before giving up, because the attempt counter could be incremented to the maximum while the reconnect banner was still rendering. The displayed value is now clamped to the configured maximum. Second, every failed ffmpeg spawn logged the full ~20-line ffmpeg version/configuration banner, producing hundreds of lines of noise per failed camera click (one reported click produced 555 log lines across 30 retries). A new stderr summarizer strips the ffmpeg banner before logging so only the actual error lines remain. The underlying "camera service stops accepting new connections after prolonged uptime" behaviour in the X1C firmware is still under investigation.
- LDAP POSIX Primary Group Ignored — LDAP authentication only looked at groups that listed the user explicitly via
memberUid(supplementary group membership). A user's POSIX primary group — referenced by thegidNumberattribute on the user object and matching thegidNumberon aposixGroup— was ignored entirely, so users whose role came from their primary group landed without the expected permissions. The authenticator now also searches forposixGroupentries whosegidNumbermatches the user's primarygidNumber, and dedupes DNs case-insensitively before resolving the group mapping (LDAP DNs are case-insensitive by spec). - Support Bundle Leaks Virtual Printer IP Address — The debug support bundle included the
virtual_printer_remote_interface_ipsetting value unmasked insupport-info.json. The setting key didn't match any of the existing sensitive-key filters, so the raw IP address was included in the bundle. Added_ipto the sensitive key filter so IP address settings are excluded from support bundles. Log file content was already covered by the existing IPv4 regex redaction. - "Build Plate Cleared" Button Unclickable After Second Print (#912) — After completing the first queued print and confirming the plate was cleared, the "Build plate cleared — ready for next print" button became unresponsive after the second print finished. The React Query mutation's
isSuccessstate persisted from the first plate-clear confirmation, causing the component to render the static "Plate Ready" confirmation instead of the clickable button. The mutation state is now reset when the printer leaves the FINISH/FAILED state, so the button works correctly on every print cycle. - Spoolman Location Not Cleared When Spool Removed from AMS (#921) — When Spoolman auto-sync was enabled and a spool was removed from an AMS slot, its location in Spoolman was never cleared, causing "double-booked" slots where multiple spools shared the same location. The auto-sync callback set locations for newly inserted spools but skipped the cleanup step that clears stale locations. The location clearing logic now runs after every auto-sync cycle. Also fixed the single-printer manual sync endpoint which didn't track synced spool IDs, risking incorrect location clearing for location-matched (non-RFID) spools.