Skip to main content

Team Messaging (Mattermost Integration)

Status: DRAFT Priority: P2 Last Verified: 2026-02-27 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)

ComponentFile PathPurpose
MessagingPageequa-web/src/modules/messaging/pages/messaging-page.tsx, Line: 55Page container with loading/error/embed states
MattermostEmbedequa-web/src/modules/messaging/components/mattermost-embed.tsx, Line: 17iframe embedding Mattermost UI with token auth
MessagingLoadingequa-web/src/modules/messaging/components/messaging-loading.tsxSpinner during session provisioning
MessagingErrorequa-web/src/modules/messaging/components/messaging-error.tsxError state with retry button
useMattermostSessionequa-web/src/modules/messaging/hooks/use-mattermost-session.ts, Line: 13Hook that calls session endpoint, manages token lifecycle
mattermost-apiequa-web/src/modules/messaging/services/mattermost-api.tsHttpClient wrappers for Mattermost API calls
Route registration:
Source: equa-web/src/app/routes/routes.ts, Line: 509
Navigation link:
Source: equa-web/src/app/components/header/shared/organization-links.tsx, Line: 125
Path definition:
Source: equa-web/src/logic/paths.ts, Line: 195

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 React version conflicts (Equa uses React 16, Mattermost uses React 18) and isolates the Mattermost UI. 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
  • AC-2: Mattermost UI loads in an iframe, auto-authenticated with the provisioned token
  • AC-3: User sees only their organization team and channels
  • AC-4: Messages persist across sessions
  • AC-5: Mentioning @equabot in #agent channel triggers an agent response (requires gateway config)
  • AC-6: Share issuance posts a notification to #cap-table (requires notification wiring)
  • AC-7: Adding/removing org members syncs Mattermost team membership (requires equa-server redeploy)

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