Skip to content

Connect: a disconnect reaches the application, and one session per application - #1121

Merged
cryptskii merged 1 commit into
mainfrom
fix/connect-disconnect-reaches-the-app
Oct 12, 2026
Merged

cryptskii merged 1 commit into
mainfrom
fix/connect-disconnect-reaches-the-app

Conversation

@cryptskii

Copy link
Copy Markdown
Collaborator

A grant ends when the player disconnects the application (DSM Amendment A11, MR-DSM-0292). Two things broke that on the phones:

  • The disconnect never reached the application. connect.disconnect ended the wallet's own row and told no one. The application's account hard-coded every session as Connected (app_session_view), so a game played on with a wallet that had left, and its next request waited on a session that no longer existed.
  • Sessions stacked. connect.approve refused a second approval only for the same session id. Each new code is a new offer, so scanning again opened a second session to the same application beside the first.

The change

  • The wallet tells the application. connect.disconnect posts the wallet's signed notice over the application's relay: an AppResponseV1 at seq = 0, which answers no request (requests count from 1). Nothing new on the wire. The session is ended on the wallet first, reachable or not; an unreachable application is told at the next connect.sync (the notice is kept in connect_log unsent, and sync delivers what a disconnected session still owes before skipping it).
  • The application holds the status. connect.app.respond ends the session on the notice, connect.app.request refuses an ended session, and connect.app.sessions reports the real status. connect_app_sessions gains a status column; the client database schema moves to 31 (beta policy: reset, no migration).
  • One session per application. connect.approve refuses when a connected session to the same app_device_id exists: "disconnect it first to connect again". The Apps screen already shows that text.

Verification

  • dsm_sdk::handlers::connect_e2e_tests 10/10 (two new: the notice over the relay, the application's status and refusal, a second approval refused while a session stands and a fresh one after; the relay down at the disconnect with the kept notice going at the sync).
  • dsm_app_host::real_connection::a_game_and_a_wallet_connect_over_the_real_relay now asserts the application lists the session disconnected and refuses the payment it would ask (previously the request was accepted and left unanswered).
  • Mutations, each red at its test: the notice taken in but the session left connected; the one-session guard removed.
  • make lint, the real-code guard and ci/conformance_evidence.py pass. VERIFICATION_MATRIX.md MR-DSM-0292 extended with the new gates, tests and mutations.
  • Pre-existing, unrelated: bluetooth::android_ble_bridge::tests::pairing_smoke_identity_before_connection fails the same way on untouched main.

In Wildstate

The game server already checks sessions every five seconds (cryptskii/wildstate-dsm#13) and kicks a page whose session is disconnected. It could never fire until this, because the application's account always said "connected".

…plication

A grant ends when the player disconnects the application (DSM
Amendment A11, MR-DSM-0292). The wallet ended its own row and told no
one: the application's account answered every session as connected,
whatever the wallet had done, so a game played on with a wallet that
had left and its next request waited on a session that no longer
existed. And approving a second code from an application already
connected stacked a second session beside the first.

- connect.disconnect posts the wallet's signed notice to the
  application over its relay: an AppResponseV1 at seq 0, which answers
  no request (requests count from 1). The session is ended on the
  wallet first, whether or not the application can be reached; an
  unreachable one is told at the next sync (the notice is kept in
  connect_log, unsent, and connect.sync delivers what a disconnected
  session still owes before skipping it).
- The application's side holds the status: connect.app.respond ends the
  session on the notice, connect.app.request refuses an ended session,
  and connect.app.sessions reports the real status instead of
  Connected unconditionally. connect_app_sessions gains a status
  column; the client database schema moves to 31 (beta: reset, no
  migration).
- connect.approve refuses a second session to an application already
  connected (session_with_app): disconnect it first to connect again.

Two Connect e2e tests cover the notice over the relay, the status, the
refusal, a second approval refused and a fresh session after; and the
relay down at the disconnect with the notice going at the sync. The
app host's real-connection test now asserts the application's status
and its refusal after the disconnect. Mutations: the notice taken in
but the session left connected, and the one-session guard removed,
each red at its test.
@cryptskii
cryptskii merged commit f160d22 into main Oct 12, 2026
25 of 27 checks passed
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.

1 participant