Skip to content

Improve PIN pairing window - #130

Merged
maximmaxim345 merged 2 commits into
mainfrom
ap/improve-pin-pairing-window
Aug 5, 2026
Merged

Improve PIN pairing window#130
maximmaxim345 merged 2 commits into
mainfrom
ap/improve-pin-pairing-window

Conversation

@arturpragacz

Copy link
Copy Markdown
Contributor

Improve PIN pairing window.

Unifies PIN-pairing consent around the pairing window and replaces terminal lockout with escalation.

  • Pairing window as the single consent primitive. The window (previously static-PIN-only) now gates any gesture-gated attempt: every static_pin attempt, and dynamic_pin attempts when the method is escalated or the session's PIN is shorter than 6 digits.
  • Lockout → escalation. The failure counter (now dynamic_pin-only) no longer ends in terminal lockout: at 10 failures the method is escalated — every attempt becomes gesture-gated until a successful round resets it. The locked_out field (descriptor & config) and abort reason are gone; get-pairing-config reports escalated instead. Static PIN needs no counter: each attempt already costs a window.
  • New client/pair-pending. Signals that an attempt is gesture-gated and awaiting a window, without starting the attempt; index-gated like client/pair-init.
  • New management/open-pairing-window. Lets a paired server open the window in place of the operator gesture.
  • server/activate shape. selected_pair_method becomes a pairing object; pin_length moves from server/pair-init into the activation so the client can validate it and decide gating before anything starts.
  • Method advertisement & provisioning. supported_pair_methods is clarified to list only currently-offered (enabled) methods. Out-channel devices SHOULD ship static_pin too — disabled, with no PIN provisioned; enabling it without a configured (or simultaneously supplied) PIN is rejected.

@maximmaxim345 maximmaxim345 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, changes look good to me.
Really nice to not have a lockout anymore!

@maximmaxim345
maximmaxim345 merged commit 5b0e646 into main Aug 5, 2026
1 check passed
@maximmaxim345
maximmaxim345 deleted the ap/improve-pin-pairing-window branch August 5, 2026 12:08
maximmaxim345 added a commit to Sendspin/aiosendspin that referenced this pull request Aug 10, 2026
Improve PIN pairing window.

Implements Sendspin/spec#130.

## Breaking changes

This PR implements breaking changes made to protocol. However, those
only touch encryption/pairing related parts of the Spec. Legacy clients
will still keep working as before.

Client SDK: `pin_display` and `pairing_window` on `SendspinClient`
become a single `pairing_support=PairingSupport(...)`, and the SDK now
owns the window lifetime, opened via `open_pairing_window()` instead of
a caller-supplied awaitable. `ClientPairingStore`'s failure-counter
methods drop their `method` argument and `is_pin_locked_out()` becomes
`is_pin_escalated()`. The persisted `pin_failures` mapping becomes an
integer, migrated on load.

Server SDK: pairing timeouts now raise `PairingTimeoutError` rather than
a bare `TimeoutError`.
maximmaxim345 added a commit to Sendspin/sendspin-js that referenced this pull request Aug 11, 2026
Aligns the client with the new pairing spec changes and moves the E2E
dependency to aiosendspin 9.0.0.

On the Spec side, this PR implements:
- Sendspin/spec#129
- Sendspin/spec#130
- Sendspin/spec#131
- Sendspin/spec#132.


## Pairing

`server/activate` now carries a `pairing` object (`method`,
`pin_length`, `languages`) in place of `selected_pair_method`, and
`pin_length` moved out of `server/pair-init` so the client validates it
before an attempt starts. Terminal lockout is gone: a single dynamic-PIN
failure counter escalates the method to gesture-gating at ten failures
and de-escalates on the next verified round, so repeated wrong PINs can
no longer leave a device permanently unpairable. A gesture-gated attempt
signals `client/pair-pending` and waits for `openPairingWindow()` rather
than closing the connection after five minutes, and a window opened
before the server asks now survives a reconnect instead of being
silently discarded.

Clients can advertise where the operator finds each static secret
through `locations`, and which channels convey the dynamic PIN through
`out_channels`. A server's spoken-PIN language preference reaches the
app as a second argument to `onPairingPin`; the sample player uses it to
read the PIN aloud in the operator's language.

## Breaking changes

`isPairingLockedOut()` and `clearPairingLockout()` are removed in favour
of `isDynamicPinEscalated()`, since escalation has no operator exit
other than a successful round. `onPairing` gained a `pending` event and
`onPairingPin` a second `languages` argument. New optional config:
`pinOutChannels`, `staticPinLocations` and `pairingPskLocations`, all
omitted from `client/hello` when unset.
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