Skip to content

feat(split): optionally clear stale split bond on PIN/key mismatch#3377

Open
akira-toriyama wants to merge 1 commit into
zmkfirmware:mainfrom
akira-toriyama:feat/auto-unpair-on-key-mismatch
Open

feat(split): optionally clear stale split bond on PIN/key mismatch#3377
akira-toriyama wants to merge 1 commit into
zmkfirmware:mainfrom
akira-toriyama:feat/auto-unpair-on-key-mismatch

Conversation

@akira-toriyama

@akira-toriyama akira-toriyama commented Jun 4, 2026

Copy link
Copy Markdown

What

Add CONFIG_ZMK_SPLIT_AUTO_UNPAIR_ON_KEY_MISMATCH (off by default,
depends on ZMK_SPLIT_ROLE_CENTRAL). When enabled, a split central
that receives BT_SECURITY_ERR_PIN_OR_KEY_MISSING from a peripheral
calls bt_unpair() + bt_conn_disconnect() so the next advertising
window re-pairs from scratch.

Why

In a split setup (one central + N peripherals), the central's bond
record for one peripheral can become invalid while the peripheral
still holds the old LTK. Once that happens the next reconnection
sits in an infinite PIN_OR_KEY_MISSING loop and cannot recover
without manual intervention — which on a dongle-style central is
particularly painful because the dongle has no keymap from which to
issue any recovery behavior.

Concrete situations that produce this state:

  • A settings_reset UF2 is flashed on the central (or, less
    commonly, on the peripheral) while the other side keeps its bond.
  • The central hardware is swapped (e.g. a different XIAO BLE) while
    the peripherals are kept as-is.
  • USB-level reset of the dongle — observed on hardware as one of
    the two peripherals occasionally landing in this state on KVM
    switch-over and recovering automatically once the patch is active
    (I can't fully attribute the KVM trigger to NVS state yet, only
    that the symptom is the same PIN_OR_KEY_MISSING loop and the
    same bt_unpair() clears it).

In all three the symptom is the same: the central's LTK is gone or
shifted for that peripheral, the peripheral still replays the old
LTK, and the link cannot complete without user action.

Scope and gating

  • Strictly the split central path: gated on
    IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL). The new behavior
    cannot fire on the central ↔ host pairing path.
  • Strictly opt-in: ZMK_SPLIT_AUTO_UNPAIR_ON_KEY_MISMATCH is
    off by default. Clearing a bond is destructive; only deployments
    that are known to benefit (typically dongle setups) should enable
    it.
  • Strictly the exact error code
    BT_SECURITY_ERR_PIN_OR_KEY_MISSING. Other security failures are
    left alone.

Testing

Verified on hardware via akira-toriyama/canon (Cyboard Imprint:
XIAO BLE dongle as central, both halves as peripherals):

  • Builds clean on xiao_ble/nrf52840/zmk + imprint_dongle.
  • With the flag off, existing behavior is byte-identical.
  • With the flag enabled, dongle + peripherals operate as before
    under normal use, and a previously-reproducible failure where
    one peripheral half stopped responding after a KVM USB switch-over
    no longer reproduces in the user's setup. A few switch cycles
    tested; not claimed as exhaustive given KVM-USB enumeration
    variability.

Related

  • Bluetooth security failing on windows machine #805 — same Security failed: ... err 2 =
    BT_SECURITY_ERR_PIN_OR_KEY_MISSING pattern in a different
    (host-side) scenario. This PR doesn't claim to close it; the
    split flag is unrelated to the host pair path by design.

Notes for maintainers

  • Happy to rename (ZMK_SPLIT_DROP_STALE_BOND, etc.) if there's a
    preferred convention.
  • I considered also covering the symmetric peripheral-side case
    (peripheral keeps its bond, central LTK shifted) but left it out
    of this PR to keep the scope tight. Happy to follow up if you'd
    like it included here.

@caksoylar

caksoylar commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

I am confused about the scope of this change. Is this for

  • ZMK centrals pairing (as peripherals) to hosts/computers? If so, the dongle comments don't make sense.
  • ZMK peripherals pairing to ZMK centrals, for splits? If so, comments like "link
    never recovers without manual &bt BT_CLR on the peripheral" or "for example when the
    central is re-flashed but the peripherals are not" don't make sense, since (a) you cannot clear split bonds with the &bt behavior, (b) flashing firmware on central won't clear bonds, unless you are flashing a special one like settings_reset

Add CONFIG_ZMK_SPLIT_AUTO_UNPAIR_ON_KEY_MISMATCH (off by default,
depends on ZMK_SPLIT_ROLE_CENTRAL). When enabled, a split central
that receives BT_SECURITY_ERR_PIN_OR_KEY_MISSING from a peripheral
calls bt_unpair() + bt_conn_disconnect() so the next advertising
window re-pairs from scratch without manual user action.

In a split setup the central's bond record for one peripheral can
become invalid while the peripheral still holds the old LTK. Once
that happens the next reconnection sits in an infinite
PIN_OR_KEY_MISSING loop and cannot recover without user action.
Dongle-style centrals suffer the most because they have no keymap
from which to trigger recovery behaviors.

The flag is off by default because clearing a bond is destructive.
The check is gated on CONFIG_ZMK_SPLIT_ROLE_CENTRAL so it cannot
fire on the central <-> host pairing path, and only matches the
exact PIN_OR_KEY_MISSING error code.

Related: zmkfirmware#805
@akira-toriyama
akira-toriyama force-pushed the feat/auto-unpair-on-key-mismatch branch from 51b7e76 to 00d14f8 Compare June 5, 2026 00:17
@akira-toriyama akira-toriyama changed the title feat(ble): optionally clear stale bond on PIN/key mismatch feat(split): optionally clear stale split bond on PIN/key mismatch Jun 5, 2026
@akira-toriyama

Copy link
Copy Markdown
Author

Thanks for the careful read — your second bullet is the intent, and you've correctly flagged two factual errors in my body. I've just force-pushed an update; here's the summary.

Scope: split central ↔ peripheral pairing. To make that unambiguous in the code I renamed the Kconfig to CONFIG_ZMK_SPLIT_AUTO_UNPAIR_ON_KEY_MISMATCH and gated it on CONFIG_ZMK_SPLIT_ROLE_CENTRAL, so the new behavior cannot fire on the central ↔ host pairing path.

On the inaccuracies you spotted:

  • &bt BT_CLR doesn't touch split bonds — agreed, dropped.
  • A normal west flash / UF2 overwrite doesn't wipe NVS, so "re-flashing the central wipes its bond" wasn't a realistic trigger — agreed. The real situations that produced a one-sided stale bond in our dongle setup were:
    • flashing a settings_reset UF2 on one side (intentionally or otherwise),
    • swapping the central hardware while keeping the existing peripherals,
    • and, observed on hardware: a KVM USB switch reset of the dongle that occasionally left one peripheral's split link wedged on Security failed ... err 2 until the patch kicked in (I can't fully attribute the KVM trigger to NVS state yet, only that the symptom matches and bt_unpair() cleared it).

In all three, the central's view of one peripheral's bond is gone or shifted, the peripheral keeps replaying the old LTK, and the link sits in PIN_OR_KEY_MISSING until somebody intervenes.

Updated body and a cleaner testing section are pushed. Marking ready-for-review.

@akira-toriyama
akira-toriyama marked this pull request as ready for review June 5, 2026 00:17
@akira-toriyama
akira-toriyama requested a review from a team as a code owner June 5, 2026 00:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants