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
#agentchannel - Platform events (share issuance, document uploads, member changes) post notifications to relevant channels
2. Current State
Frontend (equa-web)
| Component | File Path | Purpose |
|---|---|---|
MessagingPage | equa-web/src/modules/messaging/pages/messaging-page.tsx, Line: 55 | Page container with loading/error/embed states |
MattermostEmbed | equa-web/src/modules/messaging/components/mattermost-embed.tsx, Line: 17 | iframe embedding Mattermost UI with token auth |
MessagingLoading | equa-web/src/modules/messaging/components/messaging-loading.tsx | Spinner during session provisioning |
MessagingError | equa-web/src/modules/messaging/components/messaging-error.tsx | Error state with retry button |
useMattermostSession | equa-web/src/modules/messaging/hooks/use-mattermost-session.ts, Line: 13 | Hook that calls session endpoint, manages token lifecycle |
mattermost-api | equa-web/src/modules/messaging/services/mattermost-api.ts | HttpClient wrappers for Mattermost API calls |
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)
| Module | File Path | Purpose |
|---|---|---|
| Endpoints | equa-server/modules/agent/src/mattermost/mattermost-endpoints.ts, Line: 14 | 4 Express routes for session, config, mapping, sync |
| REST Client | equa-server/modules/agent/src/mattermost/mattermost-client.ts, Line: 7 | Thin wrapper around Mattermost REST API v4 |
| Provisioning | equa-server/modules/agent/src/mattermost/mattermost-provisioning.ts, Line: 7 | User/team/channel provisioning logic |
| Notifications | equa-server/modules/agent/src/mattermost/mattermost-notifications.ts, Line: 9 | Platform event to channel post bridge |
| Types | equa-server/modules/agent/src/mattermost/types.ts | Shared interfaces and default channel config |
| Member Sync | equa-server/modules/organizations/src/mattermost-sync.ts | Async member add/remove sync |
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)
| Module | File Path | Purpose |
|---|---|---|
| Mattermost Extension | equabot-gateway/extensions/mattermost/ | Existing bot auth, WebSocket monitoring, message routing |
| Org Resolution | equabot-gateway/extensions/equa-platform/src/dispatch.ts, Line: 91 | Maps Mattermost teamId to Equa organizationId |
Data Flow
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
#agentmentioning 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
| Entity | Field | Type | Description |
|---|---|---|---|
MattermostUserMappings | id | UUID (PK, FK -> Users) | Equa user ID |
mattermostUserId | string | Mattermost user ID | |
mattermostUsername | string (nullable) | Mattermost username | |
personalAccessToken | string (nullable) | AES-encrypted personal access token | |
created | timestamp | Creation timestamp | |
modified | timestamp | Last update timestamp | |
MattermostOrgMappings | organizationId | UUID (PK, FK -> Organizations) | Equa organization ID |
mattermostTeamId | string | Mattermost team ID | |
mattermostTeamName | string (nullable) | Mattermost team name | |
autoSync | boolean (default: true) | Whether to auto-sync members | |
created | timestamp | Creation 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
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /api/v1/mattermost/session | Cookie | Provision user/team, return session token |
GET | /api/v1/mattermost/config | Cookie | Return { enabled, baseUrl } |
GET | /api/v1/mattermost/team-mapping/:teamId | Bearer (gateway) | Return { organizationId } for a Mattermost team |
POST | /api/v1/mattermost/sync-org/:orgId | Cookie + admin | Trigger 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
| Route | Component | Module |
|---|---|---|
/:orgId/messaging | MessagingPage | equa-web/src/modules/messaging |
Component Tree
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. TheMattermostEmbed component constructs a login URL:
sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox" for security.
7. Business Rules and Validation
-
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)
- Enforced at:
-
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)
- Enforced at:
-
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)
- Enforced at:
-
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)
- Enforced at:
-
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
- Enforced at: Mattermost env vars
8. Acceptance Criteria
- AC-1: Navigating to
/:orgId/messagingprovisions 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
@equabotin#agentchannel 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 Case | Likelihood | Impact | Mitigation |
|---|---|---|---|
| iframe X-Frame-Options blocking | Low | High | MM_SERVICESETTINGS_ALLOWCORSFROM configured for app.equa.cc |
| Token expiry or rotation | Medium | Medium | useMattermostSession hook handles refresh; equa-server rotates stale tokens |
| Railway resource limits (hobby plan) | Medium | Medium | Mattermost needs ~512MB RAM; monitor usage, consider upgrade if needed |
| React version conflict | Low | High | iframe embedding sidesteps entirely — Mattermost runs its own React 18 |
| Mattermost server downtime | Low | Medium | Health check at /api/v4/system/ping; MessagingError component shows retry |
| Personal access token leak | Low | High | Tokens AES-encrypted at rest; transmitted over HTTPS only |