Skip to content

[Defect][Device] Browser reload/tab close still results in LOCKED Device #204

Description

@miso-develop

Parent / governing decisions

Owner

  • ACTIVE_ROLE: implementation
  • DOMAIN: device

State

READY_FOR_IMPLEMENTATION

Defect report

Physical use after #196 still observes the undesired behavior:

  1. Connect the Web app to the Device.
  2. Complete Trusted Browser Unlock and confirm the Device is UNLOCKED.
  3. Close the browser tab or reload the page.
  4. The Device is subsequently observed as LOCKED.

This contradicts the approved contract:

  • browser/page/Web Serial lifecycle teardown must not implicitly Lock a still-running Device;
  • same physical runtime may remain UNLOCKED;
  • only explicit Lock, configured automatic-lock expiry, reboot/actual power loss, or another approved Device security boundary destroys the active VMK.

A USB cable removal that leaves the StickS3 running on battery has been observed by the human owner to preserve UNLOCKED, reinforcing the desired behavior.

Current static evidence

Specification re-checked current main = b3aa8fc0cc06143990dcf32a971853264f73eba8.

Web

web/src/browser-lifecycle.ts:

  • page lifecycle cleanup calls management.disconnectTransport() when management exists;
  • otherwise closes only the raw serial session;
  • no device.lock request is present.

CanonicalDeviceManagement:

  • close() sends device.lock and is the explicit Lock path;
  • disconnectTransport() clears browser-side management state and calls transport close() only.

SerialSession.close():

  • cancels/releases stream locks and closes the Web Serial port;
  • it does not send a Protocol-v2 device.lock.

Device

Production USB disconnect handling in firmware/main/app_main.cpp calls:

  • protocol.disconnect();
  • transport PRE_HANDSHAKE reset.

CanonicalProtocolV2Handler::disconnect():

  • disconnects protocol/session state;
  • cancels pending VMK delivery/reset presence;
  • does not call runtime_.lock().

The normal runtime_.lock() path is reached through approved security boundaries such as explicit device.lock or automatic-lock expiry.

Therefore the source-level intended contract appears correct. The observed physical outcome requires runtime diagnosis rather than another speculative Web-only change.

Objective

Determine why tab close/reload results in a physically/reconnect-observed LOCKED Device despite the non-locking source contract, then fix the actual causal path while preserving #195.

The diagnosis must distinguish at least:

  1. an unexpected device.lock request;
  2. a Device reboot/reset caused or correlated with Web Serial/page teardown;
  3. an unintended Device security-boundary call;
  4. automatic-lock expiry;
  5. stale/incorrect UI interpretation where the Device did not actually Lock;
  6. old/stale Web or firmware build behavior not matching current main;
  7. another transport/browser lifecycle interaction.

Reproduction / evidence requirements

A. Establish exact versions

Before changing code, record non-secret identities:

  • exact Web build/commit served by the tested page;
  • exact Device firmware/build commit from normal hello;
  • configured automatic-lock setting/state;
  • browser version used for the physical reproduction.

Do not record Device ID, account metadata, OTPs, TOTP secrets, Vault identifiers, Recovery data, keys, or credential-bearing logs.

If the physical Device firmware is not a build containing the current lifecycle contract, reproduce again with the intended current candidate before claiming a current-main regression.

B. Separate the lifecycle cases

Using the same unlocked Device/runtime where practical, characterize independently:

  • page reload;
  • tab close;
  • navigation away;
  • browser process close, if safely reproducible;
  • explicit Lock & Disconnect as positive control;
  • direct transport-only disconnect through the application's non-locking path, if a diagnostic harness can invoke it safely;
  • USB cable removal while battery keeps Device running as physical control.

For each case record only:

  • Device state before action;
  • whether display/runtime remains alive continuously;
  • Device state immediately after;
  • reconnect hello.state;
  • whether a fresh Unlock physical-confirmation request is required.

C. Distinguish Lock from reboot/reset

A tab/reload observation must not be classified merely from the reloaded Web UI.

Use the least-invasive non-secret evidence available to determine whether the Device:

  • remained in the same boot/runtime and changed to LOCKED; or
  • rebooted/reset, thereby correctly losing RAM-only VMK.

Prefer existing/sanitized diagnostics. If additional diagnostic instrumentation is necessary, it may expose only non-secret lifecycle evidence such as:

  • boot/reset counter/reason;
  • monotonic boot continuity marker;
  • whether an explicit Device Lock operation was accepted;
  • transport connect/disconnect transitions;
  • security-boundary reason enum/counter.

It must not expose VMK/key material, Vault contents/IDs, account metadata, credentials, Passphrases, OTPs, session private data, or raw protocol secret payloads.

Production diagnostic expansion requires normal security review if it becomes user-accessible; isolated diagnostic builds are preferred during root-cause work.

D. Check Web request boundary

Prove whether a device.lock Protocol request is emitted in each lifecycle case.

The existing mock regression from #196 is necessary but no longer sufficient because the physical behavior disagrees.

If browser teardown itself cannot reliably complete asynchronous logging, use a bounded local test harness/transport spy that records only operation names, never parameters or secret-bearing payloads.

E. Check Device boundary

Instrument/test the Device boundary sufficiently to prove which event, if any, changes runtime from UNLOCKED to LOCKED.

At minimum distinguish:

  • explicit device.lock;
  • automatic-lock expiry;
  • reboot/reset;
  • other lock_security_boundary() caller.

Do not weaken automatic-lock or other legitimate fail-closed boundaries to make the test pass.

Fix policy

If Web sends an unexpected explicit Lock

Remove that implicit path while preserving:

  • explicit Lock & Disconnect;
  • browser transient-secret cleanup;
  • best-effort transport/resource release.

If Web Serial close/reload causes Device reboot/reset

Do not disguise the reset as a Web-state issue.

Determine the causal browser/USB/Device path and apply the narrowest fix. If avoiding an explicit port.close() during page teardown is necessary, prove:

  • browser reload/tab-close still releases ownership sufficiently for reconnect;
  • no serial resource/permission deadlock is introduced;
  • explicit user disconnect remains deterministic;
  • no security boundary is weakened.

If the reset is unavoidable platform behavior, return to Specification with physical evidence before changing the product lifecycle expectation.

If Device incorrectly locks on transport/session disconnect

Separate session teardown from VMK runtime lifetime per #195. Keep:

  • pending session/authorization cancellation;
  • framing/session fail-closed behavior;
  • explicit Lock;
  • automatic Lock;
  • reboot/power-loss lock semantics.

If stale build is the cause

Do not create unnecessary production changes. Record exact stale/current identities, confirm the current intended build physically satisfies #195, and close this Defect with evidence.

Tests

Add/extend regression coverage for the causal fix.

Required end-state tests include:

  • lifecycle page teardown does not issue device.lock;
  • transport disconnect does not call Device runtime lock;
  • explicit Lock & Disconnect still locks;
  • automatic-lock expiry still locks;
  • reboot/power-loss model still starts LOCKED;
  • reconnect to same still-running runtime after non-locking teardown reports UNLOCKED;
  • any fixed browser/serial teardown path can reconnect without stale port ownership;
  • transient browser secrets are still cleared.

Where feasible add a Chrome/browser integration test covering actual reload/port teardown behavior in addition to mocks.

Acceptance

  • Exact physical reproduction has been characterized with Web + firmware build identities.
  • Root cause distinguishes explicit Lock vs reboot/reset vs other security boundary vs stale UI/build.
  • Tab close and reload do not cause Device Lock when the same physical Device runtime continues.
  • Reconnect after such teardown reports UNLOCKED without a new Unlock confirmation.
  • USB data disconnect while battery keeps the same runtime alive remains non-locking.
  • Explicit Lock & Disconnect still destroys the unlocked session.
  • Automatic-lock expiry remains unchanged.
  • Actual reboot/power loss still destroys RAM-only VMK and starts LOCKED.
  • Browser transient-secret cleanup remains.
  • No Protocol/Vault/credential/eFuse relaxation is introduced.
  • Foundation/Security/relevant Web+firmware tests are green.
  • Physical Human Gate verifies reload and tab-close behavior on the exact fix head.

Collision / coordination

At Specification time there are no open PRs, but #203 is a newly-open Web Task without a PR yet and is expected to touch shared Web layout rather than lifecycle/serial code.

Device Implementation owns this Defect initially because current static Web and Device contracts both appear non-locking and the unresolved question is the physical Device state transition/runtime continuity.

If evidence proves the causal fix is exclusively Web-side, persist the finding here and hand off to Web Implementation rather than silently crossing domains.

Re-check current Issues/PRs/branches before claim.

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions