TOTP (Two-Factor Authentication):
- Detect TOTP vs email verification from Bambu API loginType response
- Use dedicated TFA endpoint on bambulab.com (not api.bambulab.com)
- Include browser-like headers to bypass Cloudflare protection
- Extract token from JSON response or cookies
- Frontend shows appropriate messages for each verification type
- Added i18n translations for TOTP UI (en, de, ja)
Closes#182
Add missing Authorization header to fetch() calls that bypass the
request() helper. These endpoints returned 401 when auth was enabled
because the JWT token wasn't being sent with the request.
Affected functions in frontend/src/api/client.ts:
- downloadSupportBundle (support bundle download)
- downloadPrinterFilesAsZip (printer file downloads)
- exportArchives (archive CSV/XLSX export)
- exportStats (statistics CSV/XLSX export)
Closes#231
When a spool is already linked in Spoolman, the FilamentHoverCard now shows
"Open in Spoolman" button instead of "Link to Spoolman". This allows users
to quickly navigate to the spool's page in Spoolman for editing.
Changes:
- Add GET /api/v1/spoolman/spools/linked endpoint returning tag->spool_id map
- FilamentHoverCard shows "Open in Spoolman" when linkedSpoolId is set
- "Link to Spoolman" only shows when spool is not linked
- Fix unlinked spools detection to strip JSON quotes from empty tags
- Add toast notifications for link success/failure
- Invalidate linked-spools query after linking
- Add backend tests for linked spools endpoint
- Add frontend tests for LinkSpoolModal
Closes#210
Add explanatory comment for CodeQL alert about clear-text storage
of JWT secret. This is intentional and secure:
- JWT secrets must be readable by the application
- File permissions set to 0600 (owner read/write only)
- Standard practice for self-hosted apps (same as .env files)
The alert should be dismissed in GitHub Security tab as "Won't fix".
The File Manager (Library) backend had no permission enforcement - endpoints were returning data to any authenticated user regardless of their group permissions.
Closes#224
Features:
- Add location filter for "Any {Model}" queue assignments
- Queue items can target a specific location (e.g., "Any X1C in Workshop")
- Location dropdown filter on Queue page to view jobs by location
- Scheduler considers location when assigning model-based jobs
Closes#220
## Summary
Address two critical security issues reported via GitHub Security Advisory:
1. Hardcoded JWT secret key allowing token forgery
2. Missing authentication on 77+ API endpoints
## Changes
### JWT Secret Key (backend/app/core/auth.py)
- Remove hardcoded secret "bambuddy-secret-key-change-in-production"
- Load secret from JWT_SECRET_KEY environment variable (recommended)
- Fall back to .jwt_secret file in data directory (auto-generated)
- Generate cryptographically secure 64-byte random secret if neither exists
- File is created with 0600 permissions for security
### API Authentication Middleware (backend/app/main.py)
- Add HTTP middleware that enforces auth on ALL /api/ routes
- When auth is enabled, every API request requires valid JWT or API key
- Only exempt routes that must be public:
- /api/v1/auth/status (check if auth enabled)
- /api/v1/auth/login (login endpoint)
- /api/v1/updates/version (version check)
- /api/v1/ws/* (WebSockets handle own auth)
### Test Updates
- backend/tests/conftest.py: Patch middleware's async_session for tests
- backend/tests/integration/test_ownership_permissions.py: Add missing
auth headers to requests that now require authentication
## Migration Notes
- Existing JWT tokens will be invalidated (users must re-login)
- Set JWT_SECRET_KEY env var in production for token persistence across restarts
- No database changes required
Fixes: GHSA-gc24-px2r-5qmf
Security: CWE-306 (Missing Authentication), CWE-321 (Hardcoded Crypto Key)
Closes GHSA-gc24-px2r-5qmf
The ams_mapping2 format was incorrectly using the tray_id (254/255) as
the slot_id for external spools. The printer expects slot_id to be the
actual slot index (0 for main nozzle, 1 for deputy nozzle), not the
tray_id value.
Before: {"ams_id": 255, "slot_id": 254} <- invalid slot index
After: {"ams_id": 255, "slot_id": 0} <- correct slot index
This caused prints using external spool to fail immediately with error
code 07FF_8007.
Closes#213
The ams_mapping2 format was incorrectly using the tray_id (254/255) as
the slot_id for external spools. The printer expects slot_id to be the
actual slot index (0 for main nozzle, 1 for deputy nozzle), not the
tray_id value.
Before: {"ams_id": 255, "slot_id": 254} <- invalid slot index
After: {"ams_id": 255, "slot_id": 0} <- correct slot index
This caused prints using external spool to fail immediately with error
code 07FF_8007.
Closes#213
Bambu Studio converts spaces to underscores when saving files to the
printer, but MQTT reports the original name with spaces. This caused
FTP downloads to fail with "550 Failed to open file" because we were
searching for "Battery Storage_giesela.gcode.3mf" but the actual file
was "Battery_Storage_giesela.gcode.3mf".
Changes:
- Add underscore variants to direct download path attempts
- Normalize spaces/underscores in fallback directory search
- Apply fix to archive download, cover extraction, and objects reload
Closes#218
The fix for A1/P1S FTP uploads (commit 82a6025) was accidentally broken in
commit 9969005 which removed the skip_session_reuse parameter from the
ImplicitFTP_TLS constructor. This caused P2S (and other models in
SKIP_SESSION_REUSE_MODELS) to still use SSL on the data channel, resulting
in "426 Failure reading network stream" errors.
The fix was implemented in commit b96ecfa on test/issue_174 branch but
never merged to main. This cherry-picks that fix.
Also includes:
- Storage diagnostics for debugging upload issues
- Better FTP error logging with specific error codes (553, 550, 552)
- Improved error messages in print scheduler
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Bug 1: Delete printer was not actually deleting archives when
delete_archives=True (default). The if-condition was inverted, causing
archives to remain and potentially causing FK constraint issues.
Bug 2: SmartPlug.printer_id had unique=True constraint, preventing
multiple HA scripts from being linked to the same printer. Removed the
constraint to allow multiple plugs/scripts per printer (matching
feature/176 behavior).
Closes#214
The support bundle states that printer serial numbers are NOT collected,
but they were appearing in debug logs. Added regex to sanitize Bambu Lab
serial numbers (00M/01D/01S/01P/03W prefix + alphanumeric) while keeping
the prefix for debugging context.
Example: [01D00A12345678] -> [01D[SERIAL]]
Closes#216
PR #215 claimed to fix this but the actual ALTER TABLE migration was
not included. Users upgrading from 0.1.6b11 to 0.1.6 still see:
"no such column: print_archives.sliced_for_model"
This commit adds the actual migration that was missing.
Closes#211
PR #215 claimed to fix this but the actual ALTER TABLE migration was
not included. Users upgrading from 0.1.6b11 to 0.1.6 still see:
"no such column: print_archives.sliced_for_model"
This commit adds the actual migration that was missing.
Closes#211
When printing with external spool holder (no AMS connected), the printer
reported error [0700-8012] "Failed to get AMS mapping table".
Root cause: The ams_mapping2 calculation for external spool (tray_id=254)
was incorrectly computing ams_id=63, slot_id=2 instead of the required
ams_id=255, slot_id=254.
The fix adds special handling for external spool tray IDs (254 for main
nozzle, 255 for deputy nozzle) which use ams_id=255 with slot_id matching
the tray_id.
Fixes#213
The sliced_for_model column was added to the PrintArchive model for
model-based queue assignment but the database migration was missing.
This caused upgrades from 0.1.6b11 to 0.1.6 to fail with:
"no such column: print_archives.sliced_for_model"
Users' data was not deleted - just inaccessible due to query failures.
- Add new `printers:ams_rfid` permission for re-reading AMS RFID tags
- Allows granting RFID re-read access without full printer control
- Operators group includes this permission by default
- Previously used `printers:control` which grants broader access
- Permission available in Settings > Users > Group Editor
Closes#204
Backend:
- Split update/delete permissions into *_own and *_all variants:
- queue:update_own/all, queue:delete_own/all
- archives:update_own/all, archives:delete_own/all, archives:reprint_own/all
- library:update_own/all, library:delete_own/all
- Add require_ownership_permission dependency factory in auth.py
- Enforce ownership checks on all relevant API endpoints:
- archives.py: PATCH, DELETE, POST /reprint
- print_queue.py: PATCH, DELETE, POST /cancel, PATCH /bulk
- library.py: PUT /files, DELETE /files, POST /bulk-delete, DELETE /folders
- Add user items count endpoint: GET /users/{id}/items-count
- Add delete_items parameter to DELETE /users/{id}
- Explicitly set created_by_id to NULL on user deletion for DB portability
- Add permission migration for existing groups in database.py
- Add require_permission_if_auth_enabled for folder delete
Frontend:
- Add canModify helper to AuthContext for ownership-based checks
- Update ArchivesPage: use canModify for edit/delete/reprint buttons
- Update QueuePage: use canModify for edit/delete/cancel buttons
- Update FileManagerPage: use canModify for edit/delete buttons
- Update SettingsPage: add user deletion modal with item handling options
- Update StatsPage: use archives:update_all for recalculate costs
- Update Permission type with new ownership permissions
- Add getUserItemsCount and update deleteUser API methods
Tests:
- Add test_ownership_permissions.py with 28 comprehensive tests
- Test admin *_all permissions, operator *_own permissions
- Test bulk operations skip non-owned items
- Test auth disabled allows all operations
- Test user deletion with/without items
Closes#205
Track and display who performs key actions in Bambuddy:
- Archives: who uploaded each archive file
- Library: who uploaded each file in File Manager
- Queue: who added each print job to the queue
- Printers: who started the current print (reprint tracking)
Backend changes:
- Add created_by_id column to print_archives, library_files, print_queue tables
- Add database migrations for new columns (auto-run on startup)
- Update archive, library, and queue routes to capture current user
- Add current-print-user endpoint for printer reprint tracking
- Track reprint user in PrinterManager in-memory state
- Fix file uploads not sending auth headers (FormData requires explicit headers)
Frontend changes:
- Display username on archive cards, library files, queue items
- Show "Started by" on printer cards during active prints
- Add auth headers to all 12 FormData upload functions
- Update TypeScript types for user tracking fields
Tests:
- Add unit tests for PrinterManager user tracking methods (7 tests)
- Add integration tests for current-print-user endpoint (3 tests)
- Add integration tests for library file user tracking (3 tests)
Works when authentication is enabled; gracefully hidden when disabled.
Closes#206
- Add "Schedule" button next to "Reprint" on archive cards for quick
access to print queue functionality
- Button respects queue:create permission for restricted users
- Previously only accessible via context menu (right-click)
Closes#208
Library files now store paths relative to base_dir instead of absolute
paths. This ensures thumbnails and files work correctly after restoring
a backup on a different system or with a different data directory.
Changes:
- Add to_relative_path() and to_absolute_path() helper functions
- Update file upload, ZIP extraction, and STL thumbnail generation
to store relative paths
- Update download, thumbnail, gcode, and delete endpoints to resolve
relative paths when accessing files
- Add database migration to convert existing absolute paths to relative
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The code is ready for testing. After pushing to the remote host:
1. The migration will run automatically on startup, converting any existing absolute paths
2. New files will be stored with relative paths
3. Thumbnails should display correctly after backup/restore
Replace the complex JSON-based backup system (~2000 lines) with a simple
approach that copies the SQLite database and all data directories into a
single ZIP file.
Backend changes:
- Add close_all_connections() and reinitialize_database() helpers to database.py
- New GET /backup endpoint: creates complete ZIP with bambuddy.db and all
data directories (archive, virtual_printer, plate_calibration, icons, projects)
- New POST /restore endpoint: extracts ZIP, replaces database and directories,
requires restart after restore
- Move legacy endpoints to /backup-legacy and /restore-legacy for transition
Frontend changes:
- Simplify api.exportBackup() - no longer takes category parameters
- Simplify api.importBackup() - no longer takes overwrite parameter
- Remove BackupModal and RestoreModal components from GitHubBackupSettings
- Add simple Download/Restore buttons with inline logic
- Add blocking modal overlay during backup/restore operations
- Add beforeunload handler to prevent accidental navigation
- Show operation status messages during backup/restore
Benefits:
- ~100 lines vs ~2000 lines of backup/restore code
- Complete by definition - SQLite database contains ALL data
- No code changes needed when schema changes
- No ID remapping required - IDs stay the same
- Faster - file copy vs querying all tables
- Add ?fps=N parameter to control camera frame rate (1-30, default 15)
- Add ?camera=false parameter for status-only overlay without camera feed
- Increase default camera FPS from 10 to 15 across all camera views
- Add comprehensive tests for new overlay parameters
Resolves user request on Issue #164 for higher FPS and status-only option.
Add --no-cache and --pull flags to ensure clean builds every time.
Remove registry cache arguments that could cause stale layers.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
These columns were added in 0.1.6 beta but migrations were missing:
- nozzle_count: Integer DEFAULT 1 (for dual-extruder detection)
- print_hours_offset: Float DEFAULT 0.0 (baseline hours adjustment)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add migration for target_model, required_filament_types, and waiting_reason
columns in print_queue table. These columns were added to the model for the
model-based queue assignment feature but the migration was missing.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add _sanitize_camera_url() that returns reconstructed URL from
validated and parsed components, breaking CodeQL's taint tracking
- Update _capture_mjpeg_frame, _capture_snapshot, _stream_mjpeg to
use sanitized URLs instead of original user input
- Keep _validate_camera_url as legacy wrapper for backwards compat
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Block access to cloud metadata services and dangerous destinations:
- AWS/GCP/Azure metadata endpoint (169.254.169.254)
- GCP internal metadata hostnames
- localhost and loopback addresses
- All link-local addresses (169.254.x.x)
Local network IPs (192.168.x.x, 10.x.x.x) are still allowed since
cameras are typically on the same LAN as the server.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Path traversal: Convert device number to integer to break taint chain,
use strict /dev/videoN validation with range limit
- SSRF: Add documentation explaining intentional SSRF for user-configured
external camera URLs, add lgtm suppression comments
- Info exposure: Don't expose exception messages in plate calibration
errors, only expose error type name
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>