Firmware 3.10.4 separates two credentials: a dashboard user ID and password for people, and an optional developer API access code for programs. New HATs and previously unprotected upgrades start with dashboard ID admin / password admin; developer access is off. An explicitly saved security-off setting is preserved. The developer code is independent of the Bambu printer's LAN code. All access protection also applies through the HAT's setup Wi-Fi network.
Sign in with admin / admin on a new HAT. Open Settings > Network > Dashboard Login to choose your own ID and password. Enter the current password, new ID and new password twice, then select Save ID and Password. There is one local dashboard account and no cloud account. If security was disabled, use Enable Dashboard Login to create the account again.
The ID is case-sensitive and contains 3–32 ASCII letters, digits, ., _ or -. Since firmware 3.10.3, custom passwords contain 8–128 UTF-8 bytes, with no required numbers, symbols or mixed case. The exact public default admin is also allowed when the ID is admin; no other short password is accepted. Spaces are preserved; credentials are not trimmed or case-folded. Multibyte characters count as more than one byte. Invalid UTF-8 and embedded NUL bytes are rejected.
Enabling login protects HTTP state, controls, configuration, calibration/firmware transfers, print thumbnails and WebSocket telemetry/commands. The static login page/logo and access metadata remain public. mDNS still advertises the HAT's address. With developer access disabled, programs also need a valid dashboard browser session; unauthenticated API requests are not left open.
Password checks run cooperatively in the existing page worker, while telemetry and control tasks continue. Wait for the pending operation to finish; do not automatically retry it. Custom credentials use a random 16-byte salt and a PBKDF2-HMAC-SHA256 verifier with 600,000 iterations; the password is not saved. The deliberately public factory account has a precomputed verifier and a fast initial check. Firmware 3.10.4 batches ESP32-S2 hardware SHA blocks, reuses keyed states and padding, and yields by elapsed time without changing the verifier format or iteration count. It releases the shared crypto lock at least every 128 rounds and before cancellation/yield callbacks. Browser login measured about 6–9 seconds and changing both credentials about 16–25 seconds on an ESP32-S2 HAT, depending on telemetry load. A 141-second connected-printer follow-up received 122 reports without new MQTT errors or disconnects. The page shows elapsed seconds. A username-only change verifies the current password once and reuses its existing verifier. Saved credentials from 3.10.3 remain valid without conversion.
After enabling dashboard login, open Developer API Access and select Enable API Access Code. Save the displayed eight-character code before selecting I Saved It. Generate New API Code replaces it; the saved code cannot be displayed again. Disable API Access Code turns off direct code authentication while keeping dashboard login enabled.
Treat the developer code as a trusted device-administration credential. It can operate the documented control, firmware-upload and factory-reset APIs. It cannot sign into the dashboard or use the account-settings endpoints once a dashboard account exists. This separation is not a restricted-permission API role.
When developer access is enabled, supply X-Tyson-Access-Code on each HTTP request or WebSocket upgrade. Keep the existing Host, Origin and mutation-header rules. Replace the example code:
GET /api/state HTTP/1.1
Host: 192.0.2.42
Accept: application/json
X-Tyson-Access-Code: AB23CD45
For WebSocket, connect to ws://192.0.2.42:81/ with Origin: http://192.0.2.42 and the same code header. Browser JavaScript cannot add this custom upgrade header: use a signed-in dashboard session or a native WebSocket library. Codes are not accepted in URLs or WebSocket text messages. A developer code does not create a browser session.
Codes use 0123456789ABCDEFGHJKMNPQRSTVWXYZ; lowercase is accepted. An explicitly supplied invalid code never falls back to a browser cookie. 401 means credentials are missing, invalid or expired. 429 means failed attempts are throttled, with Retry-After in seconds. Do not automatically replay a control request after authentication or transport failure.
The Python example supports a hidden prompt:
python wiki/examples/python/tyson_client.py --host http://192.0.2.42 --ask-access-code
Pass access_code=code to TysonClient; the CLI also reads TYSON_LAN_ACCESS_CODE. Avoid putting credentials in URLs, command history, logs or shared source files. The Python client uses developer authentication; it does not create dashboard accounts or handle passwords.
Responses are JSON with Cache-Control: no-store. POSTs require matching Host/Origin checks, X-Tyson-Request: 1, and URL-encoded fields. These credential forms have a separate 1024-byte encoded-body limit. Credentials must be sent exactly once; malformed or duplicate form fields are rejected.
| Method / path | Fields | Behavior |
|---|---|---|
GET /access/status |
None | Public booleans: enabled, authenticated, dashboard_enabled, api_enabled, setup_required. No ID, password, code or device state. authenticated describes dashboard access, not a supplied developer code. |
POST /access/login |
id, password |
Valid dashboard credentials set a browser session. The developer code is not accepted here. |
POST /access/logout |
Empty form | Revokes the supplied browser session and expires its cookie; harmless without one. |
POST /access/dashboard |
action=enable, id, password |
Creates the account. Re-enabling setup is open while both methods are explicitly disabled. An upgraded code-only installation additionally requires its existing code header. |
POST /access/dashboard |
action=update, id, password, current_password |
Requires the dashboard session and current password. Saves both the new ID and new password. |
POST /access/dashboard |
action=disable, current_password |
Requires the dashboard session and current password. Disable developer access first. |
POST /access/dashboard |
action=disable_all, current_password |
Turns off both methods after checking the dashboard session and current password. For a legacy code-only setup, supply its developer code header instead; no current password exists yet. |
POST /access/configure |
action=enable, rotate or disable |
Requires the dashboard session. Changes developer access; enable/rotate return the new code once. Dashboard login must already be enabled. |
Successful access operations return the status booleans, with code only on developer-code creation or rotation. enabled means either method protects the device. authenticated is true with a valid dashboard session, or while both methods are disabled. setup_required means an older developer code exists and a dashboard account still needs to be created.
Wrong dashboard login returns 401; a wrong current password while changing an account returns 403. Unknown actions or invalid fields return 400; configuration conflicts return 409; throttling returns 429. A busy password worker or unavailable access storage returns 503. Account/code changes are blocked during firmware maintenance or factory reset.
Disable All Security saves both methods as disabled, clears browser sessions and allows reachable clients to use the dashboard and API without credentials. This choice survives restart. Re-enabling dashboard login requires choosing an ID/password again. With both methods already disabled, disable_all is idempotent and persists the off choice.
Failed persistence returns 500. Single-record changes preserve their previous runtime configuration. Disabling both methods requires two durable saves: a partial failure may leave developer access off while dashboard login stays enabled, or retain the old developer guard on a legacy code-only installation. Refresh /access/status to see the actual state before deciding whether to retry. A failure does not silently open a previously protected device.
Never automatically repeat a timed-out enable, password change or code rotation. It may have committed despite the missing reply. Check status and sign in with the intended credentials before taking another action.
Dashboard password operations and developer-code requests have different costs. Checking X-Tyson-Access-Code does not run the dashboard's 600,000-round password derivation. Enable the developer code for a native integration and send it with each request; do not log into the dashboard before every state read.
| Operation | Measured behavior on firmware 3.10.4 |
|---|---|
| Custom login with a connected printer and HTTP telemetry polling | About 9 seconds |
| Change both ID and password with the same polling | About 18 seconds |
| Browser account change with browser telemetry plus extra HTTP polling | About 25 seconds |
Public default admin / admin login |
About 0.1 seconds; this uses the factory verifier shortcut |
These are observations, not timeout guarantees. For an integration that implements /access/login or password-changing /access/dashboard operations, allow up to 300 seconds, matching the dashboard client. The worker rejects an expired or disconnected job after its 240-second processing deadline/checkpoints. Normal developer-code state requests do not need this password-operation timeout. The controller has one password worker shared with page downloads; overlapping password work can return 503. Telemetry and control tasks continue, while new page/thumbnail downloads may report busy.
Serialize credential operations and wait for their response. Apply the returned Set-Cookie before reconnecting browser telemetry. If an update times out, check /access/status and attempt the intended credentials once when the controller is available; do not assume the old account remains active or automatically replay the update. A successful account update revokes old sessions, so their subsequent 401 is expected. Logout should await its server response before treating cookie removal as complete. Respect Retry-After on 429.
The host-only tyson_dashboard cookie has HttpOnly, SameSite=Strict, Path=/ and an absolute eight-hour lifetime. Reading state does not extend it. Random tokens are stored as hashes in RAM and disappear on restart. Up to eight browser sessions are retained; a ninth login replaces the oldest.
Sign out revokes that browser session. Changing or disabling the dashboard account revokes its old browser sessions, including their open WebSockets and queued private output. Changing the developer code revokes native code grants while preserving dashboard sessions. A browser sign-out does not revoke separate developer connections. Reconnect and read fresh state; never replay queued heater commands.
Revocation prevents subsequent authorization and queued live output. It does not undo a previously accepted command or cancel an accepted firmware/calibration transfer. Account and code operations do not change heater targets or outputs.
HTTP and WebSocket traffic remain unencrypted. Passwords, developer codes and sessions require a trusted local network; this feature does not add HTTPS. Cookie attributes follow documented browser behavior and do not encrypt traffic.
Firmware 3.9.0 used one code for both browser and API access. Its developer code survives the upgrade. If it was enabled, the browser shows Set Up Dashboard Login: enter that existing developer code once and create a separate ID/password. Existing developer integrations continue using their code. Old tyson_lan browser cookies are not accepted. Previously unprotected installations without a saved dashboard setting receive the admin / admin account. Once security is explicitly disabled in 3.10.0, that choice survives restart.
For a lost developer code, sign in with the dashboard password and generate another. For a lost dashboard password, a session alone cannot replace it: account changes require the current password. On firmware 3.10.12 or later, hold physical BOOT for five seconds until the NeoPixel lights blue, then release before twenty seconds to restore dashboard login admin / admin and revoke old browser sessions. Other settings and developer access are preserved. A continuous twenty-second hold performs full factory reset with a five-second red indication, erasing Wi-Fi, printer settings, custom calibration and developer access. Each color lasts five seconds. Firmware 3.10.11 uses a twelve-second hold; older firmware uses a five-second hold for full factory reset. There is no unauthenticated remote password-reset endpoint.
Source basis: main/dashboard_password.h, main/lan_access.cpp, main/lan_access_policy.h, main/access_form.h, main/main.cpp and main/web/app.js.