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:

  1. Transport — SignalR, long-poll, or external service
  2. Message store — legacy tables, new Cube-2.0-owned tables, or external
  3. 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 AddJwtBearer for 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_at

Idempotent 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 itar claim; 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.