Note
This is a daily beta build (2026-04-08). 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.
New Features
- Optional PostgreSQL Database Support — Bambuddy can now use an external PostgreSQL database instead of the built-in SQLite. Set the
DATABASE_URLenvironment variable (e.g.,postgresql+asyncpg://user:passhost:5432/bambuddy) to connect to Postgres. SQLite remains the default when noDATABASE_URLis set. All features work with both backends including full-text archive search (FTS5 on SQLite, tsvector+GIN on PostgreSQL), backup/restore (file copy vs pg_dump/pg_restore), health diagnostics, and cross-database restore (import a SQLite backup into PostgreSQL with automatic type conversion and FK handling). - Shortest Job First Queue Scheduling (#879) — New SJF toggle badge on the queue page header. When enabled, the scheduler starts shorter print jobs before longer ones instead of FIFO order. A starvation guard ensures long jobs that get skipped once are protected from being skipped again — they move to the front of the queue on the next cycle. The queue display automatically reorders to show the scheduler's actual execution order. Print duration is cached on queue items at creation time from the 3MF metadata.
- Auto-Print G-code Injection (#422) — Configure custom start and end G-code snippets per printer model in Settings (Workflow tab) for bed-clearing systems like Farmloop, SwapMod, AutoClear, and Printflow 3D. When adding a print to the queue, enable "Inject G-code" to have the scheduler inject the configured snippets into the 3MF before uploading to the printer. The original file is never modified — injection creates a temporary copy for upload only.
- External Folder Subfolder Preservation (#890) — Scanning an external folder now mirrors the real directory structure into the file manager folder tree instead of flattening all files into the root. Subdirectories are created as child LibraryFolders with correct parent/child hierarchy, and files are assigned to their matching subfolder. Hidden directories are skipped when "Show hidden files" is disabled. Subfolders that are deleted from disk are automatically cleaned up on the next scan. Created subfolders inherit the parent's read-only and show-hidden settings.
- LDAP Authentication (#794) — Users can now authenticate against an LDAP/Active Directory server. Configure the LDAP server URL, bind DN, search base, and user filter in Settings > Authentication > LDAP. Supports StartTLS, LDAPS (SSL), and plaintext connections. LDAP groups can be mapped to BamBuddy groups (Administrators, Operators, Viewers) for automatic role assignment. Auto-provisioning creates BamBuddy accounts on first LDAP login when enabled. Local admin accounts remain as fallback when the LDAP server is unreachable. Password management features (change password, forgot password, admin reset) are automatically disabled for LDAP users.
- SpoolBuddy Quick Menu (#893) — Swipe down from the top of the SpoolBuddy display to open a quick-access control panel. Toggle printer power via smart plugs directly from the display, and manage the SpoolBuddy system with restart daemon, restart browser, reboot, and shutdown controls. All destructive actions require confirmation. The menu shows real-time smart plug state (ON/OFF) for each printer that has a linked power plug.
Improved
- Database Engine Info on System Page — The System Information page now shows the active database engine (SQLite or PostgreSQL) and its version in the Database section, making it easy to verify which backend is in use.
- Plate Number in Printer View (#881) — Printer cards and the stream overlay now show the plate number alongside the filename when printing plate 2+ of a multi-plate 3MF file (e.g. "MyModel — Plate 3"). Single-plate prints are unchanged.
- Printer Name in Queue for Model-Based Jobs (#881) — Queue items assigned to a printer type ("Any P1S") now show the actual printer name once the scheduler assigns a specific printer, instead of continuing to display the generic model target while printing or in history.
- AMS Drying Support for H2S (#886) — Remote AMS drying and queue auto-drying now work on H2S printers with firmware 01.02.00.00 or later.
- REST Smart Plug: Separate Power/Energy URLs and Unit Multipliers (#472) — REST/Webhook smart plugs can now use individual URLs for power and energy data instead of requiring all values in a single status response. Each value falls back to the shared Status URL when no separate URL is configured, so existing setups work without changes. Added power and energy multipliers for unit conversion (e.g., set energy multiplier to
0.001to convert Wh to kWh). Useful for platforms like ioBroker that expose each data point as a separate API endpoint.
Security
- Path Traversal in File Upload Endpoints — Archive upload endpoints (
/upload,/upload-bulk,/{id}/source,/source-by-name,/{id}/f3d,/{id}/timelapse) used the client-supplied filename directly in file paths without stripping directory components. An authenticated attacker could write files outside the intended directory via directory traversal (e.g.../../evil.3mf). All upload endpoints now sanitize filenames by extracting only the basename before constructing paths. Reported responsibly by Sacha Vaudey via securitybambuddy.cool. - Unauthenticated Bug Report Endpoints — The bug report endpoints (
/start-logging,/stop-logging,/submit) had no authentication, allowing anyone on the network to enable debug logging, retrieve system logs, and trigger bug report submissions with system diagnostics when authentication was enabled. All three endpoints now require authentication —start-loggingrequiressettings:updatepermission,stop-loggingandsubmitrequiresettings:read. Endpoints remain open when authentication is disabled (the default). Reported responsibly by Sacha Vaudey via securitybambuddy.cool. - API Key Empty Printer List Grants Full Access — An API key with an empty
printer_idslist ([]) was treated identically tonull(global access to all printers), granting full printer access instead of no access. Nownullmeans global access (admin key) and[]means no printer access. Existing API keys with empty lists are automatically migrated tonullon startup. Also fixed the webhook queue endpoint which used a falsy check that would bypass the filter for empty lists. Reported responsibly by Sacha Vaudey via securitybambuddy.cool. - Missing HTTP Security Headers — API responses did not include standard security headers. Added a middleware that sets
X-Content-Type-Options: nosniff(prevents MIME-sniffing),X-Frame-Options: DENY(prevents clickjacking via iframe embedding), andReferrer-Policy: strict-origin-when-cross-origin(limits URL leakage to external services) on every response.Content-Security-Policywas omitted because the React SPA uses inline styles extensively and a permissive CSP would provide no meaningful protection.Strict-Transport-Securitywas omitted because Bambuddy is a LAN application commonly accessed over HTTP — HSTS would lock users out. Reported responsibly by Sacha Vaudey via securitybambuddy.cool. - Camera Snapshot Temp Files World-Readable — Camera snapshot and plate detection endpoints created temporary JPEG files in
/tmpwith default 0644 permissions, making them readable by any local user. Switched fromNamedTemporaryFile(delete=False)tomkstempwith explicit 0600 permissions so only the application user can read them. Cleanup was already handled viafinallyblocks. Reported responsibly by Sacha Vaudey via securitybambuddy.cool.
Fixed
- Spool Weight Not Updated After Print (#839) — Filament usage tracking failed silently in several scenarios: (1) when FTP download failed and a fallback archive was created without a 3MF file, the primary tracking path was skipped entirely — now falls back to matching the 3MF from the library or a previous archive of the same file; (2) external/VT tray spools were never tracked by the AMS remain% fallback because it only iterated AMS unit trays — now captures and tracks VT tray remain% deltas; (3) notifications showed "Unknown" for time and filament on fallback archives — now enriches notifications with usage tracker results and captures estimated print time from MQTT at archive creation; (4) when auto-archive was disabled,
archive_idwas None at print completion so the entire 3MF tracking path was skipped — now searches library files and previous archives by filename to find the 3MF even without an archive, and captures the AMS slot-to-tray mapping at print start so it's available at completion regardless of archive state; (5) when auto-archive was disabled but the print was dispatched by BamBuddy (queue/reprint), the on_print_start callback discarded the expected print entry and returned early — the archive was never promoted to_active_prints, so at completionarchive_idandams_mappingwere both None, making all tracking paths fail. Now detects expected prints before the auto-archive early-return and falls through to the normal promotion path, also injecting the storedams_mappinginto the usage tracker session. - File Manager Stale UI After Deleting Folders/Files — Deleting a folder, file, or bulk-deleting items in the file manager appeared to succeed (toast shown) but the UI didn't update until a page reload. The delete endpoints (
delete_folder,delete_file,bulk_delete) relied on FastAPI's dependency cleanup auto-commit which runs after the response is sent — the frontend received the success response, refetched the folder/file list, but the delete hadn't been committed yet. Added explicitdb.commit()before returning in all three endpoints. - Spool Manager Deducts Double the Filament Used (#880) — After a print completed, the built-in spool manager subtracted twice the actual filament consumption. The printer's MQTT status message contains both updated AMS remain percentages and the
FINISHstate, which triggered two independent deduction paths in the same event loop cycle: the AMS weight sync (absolute SET from remain%) and the usage tracker (additive delta from 3MF data). The AMS weight sync now skips updates while a print session is active, letting the usage tracker handle deductions precisely via 3MF slicer data. - Thumbnails Broken After Backend Restart — Archive and library thumbnails returned 401 Unauthorized after a backend restart because stream tokens are stored in memory and lost on restart. The frontend now detects failed token-protected image loads and automatically refreshes the stream token, so thumbnails recover without a page reload.
- SpoolBuddy Kiosk Screen Blanks on Boot — The touchscreen display would blank immediately after the RPi booted, requiring a touch to wake. Added
consoleblank=0to the kernel cmdline to disable Linux console blanking during the Plymouth-to-labwc transition, and changed thewlr-randranti-blank loop to fire immediately instead of sleeping 60 seconds first. - Queue Widget Ignores Plate-Clear Setting (#752) — The "Clear Plate & Start Next" button on printer cards appeared even when "Require plate-clear confirmation" was disabled in Settings → Queue. The backend correctly auto-dispatched without waiting, but the frontend widget always showed the prompt. The widget now respects the setting and shows a passive queue link instead when plate-clear confirmation is disabled.
- Ghost Jobs From SQLite Lock on Print Completion (#897) — When a print finished, the queue status update (
printing→completed) could fail silently if the SQLite database was locked by another writer (e.g. the runtime tracker). The failed commit left the job permanently stuck inprintingstatus — a "ghost job" that caused the UI to show false double-assignments when the next job started. The critical queue status commit now retries up to 3 times with backoff on SQLite lock errors (PostgreSQL is unaffected — it uses row-level locking). Additionally, the runtime tracker was holding a single long transaction across all printers; it now commits per-printer to minimize lock hold time. - Multi-Plug Automation Only Works for First Plug (#903) — When multiple smart plugs were assigned to the same printer (e.g. a TUYA printer plug and a particle filter plug via Home Assistant), only the first plug's automation worked. The auto-on at print start, auto-off at print completion, and queue auto-off all queried for a single plug instead of iterating all plugs linked to the printer. All automation paths now control every assigned plug. Also fixed the queue auto-off path which was hardcoded to Tasmota instead of using the correct service for the plug type (HA, MQTT, REST).
- SpoolBuddy Inventory Not Updating on Spool Changes — Adding, editing, deleting, archiving, or restoring a spool in the internal inventory did not update SpoolBuddy's frontend views until the next manual refresh or 30-second poll. The spool CRUD endpoints did not emit websocket events, and the SpoolBuddy Dashboard had no polling fallback. All inventory mutation endpoints now broadcast an
inventory_changedwebsocket event, and the frontend invalidates the spool cache on receipt — so SpoolBuddy (and all other tabs) reflect changes instantly. - AMS Slot Changes Fail Until Reconnect (#887) — After a keep-alive timeout, paho-mqtt auto-reconnects but the new session can be half-broken: the printer continues sending status updates but silently ignores commands. The developer mode probe detected this (no response, leaving
developer_modeasnull), but had no timeout or recovery — one unanswered probe permanently blocked retries. Added a 10-second probe timeout with one retry; after two consecutive unanswered probes, Bambuddy force-closes the socket to trigger a clean reconnect with a fresh session. Additionally, the developer mode probe was firing on every auto-reconnect, which destabilized some firmware MQTT brokers (A1/P1 series) — causing a reconnect → probe → disconnect feedback loop. The probe result is now cached across reconnects and only runs once on the first connection, with a 5-second delay after connect to let the session stabilize. - WebSocket Crash on Printers Without
funField (#873) — Connecting to printers that don't send the MQTTfunfield (A1, P1 series, X1Plus firmware) caused a repeating'str' object has no attribute 'get'crash in the WebSocket handler, showing the printer as offline with missing AMS and SD card info. The developer mode probe introduced in 0.2.3b1 published an MQTT message inside_update_state()between overwritingraw_datawith the full MQTT dict (wherevt_trayis a raw dict) and restoring the previously normalized list — thepublish()call released the GIL, letting the event loop read the un-normalized dict and iterate over string keys instead of spool dicts. Fixed by normalizingvt_traydict→list in the MQTT data before assignment, and moving preserved field restoration before the probe. Added defensive normalization inprinter_state_to_dictas a belt-and-suspenders guard.