ADR 0007 — MX Chat architecture#
Status: Proposed (Phase 1, 2026-09-16) — awaiting product input before implementation Depends on: 0001 — Authentication model, 0004 — Database migration strategy
Context#
BFC-7 in the Phase 1 roadmap lists MX Chat MVP as one of the core-security modules to deliver. The sub-task (BFC-27) captures three unresolved decisions before we can start:
- Transport — SignalR, long-poll, or external service
- Message store — legacy tables, new Cube-2.0-owned tables, or external
- Feature scope — DMs vs. group channels, attachments, presence, read receipts
The legacy schema has no chat table. The closest surface is the Notes system (WmUserNote, WmNoteWhisperUser, WmNoteRelationship, wmNoteReminders), which is a note-attached-to-a-business-record with per-user visibility and reply threads. Notes are not real-time; they’re audit/discussion trails on entities like sales orders, work orders, RMAs. That’s not what “MX Chat” should be — MX Chat is meant to be a peer chat between users.
This ADR proposes a concrete direction so we can commit story points once product confirms.
Proposed decision#
Transport: SignalR (ASP.NET Core native)#
- Ships with the framework, integrates with
AddJwtBearerfor auth - No new dependency, no external service
- Falls back to long-poll automatically for restrictive networks
Message store: Cube-2.0-owned tables#
Three new tables under MnxChat* prefix, following the ADR 0006 pattern (additive, no legacy conflict):
MnxChatChannel -- id, kind (DM|Group), name?, created_at, created_by
MnxChatChannelMember -- channel_id, user_id, joined_at, last_read_at
MnxChatMessage -- id, channel_id, author_user_id, body, sent_atIdempotent SQL delta ships alongside the API. Legacy Notes system stays untouched — MX Chat is a parallel, real-time surface.
MVP scope (proposed cut)#
In:
- 1:1 DMs and named group channels
- Text messages (plain, no markdown)
- History fetch (paginated)
- Live delivery via SignalR
[Authorize]on the hub (JWT); membership check on channel operations
Out (deferred to Phase 2+):
- Attachments (files, images)
- Read receipts
- Typing indicators
- Presence (online/offline)
- Message editing / deletion
- Search
- Notifications outside the app
Scaling#
- Single-instance web app for staging + initial prod. SignalR in-memory backplane is sufficient.
- If we later scale horizontally, add a SignalR backplane (Redis or Azure SignalR). This is a config-only change; the API contract doesn’t move.
Product input needed#
Before implementation, product needs to confirm:
- Naming: is “MX Chat” the shipping name?
- DM vs. Group priority: MVP could be DMs-only if easier to ship — do we need group channels day one?
- Retention policy: how long do messages live? Any legal / compliance constraints?
- ITAR: do MX Chat messages fall under ITAR scoping? If yes, we filter channels/messages by the
itarclaim; if not, no extra work. - Notifications: is a real-time in-app badge enough for MVP, or do we need email / push?
Consequences#
- Story-point estimate lands after product confirms scope. Placeholder is 13 pts (BFC-28) on the assumption of DMs + group channels + history + live delivery.
- New EF entities + SQL delta land in the same PR as the SignalR hub — one atomic delivery, easier to review and roll back.
- Notes system is unaffected. If down the road we want to bridge notes into chat (e.g. “post a note comment to the record’s chat channel”), that’s a Phase 2+ integration.
What we deliberately don’t do#
- Don’t reuse the Notes tables for chat. They model per-record discussion, not peer messaging. Bolting chat semantics onto them would leak business-record semantics into UX.
- Don’t pull an external chat service (Ably, Pusher, SendBird). Extra vendor, extra billing, extra data-residency and compliance discussions — none of it justified until we’ve grown past what SignalR native can do.
- Don’t build presence into the MVP. Presence tracking has non-trivial state and reconnect edge cases; can be added later without breaking the message-store contract.