Signing Out
1. User submits the sign-out form (landing nav, /settings,
or the backoffice sidebar)
│
v
2. signOutAction() in app/components/actions.ts
│
├──► DELETE /internal/auth-adapter/sessions/:token
│ (explicit, and throws if it fails; the row is what
│ the backend validates bearers against)
│
└──► signOut({ redirectTo: '/' })
→ clears the session cookie
→ asks the adapter to delete the row a second time,
which answers 200 `success: false` and is a no-op
│
v
3. Redirect to /
Why the delete is explicit rather than left to signOut(). @auth/core catches an adapter failure inside signOut(), logs it, and clears the cookie anyway (node_modules/@auth/core/lib/actions/signout.js). Left alone that reports success while the session row survives for its full lifetime, and the user's cookie is now gone, so they can never trigger the delete again: an unrevokable live token. Server-side revocation is the whole reason to run database sessions instead of JWTs, so sign-out fails closed instead. A failed delete throws, the cookie and the row both stay, and the user can retry.
SignOutButton (app/components/SignOutButton.tsx) is the one control, reused by all three surfaces with a caller-supplied className. It is a form post, not a link: sign-out mutates state, so it must not be reachable by a cross-site GET. Nothing about it is client-side, so it still works with JavaScript disabled.
deleteAccountAction calls signOut() directly, without the explicit delete above. Account deletion is queued rather than immediate (POST /users/me/delete-account returns { queued: true }), and the worker clears that user's sessions when it runs.