- Only emit pm_invite when target_sid exists (real user)
- Violet has no socket session, so emitting to None would broadcast
to all connected clients, leaking who is chatting with the AI
- Payment endpoint no longer uses @_require_auth (not client-callable)
- Identifies user from webhook payload user_id instead of client JWT
- Removed hardcoded payment secret from chat.js
- Client now shows placeholder message directing to admin
- Webhook secret + user_id must come from payment provider server
- Track pending PM invitations per socket session
- pm_accept now rejects room joins unless user has a valid invite
- Clean up pending invites on disconnect
- Prevents eavesdropping on other users' PM conversations
- Create config.py with shared constants, AES-GCM helpers, and JWT helpers
- app.py and routes.py now import from the single source of truth
- Eliminates JWT secret mismatch (routes.py had hardcoded default)
- Removes all duplicate _issue_jwt, _verify_jwt, _aesgcm_encrypt,
_aesgcm_decrypt definitions
- start.py also uses shared config loader