- Shipped
- September 5, 2026 at 5:40 PM UTC
- Author
- Kamo
- Commit
- e50a448
KamoMobile was signing itself out at random, most visibly across app updates, and the exchange endpoint was doing it. Three separate ways: The secret rotated FIRST, before the user lookup, the membership resolution, the DNS gate and the provider-org check — any of which can refuse the exchange. A 403 from those, or a 500, returned no new secret while the old one had already been superseded. The member lost their enrolment to a check that was never about the device. A secret presented from the previous slot advanced the chain, so the slot came to hold a value the handset had never been told. One lost response was survivable; two locked the device out for good. An app update that kills the process mid-exchange is exactly how a response gets lost, which is why this showed up as "signed out after updating". The new test fails on attempt 2 against the old behaviour. And the grace on that slot was sixty seconds, which assumed a client always receives what it asked for. It is thirty days now, kept honest by retiring the old secret the moment the client proves it got the new one — so a delivered rotation still invalidates its predecessor at the very next exchange, and the window only stays open for a device that is not being used. validateAndRotate is split into validate (no side effects) and commitUse (called last, once the response is certain), and a 401 now carries a machine-readable reason so the app can tell a revoked device from a proxy error page.