Skip to main content

Team Messaging (Mattermost Integration)

Status: DRAFT Priority: P2 Last Verified: 2026-03-29 Spec Author: Claude (automated)

1. Problem Statement / Feature Purpose

Equa organizations need integrated team messaging to communicate in real-time without leaving the platform. The messaging feature embeds Mattermost Team Edition inside the Equa web app, with automatic user provisioning, organization-scoped teams, and agent (Equabot) interaction via dedicated channels. Key benefits:
  • Team members communicate within their organization context
  • No separate account creation — users are provisioned on first access
  • Equabot agent is reachable via the #agent channel
  • Platform events (share issuance, document uploads, member changes) post notifications to relevant channels

2. Current State

Frontend (equa-web) — NOT YET IMPLEMENTED

The frontend messaging module does not exist in the codebase as of 2026-03-29. The component table below describes the planned implementation, not current state. The src/modules/messaging/ directory has not been created.
Planned components:
ComponentPlanned PathPurpose
MessagingPagesrc/modules/messaging/pages/messaging-page.tsxPage container with loading/error/embed states
MattermostEmbedsrc/modules/messaging/components/mattermost-embed.tsxiframe embedding Mattermost UI with token auth
MessagingLoadingsrc/modules/messaging/components/messaging-loading.tsxSpinner during session provisioning
MessagingErrorsrc/modules/messaging/components/messaging-error.tsxError state with retry button
useMattermostSessionsrc/modules/messaging/hooks/use-mattermost-session.tsHook that calls session endpoint, manages token lifecycle
mattermost-apisrc/modules/messaging/services/mattermost-api.tsHttpClient wrappers for Mattermost API calls
Planned routing changes:
ChangeTarget FileDescription
Path constantsrc/logic/paths.tsAdd messagingPath
Route registrationsrc/app/routes/routes.tsAdd lazy-loaded /:orgId/messaging route
Navigation linksrc/app/components/header/shared/organization-links.tsxAdd “Messaging” to org nav
Existing placeholder text (marketing copy only, no functional code):
Source: equa-web/src/app/components/header/shared/organization-links.tsx, Line: 125 — “Interact with your organization using our communication system.” Source: equa-web/src/modules/auth/components/carousel.tsx, Line: 117 — Onboarding carousel mentions “Notification System”

Backend (equa-server)

ModuleFile PathPurpose
Endpointsequa-server/modules/agent/src/mattermost/mattermost-endpoints.ts, Line: 144 Express routes for session, config, mapping, sync
REST Clientequa-server/modules/agent/src/mattermost/mattermost-client.ts, Line: 7Thin wrapper around Mattermost REST API v4
Provisioningequa-server/modules/agent/src/mattermost/mattermost-provisioning.ts, Line: 7User/team/channel provisioning logic
Notificationsequa-server/modules/agent/src/mattermost/mattermost-notifications.ts, Line: 9Platform event to channel post bridge
Typesequa-server/modules/agent/src/mattermost/types.tsShared interfaces and default channel config
Member Syncequa-server/modules/organizations/src/mattermost-sync.tsAsync member add/remove sync
Member sync hooks:
Source: equa-server/modules/organizations/src/writing.ts, Line: 173 (onMemberAdded) Source: equa-server/modules/organizations/src/writing.ts, Line: 305 (onMemberRemoved)

Agent Bridge (equabot-gateway)

ModuleFile PathPurpose
Mattermost Extensionequabot-gateway/extensions/mattermost/Existing bot auth, WebSocket monitoring, message routing
Org Resolutionequabot-gateway/extensions/equa-platform/src/dispatch.ts, Line: 91Maps Mattermost teamId to Equa organizationId

Data Flow

equa-web (user navigates to /messaging)
  |
  | POST /api/v1/mattermost/session { organizationId }
  v
equa-server
  | 1. Verify session cookie + org membership
  | 2. Lookup or create Mattermost user (POST /api/v4/users)
  | 3. Issue personal access token (POST /api/v4/users/{id}/tokens)
  | 4. Ensure org team exists + membership
  | Response: { token, baseUrl, teamName, mmUserId }
  v
equa-web
  | <iframe src="chat.equa.cc/login/token?token=xxx&redirect_to=/team">
  v
Mattermost Server (chat.equa.cc)
  | User sends @equabot message in #agent channel
  | WebSocket event -> gateway monitor
  v
equabot-gateway (Mattermost extension)
  | resolveAgentRoute() with teamId
  | equa-platform resolves orgId via GET /api/v1/mattermost/team-mapping/{teamId}
  | Agent processes with org-scoped tools
  | sendMessageMattermost() -> response in channel
  v
Mattermost -> displayed in equa-web iframe

3. Target State / Complete Description

The messaging feature provides organization-scoped team chat with:
  • Auto-provisioning: First visit creates Mattermost user, team, default channels, and membership
  • Seamless auth: No separate login — equa-server issues personal access tokens mapped to the Equa session
  • Default channels per org: #general (auto), #announcements, #cap-table, #documents, #agent
  • Platform notifications: Share issuance, document uploads, and member changes post to relevant channels
  • Agent integration: Messages in #agent mentioning the bot are routed through equabot-gateway to the org-scoped agent
  • Member lifecycle sync: Adding/removing org members in Equa automatically syncs Mattermost team membership

4. Data Model

EntityFieldTypeDescription
MattermostUserMappingsidUUID (PK, FK -> Users)Equa user ID
mattermostUserIdstringMattermost user ID
mattermostUsernamestring (nullable)Mattermost username
personalAccessTokenstring (nullable)AES-encrypted personal access token
createdtimestampCreation timestamp
modifiedtimestampLast update timestamp
MattermostOrgMappingsorganizationIdUUID (PK, FK -> Organizations)Equa organization ID
mattermostTeamIdstringMattermost team ID
mattermostTeamNamestring (nullable)Mattermost team name
autoSyncboolean (default: true)Whether to auto-sync members
createdtimestampCreation timestamp
Source: equa-server/modules/persistence/src/schema.ts, Lines: 2042-2080
Migration:
Source: equa-server/modules/persistence/lab/sql/migrations/2.35.0-mattermost-mappings.sql

5. API Endpoints

MethodPathAuthDescription
POST/api/v1/mattermost/sessionCookieProvision user/team, return session token
GET/api/v1/mattermost/configCookieReturn { enabled, baseUrl }
GET/api/v1/mattermost/team-mapping/:teamIdBearer (gateway)Return { organizationId } for a Mattermost team
POST/api/v1/mattermost/sync-org/:orgIdCookie + adminTrigger full org membership sync
Source: equa-server/modules/agent/src/mattermost/mattermost-endpoints.ts, Lines: 34-170
See Mattermost API Endpoints for request/response details.

6. Frontend Components

Route

RouteComponentModule
/:orgId/messagingMessagingPageequa-web/src/modules/messaging

Component Tree

MessagingPage (Redux-connected, withOrganizationHeader HOC)
  ├── MessagingLoading (while session provisioning)
  ├── MessagingError (on failure, with retry)
  └── MattermostEmbed (iframe with token-based auth URL)

Embedding Strategy

The frontend uses an iframe rather than importing Mattermost React components. This avoids bundling Mattermost’s own React 18 into equa-web and isolates the Mattermost UI completely. The MattermostEmbed component constructs a login URL:
{baseUrl}/login/token?token={token}&redirect_to=/{teamName}/channels/{channel}
The iframe uses sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox" for security.

7. Business Rules and Validation

  1. Organization membership required: Only org members can access messaging for that org.
    • Enforced at: equa-server/modules/agent/src/mattermost/mattermost-endpoints.ts, Line: 34 (session endpoint checks org access)
  2. One Mattermost team per org: Each Equa organization maps to exactly one Mattermost team.
    • Enforced at: equa-server/modules/agent/src/mattermost/mattermost-provisioning.ts (ensureTeam uses org ID as key)
  3. Token encryption: Personal access tokens stored in the database are AES-encrypted.
    • Enforced at: equa-server/modules/agent/src/mattermost/mattermost-provisioning.ts (issueSessionToken encrypts with AesKey)
  4. Non-blocking sync: Member add/remove sync to Mattermost is fire-and-forget to avoid blocking org operations.
    • Enforced at: equa-server/modules/organizations/src/writing.ts, Lines: 173, 305 (async calls with .catch)
  5. Signup disabled: Direct Mattermost signup is disabled. Users can only be provisioned through Equa.
    • Enforced at: Mattermost env vars MM_EMAILSETTINGS_ENABLESIGNUPWITHEMAIL=false, MM_TEAMSETTINGS_ENABLEOPENSERVER=false

8. Acceptance Criteria

  • AC-1: Navigating to /:orgId/messaging provisions a Mattermost user, team, and membership on first visit — backend ready, frontend not built
  • AC-2: Mattermost UI loads in an iframe, auto-authenticated with the provisioned token — frontend not built
  • AC-3: User sees only their organization team and channels — backend ready, frontend not built
  • AC-4: Messages persist across sessions — backend ready, frontend not built
  • AC-5: Mentioning @equabot in #agent channel triggers an agent response — gateway config needed
  • AC-6: Share issuance posts a notification to #cap-tablenotification service exists, event hooks not wired
  • AC-7: Adding/removing org members syncs Mattermost team membership — sync hooks exist, equa-server redeploy needed

9. Risks and Edge Cases

Risk / Edge CaseLikelihoodImpactMitigation
iframe X-Frame-Options blockingLowHighMM_SERVICESETTINGS_ALLOWCORSFROM configured for app.equa.cc
Token expiry or rotationMediumMediumuseMattermostSession hook handles refresh; equa-server rotates stale tokens
Railway resource limits (hobby plan)MediumMediumMattermost needs ~512MB RAM; monitor usage, consider upgrade if needed
React version conflictLowHighiframe embedding sidesteps entirely — Mattermost runs its own React 18
Mattermost server downtimeLowMediumHealth check at /api/v4/system/ping; MessagingError component shows retry
Personal access token leakLowHighTokens AES-encrypted at rest; transmitted over HTTPS only