snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
@@ -0,0 +1,332 @@
|
||||
---
|
||||
title: "Channel-specific access rules"
|
||||
---
|
||||
<PlanAvailability slug="entry-adv" />
|
||||
|
||||
Channel and Team Admins can self-manage access controls for their channels directly through the Channel Settings modal, without requiring System Admin intervention. For organization-wide policies created by System Admins, see [System-wide attribute-based access policies](/administration-guide/manage/admin/abac-system-wide-policies). For team-scoped policies that apply rules across multiple channels within a team, see [Team-level channel membership policies](/administration-guide/manage/admin/abac-team-channel-policies).
|
||||
|
||||
From Mattermost v11.8, channel access rules can be applied to **both private and public channels**. The two channel types behave differently under ABAC:
|
||||
|
||||
- **Private channels** are hard-gated. Non-matching members are removed during synchronization, and only matching users can be added or invited.
|
||||
- **Public channels** are advisory. Non-matching members are *never* removed (anyone can still join a public channel by browsing or via a direct link). With auto-add enabled the policy still pulls matching users in; with auto-add disabled the channel surfaces under **Browse Channels \> Recommended channels** for users whose attributes match.
|
||||
|
||||
Each ABAC channel access policy has an explicit active state that determines whether the policy will automatically add users who meet the policy's criteria but are not yet channel members. For private channels, the policy's rules are always enforced to remove members who no longer meet the required attribute rules, regardless of the active state. For public channels, no member is ever removed by ABAC — the rules are advisory only.
|
||||
|
||||
With channel access rules, Channel and Team Admins can:
|
||||
|
||||
- Create channel-specific access rules using a simple interface.
|
||||
- Use rules that are **additive** to any system policies. For private channels, both must be satisfied for adds and invites. For public channels, the combined rules affect auto-add and recommendations only, and don't block joins or remove members.
|
||||
- Automatic member synchronization with immediate feedback.
|
||||
- Self-exclusion prevention to avoid locking yourself out (private channels only).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Attribute-Based Access Control (ABAC)](/administration-guide/manage/admin/attribute-based-access-control) must be enabled by a System Admin in **System Console \> System Attributes \> Attribute-Based Access**.
|
||||
- You need Channel Admin permissions and the `manage_channel_access_rules` permission.
|
||||
- Channel access rules are available for private and public channels, with behavior that varies by channel type. See [Public and private channel behavior](#public-and-private-channel-behavior). Default channels (such as Town Square and Off-Topic), shared channels, and group-synced channels remain ineligible.
|
||||
|
||||
### Access Channel Settings
|
||||
|
||||
1. In a private or public channel where you have Channel Admin permissions, select the channel name at the top of the center pane.
|
||||
2. Select **Channel Settings** from the dropdown menu.
|
||||
3. Navigate to the **Membership Policy** tab. This tab is only visible for eligible channels when you have the appropriate permissions and ABAC is enabled system-wide. The tab is hidden on default channels, shared channels, and group-synced channels.
|
||||
|
||||
<Tip>
|
||||
|
||||
You can also assign ABAC rules to a channel directly from a channel's details page in the System Console under **Channel Management** by enabling the **Enable attribute-based channel access** option. Under **Access policy**, select **Link to a policy** to select an existing policy.
|
||||
|
||||
</Tip>
|
||||
|
||||
## Configure access rules
|
||||
|
||||
Channel access rules use the same simple interface as system policies, allowing you to create attribute-based conditions without complex syntax.
|
||||
|
||||
1. In the **Membership Policy** tab, you'll see any inherited system policies at the top in a blue information banner (if applicable).
|
||||
2. Use the **Add attribute** button to create new access conditions:
|
||||
- **Select attribute**: Choose from available user attributes
|
||||
- **Choose operator**: Select how the attribute should match:
|
||||
- **Is**: Exact match with specified value
|
||||
- **Is not**: Must not match specified value
|
||||
- **In**: Must match any of multiple specified values
|
||||
- **Contains**: Attribute value must contain specified text
|
||||
- **Set values**: Enter the required attribute values
|
||||
3. Add multiple conditions as needed. All conditions are combined with logical AND (all must be satisfied).
|
||||
4. Select **Test access rules** to preview which users would be granted access based on your current rules.
|
||||
|
||||
### Auto-sync membership
|
||||
|
||||
The **Auto-add members based on access rules** toggle controls automatic membership management. The behavior differs by channel type:
|
||||
|
||||
- **Private channels (hard gate)**: Membership stays consistently aligned with the rules, similar to how LDAP group channels work.
|
||||
- **Enabled**: Users matching the rules are automatically added. If users temporarily lose attributes and later regain them, they will be automatically re-added.
|
||||
- **Disabled**: Rules act as a gate (preventing unauthorized joins) but don't automatically add qualifying users.
|
||||
- **Public channels (advisory)**: ABAC never removes members — anyone can still join a public channel.
|
||||
- **Enabled**: Matching users are automatically added (a convenience, not a gate).
|
||||
- **Disabled**: The channel appears under **Browse Channels \> Recommended channels** for users whose attributes match, surfacing the channel without adding anyone.
|
||||
|
||||
<Important>
|
||||
|
||||
- Auto-add/auto-sync is checked on a per-channel policy basis, not inherited from parent system-wide policies.
|
||||
- If a system policy has auto-sync enabled, Channel and Team Admins cannot disable it at the channel level.
|
||||
- If a system policy has auto-sync disabled, Channel and Team Admins can choose to enable it for their channel.
|
||||
- When no rules are configured, this toggle is automatically disabled.
|
||||
- On **private** channels, users who no longer meet required attribute rules are always removed during synchronization regardless of the auto-sync setting. On **public** channels, no member is ever removed by ABAC.
|
||||
|
||||
</Important>
|
||||
<Note>
|
||||
|
||||
If a policy rule references attribute values from a `shared_only` or `source_only` attribute that you don't personally hold, those values appear as `--------` and the row is read-only. Test and Delete actions are also disabled for those rows. See [Attribute value masking](/administration-guide/manage/admin/abac-system-wide-policies#attribute-value-masking) for details.
|
||||
|
||||
</Note>
|
||||
|
||||
### Validation and safety
|
||||
|
||||
Before saving changes, Mattermost validates your rules to prevent common issues:
|
||||
|
||||
- **Required fields**: All attribute selections and values must be completed.
|
||||
- **Self-exclusion prevention**: For private channels, you cannot save rules that would remove yourself from the channel. The check is skipped for public channels because they are advisory under ABAC and can't lock anyone out.
|
||||
- **Conflict detection**: Rules that create impossible conditions are identified.
|
||||
|
||||
When you save changes that affect membership, a confirmation dialog shows you:
|
||||
|
||||
- How many users will be added or removed.
|
||||
- Option to view the specific users affected.
|
||||
- Confirmation required before applying changes.
|
||||
|
||||
Once a policy is attached to a channel, the channel cannot be converted between public and private until the policy is removed. The two modes have different semantics (advisory vs. hard gate), so a silent conversion would change what an existing policy actually does to its members.
|
||||
|
||||
## Public and private channel behavior
|
||||
|
||||
Membership policies behave differently depending on the type of channel they're applied to:
|
||||
|
||||
- **Private channels**: Membership policies are enforced. Users who match the policy's rules are added, and users who don't match the rules are removed during synchronization.
|
||||
- **Public channels**: Membership policies are advisory. Matching users may be automatically added when auto-add is enabled, but non-matching members are not removed.
|
||||
- When auto-add is disabled for a public channel, matching channels are surfaced as **recommended** rather than enforcing membership.
|
||||
- Direct messages and group messages aren't eligible for membership policies.
|
||||
- Default channels such as **Town Square** and **Off-Topic** are excluded.
|
||||
|
||||
<Note>
|
||||
|
||||
Public channels with membership policies may appear in **Browse Channels** under **Recommended**, and matching users may be marked **Recommended** in the channel invite flow. See [Browse channels](/end-user-guide/collaborate/browse-channels) and [Manage channel members](/end-user-guide/collaborate/manage-channel-members) for the end-user experience.
|
||||
|
||||
</Note>
|
||||
|
||||
## Channel-level permission policies
|
||||
|
||||
From Mattermost v11.8.0, channel admins can define channel-level permission rules for file upload and file download based on user attributes and channel role. Applicable roles include **channel admin**, **channel member**, and **channel guest**.
|
||||
|
||||
For system-wide permission policies that restrict file upload and download actions, see [Permission policies](/administration-guide/manage/admin/abac-system-wide-policies#permission-policies).
|
||||
|
||||
## Simulate access
|
||||
|
||||
From Mattermost v11.8.0, admins can use **Simulate access** in Channel Settings to preview whether selected users can perform actions such as uploading files or downloading files before saving policy changes.
|
||||
|
||||
- Simulation can evaluate draft rules before they're saved, so you can confirm the intended scope without affecting live channel access.
|
||||
- Some denied results may indicate that the decision came from another policy. In that case, Mattermost shows that access was denied by another policy without exposing policy details you aren't authorized to see.
|
||||
|
||||
<Note>
|
||||
|
||||
Channel-level permission policies and **Simulate access** are gated by the `PermissionPolicies` feature flag (`MM_FEATUREFLAGS_PERMISSIONPOLICIES`) and require a Mattermost Enterprise Advanced license. See the Mattermost developer documentation for details on [enabling feature flags in a self-hosted deployment](https://developers.mattermost.com/contribute/more-info/server/feature-flags/#self-hosted-and-local-development). Mattermost Cloud customers can request this feature flag be enabled by contacting their Mattermost Account Manager or by [creating a support ticket](https://support.mattermost.com/hc/en-us/requests/new?ticket_form_id=11184911962004).
|
||||
|
||||
</Note>
|
||||
|
||||
## Manage team-scoped membership policies in Team Settings
|
||||
|
||||
From Mattermost v11.7, Team Admins can create, edit, and delete channel membership policies directly from Team Settings, scoped to channels within their team. This lets teams self-manage attribute-based membership for their own channels without requiring a System Admin to create or modify a system-wide policy.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [Attribute-Based Access Control (ABAC)](/administration-guide/manage/admin/attribute-based-access-control) must be enabled by a System Admin in **System Console \> System Attributes \> Attribute-Based Access**.
|
||||
- You need Team Admin permissions for the team and the `manage_team_access_rules` permission.
|
||||
- Team-scoped membership policies can be assigned to both public and private channels within the team.
|
||||
|
||||
### Team Admin workflow
|
||||
|
||||
1. Open **Team Settings** from the team menu, and go to the **Channel Membership** tab. This tab is only visible to Team Admins with the `manage_team_access_rules` permission when ABAC is enabled system-wide.
|
||||
2. Select **Add Policy** and enter a name for the policy. Parent policy names must be unique; if you enter a name that's already in use, Mattermost displays a user-friendly error and prevents the policy from being saved.
|
||||
3. Define the attribute rules that determine which users can be members of channels assigned to this policy. Rules use the same attribute conditions available for channel-specific access rules.
|
||||
4. Assign the applicable private channels in the team to the policy.
|
||||
5. Select **Save** to create or update the policy. Team-scoped policies can be edited or deleted from the same tab at any time.
|
||||
|
||||
### Team Settings sync status footer
|
||||
|
||||
The **Channel Membership** tab includes a sync status footer that shows:
|
||||
|
||||
- **Last sync time**: The time of the most recent membership synchronization for policies in this team.
|
||||
- **Sync now**: An on-demand action that triggers an immediate synchronization for the team's policies.
|
||||
|
||||
Team-scoped sync is limited to the team admin's team scope. Triggering **Sync now** from Team Settings does not affect channels or policies outside the current team.
|
||||
|
||||
### Sync behavior by channel type
|
||||
|
||||
Sync behavior for team-scoped membership policies depends on the type of channel the policy is assigned to:
|
||||
|
||||
- **Public channels**: Sync is advisory and add-only. Users who match the policy's rules are added to the channel, but no users are removed if their attributes change.
|
||||
- **Private channels**: Sync is enforced. Users who match the policy's rules are added to the channel, and users who no longer match the rules are removed during the next synchronization.
|
||||
|
||||
### Automatic sync on policy changes
|
||||
|
||||
Mattermost automatically runs a sync job whenever a team-scoped membership policy is created, or its rules, assigned channels, or active state change. Team Admins don't need to manually trigger **Sync now** for these updates; the sync runs as part of the change.
|
||||
|
||||
## Policy inheritance
|
||||
|
||||
Channel-level (child) ABAC policies now behave independently and consistently, even when parent system-wide policies exist. Each policy maintains its own active state and configuration.
|
||||
|
||||
When both [system policies](/administration-guide/manage/admin/abac-system-wide-policies) and channel rules are configured:
|
||||
|
||||
1. **System policies** are displayed in a blue banner at the top (read-only)
|
||||
2. **Channel rules** are managed in the access rules section below
|
||||
3. **Users must satisfy BOTH** system policies and channel rules to access the channel
|
||||
4. Channel rules **add restrictions** but cannot weaken system policies
|
||||
5. **Auto-add behavior** is determined by the individual channel policy, not inherited from parent system-wide policies. System-wide policies can pass down their rules, but auto-add/auto-sync is evaluated per channel.
|
||||
|
||||
## Use cases and recommendations
|
||||
|
||||
**Ideal use cases for channel access rules:**
|
||||
|
||||
- **Project-specific channels**: Restrict access to team members working on specific projects
|
||||
- **Clearance-based discussions**: Ensure only users with appropriate security clearance can participate
|
||||
- **Department communications**: Limit channel access to specific organizational units
|
||||
- **Temporary access**: Create rules for short-term project teams or contractor access
|
||||
|
||||
**Best practices:**
|
||||
|
||||
- **Start simple**: Begin with basic attribute conditions before adding complexity
|
||||
- **Test before saving**: Always use the "Test access rules" feature to verify your intended scope
|
||||
- **Document changes**: Consider posting a message in the channel when access rules change
|
||||
- **Regular review**: Periodically review rules to ensure they remain appropriate
|
||||
- **Coordinate with IT**: Work with System Admins for complex organizational policies
|
||||
|
||||
**When to use system policies vs. channel rules:**
|
||||
|
||||
- **System policies**: Use for organization-wide standards, compliance requirements, or policies affecting multiple channels
|
||||
- **Channel rules**: Use for channel-specific restrictions, project-based access, or when you need immediate control without IT involvement
|
||||
|
||||
## End-user experience
|
||||
|
||||
When channels have attribute-based access controls applied, users will see clear indicators and experience specific behaviors designed to maintain security while providing transparency.
|
||||
|
||||
### Visual indicators
|
||||
|
||||
**Channel Members panel:**
|
||||
|
||||
- Information banner at the top explains that attribute-based access is enabled. The message differs by channel type: private channels indicate that access is restricted by user attributes, while public channels indicate that member recommendations are based on user attributes.
|
||||
- Displays required attribute values as tags (e.g., "Engineering", "Confidential").
|
||||
- Tooltip on hover shows the attribute name for each value.
|
||||
|
||||
**Add Members modal:**
|
||||
|
||||
- Similar information banner and attribute value display.
|
||||
- On **private** channels, users who don't match the access criteria won't appear in search results — only eligible users can be added.
|
||||
- On **public** channels, the full team list is shown and matching users are surfaced with a **Recommended** tag at the top of the list. Anyone can still be added because public-channel ABAC is advisory.
|
||||
|
||||
**Browse Channels:**
|
||||
|
||||
- A **Recommended channels** filter is available in the channel-type dropdown when ABAC is enabled. Selecting it lists the public channels whose policies the user matches — useful when auto-add is disabled and the channel is offered as a recommendation rather than auto-joined.
|
||||
|
||||
### Functional restrictions
|
||||
|
||||
When ABAC is enabled for a **private** channel:
|
||||
|
||||
- **Search limitations**: Users who don't match access criteria don't appear in member search results.
|
||||
- **Invitation restrictions**: Only users meeting attribute requirements can be added to the channel.
|
||||
- **Guest user exclusions**: Private channels with ABAC policies cannot have guest users invited.
|
||||
- **Automatic removal**: Users who lose required attributes are automatically removed during the next synchronization.
|
||||
|
||||
When ABAC is enabled for a **public** channel:
|
||||
|
||||
- **Search results are unfiltered**: All eligible team members appear in the Add Members modal so admins can still invite anyone; matching users carry a **Recommended** tag.
|
||||
- **Recommendations**: With auto-add disabled, the channel surfaces under **Browse Channels \> Recommended channels** for matching users.
|
||||
- **Auto-add (when enabled)**: Matching users are added automatically. **Members are never removed** by ABAC — users can always leave on their own, and joining freely is unaffected because the channel is public.
|
||||
|
||||
<Note>
|
||||
|
||||
These behaviors apply across all Mattermost clients, including web, desktop, and mobile, to ensure consistent enforcement.
|
||||
|
||||
</Note>
|
||||
|
||||
## Troubleshooting and FAQs
|
||||
|
||||
Common questions about attribute-based access control implementation and usage.
|
||||
|
||||
### Permission and access
|
||||
|
||||
#### Why can't I see the Membership Policy tab in Channel Settings?
|
||||
|
||||
The **Membership Policy** tab is only visible when all of these conditions are met:
|
||||
|
||||
- You have Channel Admin role or higher for the channel.
|
||||
- The channel is a private or public channel (not a default channel like Town Square or Off-Topic, group message, direct message, shared, or group-synced channel).
|
||||
- ABAC is enabled system-wide by a System Admin in **System Console \> System Attributes \> Attribute-Based Access**.
|
||||
- Your user role includes the `manage_channel_access_rules` permission.
|
||||
|
||||
#### Can Channel and Team Admins override system policies?
|
||||
|
||||
No. Channel rules are always **additive** to system policies. Users must satisfy both system policies AND channel rules to access the channel. Channel and Team Admins cannot weaken or override restrictions set by System Admins.
|
||||
|
||||
#### What happens if I create rules that would exclude myself?
|
||||
|
||||
For **private** channels, Mattermost prevents this with self-exclusion validation. If your rules would remove you from the channel, you'll see an error message and cannot save the changes until you adjust the rules or reset them.
|
||||
|
||||
For **public** channels, the self-exclusion check is skipped — public-channel ABAC is advisory, the policy can't kick anyone out, and you can always re-join a public channel directly. This lets you author a policy intended for a different team (for example, a Sales admin configuring an Engineering recommendation) without being blocked.
|
||||
|
||||
#### Can I convert a public channel to private (or vice versa) while a policy is attached?
|
||||
|
||||
No. The two modes have different semantics — a public-channel policy is advisory while a private-channel policy is a hard gate that removes non-matching members. A silent conversion would change what the existing policy does to its members, so Mattermost requires you to remove the policy first, convert the channel, and re-attach the policy if you still want it.
|
||||
|
||||
### Rule configuration
|
||||
|
||||
#### Can I use advanced CEL expressions in Channel Settings?
|
||||
|
||||
No. Channel Settings only supports Basic Mode with simple attribute conditions. For complex expressions with nested logic or mixed operators, System Admins need to create policies in the System Console.
|
||||
|
||||
#### How do I remove all access rules from a channel?
|
||||
|
||||
Delete all attribute conditions from the access rules table. When no rules are configured and no system policies are applied, the channel returns to standard access behavior.
|
||||
|
||||
#### Why is the auto-sync toggle disabled?
|
||||
|
||||
The auto-sync toggle is automatically disabled when:
|
||||
|
||||
- No access rules are configured
|
||||
- A system policy with auto-sync enabled is applied (Channel and Team Admins cannot disable it)
|
||||
- There are validation errors in the current rules
|
||||
- The channel's access control policy is not in an active state
|
||||
|
||||
<Note>
|
||||
|
||||
**Troubleshooting auto-sync issues**: If auto-sync functionality (automatic adding/re-adding of members) is not working as expected, verify that the channel's access control policy is in an active state. An inactive policy will prevent automatic member additions from occurring. Note that enforcement of rules (removal of members who no longer meet requirements) happens regardless of the policy's active state.
|
||||
|
||||
</Note>
|
||||
|
||||
### Synchronization and membership
|
||||
|
||||
#### How quickly are membership changes applied?
|
||||
|
||||
When you save access rules, membership sync job is created and changes are applied as soon as the job is completed. Additionally, Mattermost runs synchronization jobs on a schedule to handle attribute changes from external systems (LDAP, SAML). The interval is set by `AccessControlSettings.SyncJobIntervalSeconds` and defaults to 3600 seconds (60 minutes). User attributes used for access control evaluation are refreshed on a separate schedule controlled by `AccessControlSettings.AttributeRefreshIntervalSeconds`, which defaults to 30 seconds.
|
||||
|
||||
#### Will users be notified when they're removed from a channel?
|
||||
|
||||
Yes, users receive standard Mattermost notifications when they're removed from channels due to access rule changes, similar to manual removals.
|
||||
|
||||
#### Can I see who was added or removed during synchronization?
|
||||
|
||||
Yes, the confirmation modal before saving shows exactly which users will be affected. System Admins can also view detailed synchronization logs in the System Console.
|
||||
|
||||
### Attribute and data questions
|
||||
|
||||
#### Which user attributes can I use in access rules?
|
||||
|
||||
You can use any user attributes either synchronized via LDAP/SAML or manually configured by System Admins in **System Console \> System Attributes \> User Attributes**.
|
||||
|
||||
#### What happens if a user attribute changes?
|
||||
|
||||
During the next scheduled synchronization (every 60 minutes by default), users who no longer match the access rules will be removed from the channel, and new users who now match will be added (if auto-sync is enabled).
|
||||
|
||||
#### Do guest users work with ABAC channels?
|
||||
|
||||
No. Private channels with attribute-based access control cannot have guest users. This ensures strict adherence to access control policies based on organizational attributes.
|
||||
|
||||
#### Can group-sync channels use ABAC?
|
||||
|
||||
No. Channels configured with group synchronization cannot use attribute-based access control. Group-sync and ABAC are mutually exclusive features - you must choose one method of access control per channel.
|
||||
@@ -0,0 +1,193 @@
|
||||
---
|
||||
title: "System-wide attribute-based access policies"
|
||||
---
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
<PlanAvailability slug="entry-adv" />
|
||||
|
||||
Use this guide to create and manage organization-wide attribute-based access policies in the System Console. Policies can be assigned to both channels and teams. For channel-level rules managed by Channel Admins, see [Channel-specific access rules](/administration-guide/manage/admin/abac-channel-access-rules). For team membership policies, see [Team membership access policies](/administration-guide/manage/admin/abac-team-membership).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Attribute-based access controls (ABAC)](/administration-guide/manage/admin/attribute-based-access-control) require defined user attributes. First [configure user attributes](/administration-guide/manage/admin/user-attributes) in the System Console.
|
||||
- Then go to **System Console \> System Attributes \> Attribute-Based Access** to enable attribute-based access controls for your Mattermost instance. This functionality requires a Mattermost Enterprise Advanced license.
|
||||
|
||||
From Mattermost v10.11, user-managed attributes are excluded from attribute-based access control (ABAC) rules by default for security reasons. This prevents access control policies from being circumvented by users editing their own profile attributes. To include user-managed attributes in ABAC rules, a system admin must explicitly enable the `EnableUserManagedAttributes` configuration setting. See the [user attribute](/administration-guide/manage/admin/user-attributes#before-you-begin) documentation for details on enabling this feature. This configuration setting is available only in Enterprise Edition Advanced and is disabled by default.
|
||||
|
||||
## Define access control policies
|
||||
|
||||
You can add multiple rules to a single policy, and each rule can include multiple attribute values.
|
||||
|
||||
1. In the System Console, go to **System Attributes \> Attribute-Based Access** and select **Add Policy**.
|
||||
|
||||
2. Enter a unique policy name. Parent access control policy names must be unique; if you enter a name that's already in use, Mattermost displays a user-friendly error message and prevents the policy from being saved until you choose a different name.
|
||||
|
||||
3. Choose whether to automatically add users who match your configured attribute values as new members. Automatic synchronization is disabled by default.
|
||||
|
||||
- **True**: Automatically maintains channel membership according to the defined rules as user attributes change.
|
||||
- **False** (**Default**): Only removes members and restricts adding them to the channel if they don’t match defined rules. Users are not automatically added.
|
||||
|
||||
Regardless of whether this configuration setting, users who no longer match the configured attribute values in the future will be removed from the channel after the next channel synchronization.
|
||||
|
||||
4. Define the attribute-based access rules to restrict channel membership.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="simple-mode" label="Simple Mode">
|
||||
|
||||
Simple Mode is ideal for simple and straightforward access control scenarios. Each row is a single condition that must be met for a user to comply with the policy. Simple Mode only supports simple conditions without nested logic or mixed logical operators. All rules are combined with a logical AND operator `&&`.
|
||||
|
||||
1. Select **Add attribute** and select the attribute you want to use for access control.
|
||||
|
||||
2. Specify how you want the attribute to match the user profile. You can choose from the following options:
|
||||
|
||||
> - **Is**: The attribute must be an exact match of the value.
|
||||
> - **Is not**: The attribute must not contain specified value.
|
||||
> - **In**: The attribute must match at least one value.
|
||||
> - **Starts with**: The attribute value must start with the specified value.
|
||||
> - **Ends with**: The attribute value must end with the specified value.
|
||||
> - **Contains**: The attribute value must exist somewhere with the specified value.
|
||||
>
|
||||
> When the selected attribute is a **Ranked** attribute (an ordered list of values, such as clearance or classification levels), the match options are ordered comparisons instead. These let you match everyone at or above a threshold rather than only an exact value:
|
||||
>
|
||||
> - **Is exactly** / **Is not**: The value must exactly match, or must not match, the specified value.
|
||||
> - **Is at least**: The value must be equal to or higher than the specified value - for example, clearance *is at least* Secret matches Secret and every higher level.
|
||||
> - **Is greater than**: The value must be higher than the specified value.
|
||||
> - **Is at most**: The value must be equal to or lower than the specified value.
|
||||
> - **Is less than**: The value must be lower than the specified value.
|
||||
|
||||
3. Specify the attribute values that users must have to be granted access to the channel.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="advanced-mode" label="Advanced Mode">
|
||||
|
||||
Advanced Mode is ideal for complex access control scenarios that require CEL syntax to combine multiple conditions with logical operators that support rules like `user.<attribute> == <value>`, using `&&` / `||` (and/or) for multiple conditions, and `()` to group conditions. The CEL Expression Editor provides real-time syntax validation and feedback, as well as context-aware autocomplete for attributes, operators, and attribute values.
|
||||
|
||||
You can also start defining rules in Simple Mode and then switch to Advanced Mode to refine the rules further as needed. However, you'll be blocked from switching from Advanced back to Simple Mode if one of the following are true:
|
||||
|
||||
- Mixed logical operators are used between conditions.
|
||||
- Nested logic/grouping (parentheses) are present.
|
||||
- Unsupported operators or expressions are detected.
|
||||
|
||||
The syntax structure is `user.<attribute> <operator> <value>`.
|
||||
|
||||
As you type, autocompletes show available attributes. As you select attributes, autocomplete suggests appropriate CEL operators. After selecting an operator, when attribute values are pre-defined, autocomplete suggests values to choose from. Mattermost will explicitly indicate issues such as missing operators, incorrect syntax, or incomplete conditions.
|
||||
|
||||
Select the **Validate syntax** bar to check the syntax of your rule. If the syntax is valid, the bar will turn green and display a message indicating that the syntax is valid. If there are any issues, the bar will turn red and display an error message.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
### Simulate access
|
||||
|
||||
Select **Test access rule** to test the rule against your user base to return how many users would be granted access to the channel based on the current rule. Test your rules to ensure the intended scope and avoid unexpected access changes.
|
||||
|
||||
From Mattermost v11.8.0, you can use **Simulate access** to preview allowed and denied outcomes for specific users before saving policy changes:
|
||||
|
||||
1. Open the policy editor in the System Console.
|
||||
2. Select **Simulate access**.
|
||||
3. Choose the users you want to test.
|
||||
4. Review the allowed and denied outcomes by action, such as joining a channel or uploading and downloading files.
|
||||
5. Adjust the rules before saving.
|
||||
|
||||
Simulation can test draft policy changes before they affect live channel access or file permissions. Detailed rule and attribute information is shown only when the denial comes from the policy or scope you're editing; otherwise, Mattermost may show that access was denied by another policy.
|
||||
|
||||
<Note>
|
||||
|
||||
**Simulate access** and channel-level permission policies for file upload and file download are gated by the `PermissionPolicies` feature flag (`MM_FEATUREFLAGS_PERMISSIONPOLICIES`) and require a Mattermost Enterprise Advanced license. See the Mattermost developer documentation for details on [enabling feature flags in a self-hosted deployment](https://developers.mattermost.com/contribute/more-info/server/feature-flags/#self-hosted-and-local-development). Mattermost Cloud customers can request this feature flag be enabled by contacting their Mattermost Account Manager or by [creating a support ticket](https://support.mattermost.com/hc/en-us/requests/new?ticket_form_id=11184911962004).
|
||||
|
||||
</Note>
|
||||
|
||||
### Manage rules
|
||||
|
||||
You can apply changes to existing rules or remove rules at any time using either Simple Mode or Advanced Mode. Select **Save** to save your changes.
|
||||
|
||||
### Assign policies to channels
|
||||
|
||||
From Mattermost v11.8, system-wide policies can be assigned to **both private and public channels**. Select **Add channels** to search for and select the channels you want. You can assign the policy to multiple channels at once, or you can [assign it to individual channels](#define-access-controls-per-channel) later. Select **Save** to save your changes.
|
||||
|
||||
The two channel types behave differently under the same policy:
|
||||
|
||||
- **Private channels** are hard-gated. The policy adds matching users (when auto-add is enabled), removes non-matching members during synchronization, and prevents non-matching users from being added or invited.
|
||||
- **Public channels** are advisory. The policy never removes members and never blocks anyone from joining. With auto-add enabled it automatically adds matching users; with auto-add disabled the channel appears under **Browse Channels \> Recommended channels** for matching users.
|
||||
|
||||
Before saving, you'll be prompted to confirm the impact of the change so you can review how it affects each channel type.
|
||||
|
||||
Default channels (such as Town Square and Off-Topic), shared channels, and group-synced channels remain ineligible — they are excluded from the channel selector.
|
||||
|
||||
<Note>
|
||||
|
||||
Private channels with attribute-based access control policies can't have guest users invited to them. Only users who match the defined attribute criteria can be added to ABAC-controlled private channels, ensuring strict adherence to access control policies. Public channels remain joinable by anyone regardless of the policy.
|
||||
|
||||
</Note>
|
||||
|
||||
### Assign policies to teams
|
||||
|
||||
From Mattermost v11.10, system-wide policies can also be assigned to teams. Go to **System Console \> User Management \> Teams** and open the team you want to configure. In the **Team Management** section, enable **Manage membership with attribute based membership policies** — the **Membership policies** section only appears once this toggle is on. Then link an existing policy and select **Save**.
|
||||
|
||||
Group-synced teams cannot use membership policies, so the toggle is unavailable on them.
|
||||
|
||||
The policy behaves differently depending on the team's privacy mode:
|
||||
|
||||
- **Private teams** — strict enforcement: join is gated, non-qualifying members are removed at sync, and the team is hidden from non-qualifying non-members in Browse Teams.
|
||||
- **Public teams** — advisory: the policy is never a gate. Qualifying non-members see a **Recommended** chip in Browse Teams; anyone can still join freely.
|
||||
|
||||
For a full description of team assignment, custom rules, sync configuration, and end-user surfaces, see [Team membership access policies](/administration-guide/manage/admin/abac-team-membership).
|
||||
|
||||
### Delete policies
|
||||
|
||||
To delete a policy, select the **Delete** button next to the policy you want to remove. Policies that are still assigned to channels cannot be deleted — remove the policy from those channels first.
|
||||
|
||||
Team assignments are not checked before deletion, so unlink the policy from every team that uses it before deleting it. Otherwise those teams keep a reference to a policy that no longer exists.
|
||||
|
||||
## Define access controls per channel
|
||||
|
||||
You can assign an existing access control policy to a private or public channel for more granular control over channel membership. This is useful when you need to apply different rules for different channels.
|
||||
|
||||
1. In the System Console, go to **User Management \> Channels** to select the private or public channel you want to configure, and select **Edit**.
|
||||
2. In the **Channel Management** section, enable the **Enable attribute-based channel access** option.
|
||||
3. Under **Access policy**, select **Link to a policy** to select an existing policy.
|
||||
|
||||
Once a policy is attached, the channel's privacy can no longer be flipped between public and private until the policy is removed — see [Channel-specific access rules](/administration-guide/manage/admin/abac-channel-access-rules#validation-and-safety).
|
||||
|
||||
<Tip>
|
||||
|
||||
You can also assign ABAC rules to a channel directly from a channel's details page in the System Console under **Channel Management** by enabling the **Enable attribute-based channel access** option. Under **Access policy**, select **Link to a policy** to select an existing policy.
|
||||
|
||||
</Tip>
|
||||
|
||||
### Remove channel policies
|
||||
|
||||
Disable the policy for the channel by selecting **Remove Policy**. You can then link the channel to a different policy if preferred.
|
||||
|
||||
## Permission policies
|
||||
|
||||
From Mattermost v11.7, System Admins can define attribute-based **permission policies** that restrict specific user actions in addition to channel membership. Permission policies use the same attribute-based rules as access policies, but they apply to user actions rather than channel access.
|
||||
|
||||
Permission policies can be used to restrict the following actions based on user attributes:
|
||||
|
||||
- **File upload**: Prevent users who don't match the defined attribute rules from uploading file attachments.
|
||||
- **File download**: Prevent users who don't match the defined attribute rules from downloading file attachments.
|
||||
|
||||
When a permission policy applies, users who don't match the configured attribute values can't perform the restricted action. Users may see file attachments as unavailable or redacted in messages they would otherwise have access to. See [Restricted file attachments](/end-user-guide/collaborate/share-files-in-messages#restricted-file-attachments) for the end-user-facing behavior.
|
||||
|
||||
Permission policies follow the same unique-name requirement as access policies: each parent permission policy must have a unique name, and Mattermost surfaces a user-friendly error if a duplicate name is entered.
|
||||
|
||||
## Attribute value masking
|
||||
|
||||
From Mattermost v11.8, when a policy references attribute values that the editing admin doesn't personally hold, those values are hidden in the policy editor. This prevents admins from reading or copying sensitive attribute values they're not authorized to see, while still allowing the policy to enforce access control normally.
|
||||
|
||||
Masking only applies to attributes configured with a `shared_only` or `source_only` access mode. Attributes with a `public` access mode are always visible to all admins. See [Attribute access modes](/administration-guide/manage/admin/user-attributes#attribute-access-modes) for details on how each mode controls value visibility.
|
||||
|
||||
**What you'll see:**
|
||||
|
||||
- Masked values appear as `--------` in the rule editor instead of the raw value.
|
||||
- Rows containing masked values are **read-only** and cannot be modified.
|
||||
- **Test rule** and **Delete** actions are disabled for rules that contain masked values.
|
||||
|
||||
**Enforcement is unaffected:** masking applies to the editing UI only. The underlying rules continue to control channel access and permissions as configured.
|
||||
|
||||
**To edit a masked rule**, ask the policy owner or another System Admin who holds the relevant attribute values to make the change.
|
||||
|
||||
This behavior applies to both the table editor and the CEL editor, and also to Team Admin and Channel Admin policy editors in Team Settings and Channel Settings. Requires Enterprise Advanced license and the `AttributeValueMasking` feature flag.
|
||||
@@ -0,0 +1,149 @@
|
||||
---
|
||||
title: "Team-level channel membership policies"
|
||||
sidebar_label: "Team channel policies"
|
||||
---
|
||||
<PlanAvailability slug="entry-adv" />
|
||||
|
||||
Team Admins can create and manage attribute-based membership policies for private channels within their team, directly from Team Settings, without requiring System Admin involvement. For organization-wide policies managed by System Admins, see [System-wide attribute-based access policies](/administration-guide/manage/admin/abac-system-wide-policies).
|
||||
|
||||
<Note>
|
||||
|
||||
From Mattermost v11.10, Team Settings has four tabs: **Info**, **Access**, **Team Membership**, and **Channel Membership**. This page covers the **Channel Membership** tab — policies that Team Admins configure for *channels* within their team. For team-level membership policies (controlling who can join the team itself), see [Team membership access policies](/administration-guide/manage/admin/abac-team-membership).
|
||||
|
||||
</Note>
|
||||
|
||||
With team-level channel membership policies, Team Admins can:
|
||||
|
||||
- Create policies that apply attribute-based access rules to one or more private channels within their team.
|
||||
- Control which users can join or stay in those channels based on their profile attributes.
|
||||
- Manage auto-sync membership, so channels stay up to date as user attributes change.
|
||||
|
||||
## Use cases
|
||||
|
||||
- **Team-wide project access**: Restrict all private project channels to members with a specific project attribute.
|
||||
- **Department isolation**: Ensure only users from a specific department can join the team's private channels.
|
||||
- **Clearance tiers**: Apply clearance-level requirements consistently across multiple channels in the team without System Admin involvement for each change.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Attribute-Based Access Control (ABAC)](/administration-guide/manage/admin/attribute-based-access-control) must be enabled by a System Admin in **System Console \> System Attributes \> Attribute-Based Access**.
|
||||
- You need Team Admin permissions for the team. The `manage_team_access_rules` permission is included in the Team Admin role by default.
|
||||
- Team-level membership policies apply only to private channels within the team.
|
||||
|
||||
### Access Team Settings
|
||||
|
||||
1. Select the team name in the sidebar to open the team menu.
|
||||
2. Select **Team Settings**.
|
||||
3. Navigate to the **Channel Membership** tab (the fourth tab in Team Settings). This tab is only visible when ABAC is enabled system-wide and you have Team Admin permissions.
|
||||
|
||||
<Note>
|
||||
|
||||
System Admins also have access to the **Channel Membership** tab in Team Settings and see the same policies as Team Admins.
|
||||
|
||||
</Note>
|
||||
|
||||
## Manage membership policies
|
||||
|
||||
The **Channel Membership** tab shows policies scoped to the team. Each policy displays its name and the number of private channels it applies to.
|
||||
|
||||
Team Admins only see policies whose access rules their own user attributes satisfy. If a policy has rules that exclude the Team Admin's attributes (for example, a policy requiring `Department=Engineering` and the Team Admin has `Department=Finance`), that policy will not appear in their list. This is a self-inclusion safety mechanism to prevent admins from being locked out of policies they manage.
|
||||
|
||||
### Create a policy
|
||||
|
||||
1. In the **Channel Membership** tab, select **Add policy**.
|
||||
2. Enter a unique policy name.
|
||||
3. Define access rules under **Access rules**:
|
||||
- Select **Add attribute** to add a condition.
|
||||
- For each condition, choose the user attribute, the matching operator (**Is**, **Is not**, **In**, **Contains**), and the required value.
|
||||
- Add multiple conditions as needed. All conditions are combined with a logical AND, so users must satisfy all of them.
|
||||
4. Assign channels under **Assigned channels**:
|
||||
- Select **Add channels** to search for and select private channels within the team.
|
||||
- Channels already assigned to this policy are excluded from the search results to prevent duplicates.
|
||||
- A channel can be assigned to multiple policies simultaneously; all policies are enforced independently.
|
||||
5. Optionally, toggle **Auto-add members** per channel to control whether users matching the rules are automatically added to that channel.
|
||||
6. Select **Save** to apply the policy.
|
||||
|
||||
<Important>
|
||||
|
||||
Self-exclusion prevention is enforced during save. If your defined rules would exclude your own user account, Mattermost will block the save and display an error. Adjust your rules to include your own attributes before saving.
|
||||
|
||||
</Important>
|
||||
|
||||
### Edit a policy
|
||||
|
||||
Select a policy row in the list, or use the three-dot menu and select **Edit**, to open the policy editor. You can update the policy name, access rules, and channel assignments. Select **Save** to apply changes.
|
||||
|
||||
When saving changes that affect existing channel membership, a confirmation dialog shows how many users will be added or removed. Confirm to proceed.
|
||||
|
||||
### Delete a policy
|
||||
|
||||
You can only delete a policy that has no channels assigned to it. To delete a policy:
|
||||
|
||||
1. Open the policy editor and remove all assigned channels using the **Remove** link next to each channel.
|
||||
2. Once all channels are removed, select **Delete policy** at the bottom of the editor.
|
||||
3. Confirm the deletion.
|
||||
|
||||
### Auto-add members
|
||||
|
||||
Each channel assigned to a policy has an independent **Auto-add members** toggle:
|
||||
|
||||
- **Enabled**: Users matching the policy rules are automatically added to the channel. If a user loses the required attributes and later regains them, they are automatically re-added.
|
||||
- **Disabled** (default): The policy enforces rules as a gate, removing users who no longer qualify, but does not automatically add new qualifying users.
|
||||
|
||||
Regardless of this setting, users who no longer satisfy the access rules are always removed during the next synchronization.
|
||||
|
||||
<Note>
|
||||
|
||||
If a system-wide policy has auto-sync enabled for a channel, Team Admins cannot disable it for that channel. If a system-wide policy has auto-sync disabled, Team Admins can choose to enable it.
|
||||
|
||||
</Note>
|
||||
|
||||
## Policy inheritance and combination
|
||||
|
||||
When both a system-wide policy and a team-level policy apply to the same channel, both must be satisfied. Team-level policies are **additive** and cannot relax or override restrictions set by System Admins.
|
||||
|
||||
- System-wide policies are managed in the System Console.
|
||||
- Team-level policies are managed in Team Settings.
|
||||
- Users must meet the rules of all applicable policies to access the channel.
|
||||
|
||||
### Cross-team policies
|
||||
|
||||
A policy that has private channels from more than one team is considered a cross-team policy. Cross-team policies are not visible in any team's **Channel Membership** tab — they are managed exclusively through the System Console.
|
||||
|
||||
If a System Admin adds a channel from another team to a policy that was previously scoped to one team, that policy will no longer appear in any team's **Channel Membership** tab.
|
||||
|
||||
## Synchronization
|
||||
|
||||
When you save a policy or modify channel assignments, Mattermost creates a membership synchronization job. Changes are applied as soon as the job completes. Synchronization also runs automatically on a schedule to handle attribute changes from external systems such as LDAP or SAML. The interval is set by `AccessControlSettings.SyncJobIntervalSeconds` and defaults to 3600 seconds (60 minutes). User attributes used for access control evaluation are refreshed on a separate schedule controlled by `AccessControlSettings.AttributeRefreshIntervalSeconds`, which defaults to 30 seconds.
|
||||
|
||||
## Troubleshooting and FAQs
|
||||
|
||||
### Why can't I see the Channel Membership tab in Team Settings?
|
||||
|
||||
The **Channel Membership** tab is only visible when:
|
||||
|
||||
- You have Team Admin permissions.
|
||||
- ABAC is enabled system-wide by a System Admin in **System Console \> System Attributes \> Attribute-Based Access**.
|
||||
|
||||
### Why can't I see a policy that I know exists?
|
||||
|
||||
There are two reasons a policy may not appear in your **Channel Membership** tab:
|
||||
|
||||
- **Cross-team policy**: The policy includes private channels from more than one team. Cross-team policies are not visible in Team Settings for anyone and must be managed through the System Console.
|
||||
- **Self-inclusion filter**: Your own user attributes do not satisfy the policy's access rules. For example, if a policy requires `Department=Engineering` and your profile has `Department=Finance`, you will not see that policy. A System Admin or another Team Admin whose attributes do satisfy the rules would need to manage it instead.
|
||||
|
||||
### What happens when I save rules that would exclude me?
|
||||
|
||||
Mattermost validates your access rules against your own user attributes before saving. If the rules would remove you from any assigned channel, the save is blocked and an error is shown. Adjust the rules to include your own attributes, or reset them using the **Undo** option in the save panel.
|
||||
|
||||
### Can I assign a channel to more than one team-level policy?
|
||||
|
||||
Yes. A private channel can be assigned to multiple membership policies. Each policy's rules are applied independently, and users must satisfy all of them to access the channel.
|
||||
|
||||
### Can Team Admins override system-wide policies?
|
||||
|
||||
No. Team-level policies are always additive. Users must satisfy both the system-wide policy and the team-level policy to access a channel. Team Admins cannot weaken or bypass restrictions set by System Admins.
|
||||
|
||||
### How are membership changes communicated to users?
|
||||
|
||||
Users receive standard Mattermost notifications when they are removed from or added to channels due to policy changes, consistent with other membership change notifications.
|
||||
@@ -0,0 +1,448 @@
|
||||
---
|
||||
title: "Team membership access policies"
|
||||
sidebar_label: "Team membership policies"
|
||||
---
|
||||
<PlanAvailability slug="entry-adv" />
|
||||
|
||||
From Mattermost v11.10, system admins and team admins can apply attribute-based access control (ABAC) directly to teams — controlling who can join a team based on user profile attributes. This extends the existing ABAC channel membership system to the team boundary, closing the "hallway access" gap where users who fail channel policies could still see the team, its member list, and its channel structure.
|
||||
|
||||
Team membership ABAC uses the same policies and attribute rules as channel ABAC, but behaves differently depending on whether the team is public or private:
|
||||
|
||||
- **Public teams (advisory mode)**: The policy never blocks access. Anyone can still join freely. Qualifying users who are not yet members see a **Recommended** chip in Browse Teams. Sync can auto-add qualifying users when enabled but never removes anyone.
|
||||
- **Private teams (strict mode)**: The policy gates directory visibility, join evaluation, and membership at sync. Non-qualifying users cannot find or join the team. Non-qualifying members are removed at the next sync.
|
||||
|
||||
<Note>
|
||||
|
||||
**Upgrade notice:** The Access tab in Team Settings has a new UI from Mattermost v11.10 that affects **every team on every deployment** — whether or not ABAC is licensed or enabled. The old "Allow any user to join" checkbox has been replaced by **Public Team / Private Team** selection cards. See [Access tab: Public/Private team cards](#access-tab-publicprivate-team-cards) for details.
|
||||
|
||||
</Note>
|
||||
|
||||
## Access tab: Public/Private team cards
|
||||
|
||||
<Important>
|
||||
|
||||
This change affects **all Mattermost teams on all deployments**, regardless of whether ABAC is enabled or whether your organization has an Enterprise Advanced license. Any team admin who opens **Team Settings \> Access** after upgrading will see the new UI. The exception is teams managed by LDAP/AD group sync, which show a static message in place of the cards — see [Switching modes](#switching-modes).
|
||||
|
||||
</Important>
|
||||
|
||||
**What changed:** The "Allow any user to join" checkbox has been permanently replaced by two selection cards:
|
||||
|
||||
- **Public Team** — anyone on the server can find and join.
|
||||
- **Private Team** — only invited members can join.
|
||||
|
||||
**Why the change was made:** The cards make a team's public-vs-private state explicit and unambiguous, which is what team ABAC enforcement depends on to decide between advisory and strict mode.
|
||||
|
||||
**Behavior without ABAC:** Functionally identical to the old checkbox. Public Team = "Allow any user to join" was ON. Private Team = it was OFF. No other behavior changes for teams that have no membership policy.
|
||||
|
||||
**Behavior with ABAC:** The cards drive the enforcement mode — public teams get advisory ABAC, private teams get strict ABAC. Mode changes on policy-governed teams may trigger a confirmation modal (see below).
|
||||
|
||||
**Default on new teams:** New teams are Private by default, so any policy assigned before explicitly choosing a mode activates strict enforcement. Teams are always protected by default; advisory mode requires explicitly selecting **Public Team**.
|
||||
|
||||
### Switching modes
|
||||
|
||||
**Private → Public:** Relaxes enforcement. Saves directly with no confirmation modal. If a policy is applied, the team immediately transitions to advisory mode: no existing members are removed.
|
||||
|
||||
**Public → Private:** If team ABAC is enabled (Enterprise Advanced license and **Enable Attribute-Based Access Control**) and the team has a membership policy assigned, clicking **Private Team** opens a confirmation modal before saving:
|
||||
|
||||
- Title: **"Switch to Private Team?"**
|
||||
- Body shows how many current members do not meet the policy criteria and will be removed at the next sync. If all current members meet the criteria, the modal says no one will be removed. If no count is available, a generic warning is shown instead.
|
||||
- **Cancel** — closes the modal, no changes made.
|
||||
- **Switch to Private** — saves the change and immediately creates a team sync job to enforce strict mode.
|
||||
|
||||
If the admin making the switch does not meet the policy criteria, a **"Cannot switch to Private Team"** error modal appears instead and the switch is blocked, since strict mode would remove them from the team. Update the rules to include yourself, or ask another admin to make the switch.
|
||||
|
||||
If no policy is assigned, switching Public → Private saves directly with no modal.
|
||||
|
||||
Other Access tab fields (Invite Code and Allowed Email Domain) are unaffected by this change. On teams managed by LDAP/AD group sync, the Access tab shows a static "Members of this team are added and removed by linked groups" message instead of the cards.
|
||||
|
||||
## Advisory and strict enforcement
|
||||
|
||||
Whether a policy is enforced strictly or treated as advisory depends only on whether the team is public or private. The table below summarizes how each combination behaves.
|
||||
|
||||
| Team | ABAC policy? | Join gate | Browse Teams | Sync removal | Recommended tag |
|
||||
|----|----|----|----|----|----|
|
||||
| Public | No | Open | Visible to all | N/A | No |
|
||||
| Public | Yes — **Advisory** | Open (no gate) | Visible to all | Skipped | Yes (qualifying non-members) |
|
||||
| Private | No | Invite-only | Members only | N/A | No |
|
||||
| Private | Yes — **Strict** | Denied for non-qualifying users | Hidden from non-qualifying non-members | Non-qualifiers removed | No |
|
||||
|
||||
<Note>
|
||||
|
||||
Auto-add is the only behavior that applies to **both** advisory and strict mode. When enabled, sync adds qualifying non-members regardless of whether the team is public or private.
|
||||
|
||||
</Note>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before configuring team membership policies:
|
||||
|
||||
1. [Configure user attributes](/administration-guide/manage/admin/user-attributes) in the System Console.
|
||||
2. Go to **System Console \> System Attributes \> Attribute-Based Access** and enable **Enable Attribute-Based Access Control**.
|
||||
3. For Team Admins configuring rules: the `manage_team_access_rules` permission is required. This permission is included in the Team Admin role by default.
|
||||
|
||||
Team membership ABAC is also gated by the `TeamMembershipAccessControl` feature flag, which is **enabled by default** from Mattermost v11.10. Disabling it turns off team enforcement without affecting channel ABAC. See the Mattermost developer documentation for details on [feature flags in a self-hosted deployment](https://developers.mattermost.com/contribute/more-info/server/feature-flags/#self-hosted-and-local-development).
|
||||
|
||||
**What changes when team membership ABAC is off:**
|
||||
|
||||
| Surface | Team membership ABAC ON | Team membership ABAC OFF |
|
||||
|----|----|----|
|
||||
| Join gate (private teams) | Enforced — non-qualifying users denied | Not evaluated — private teams stay invite-only, as before |
|
||||
| Browse Teams filter | Private+ABAC teams hidden from non-qualifying users | No attribute filtering — standard privacy rules apply |
|
||||
| Team Membership tab (Team Settings) | Visible to Team Admins and System Admins | Hidden — tab does not appear |
|
||||
| System Console per-team ABAC controls | Policy assignment + custom rules panel visible | Hidden |
|
||||
| Recommended tag | Shown to qualifying users on public+ABAC teams | Not shown |
|
||||
| Sync job — removal pass | Removes non-qualifiers from private+ABAC teams | No team sync jobs run |
|
||||
| Invite modal filtering | Filters on private+ABAC teams | All users shown, as before |
|
||||
| Public/Private cards (Access tab) | **Visible** — not gated | **Still visible** — rendered on every team except group-synced ones |
|
||||
| Channel ABAC | Unaffected | Unaffected |
|
||||
|
||||
## System Admin configuration
|
||||
|
||||
### Assign a membership policy to a team
|
||||
|
||||
System Admins assign existing system-wide membership policies to teams via the per-team System Console page.
|
||||
|
||||
1. Go to **System Console \> User Management \> Teams** and open the team you want to configure.
|
||||
2. In the **Team Management** section, enable **Manage membership with attribute based membership policies**. The **Membership policies** and **Team-specific membership rules** sections appear only once this toggle is on. The toggle is unavailable on group-synced teams, and becomes read-only once a policy is linked.
|
||||
3. In the **Membership policies** section, select **Link to a policy** and choose an existing policy from the picker.
|
||||
4. Select **Save**.
|
||||
|
||||
Before saving, an **Apply membership policy** confirmation modal appears. It shows only the outcomes that apply to this team:
|
||||
|
||||
- The number of current members who do not meet the policy criteria and will be removed at the next sync — private teams only, and only when that number is above zero.
|
||||
- The number of qualifying users who will be added at the next sync — only when auto-add is enabled and that number is above zero.
|
||||
- An **empty-team warning** if the team is private and no user matches the policy ("Saving may result in an empty private team").
|
||||
- A confirmation prompt: "Are you sure you want to apply the membership policy?"
|
||||
|
||||
**To remove a policy:** Select the trash icon next to the policy row and confirm in the **"Remove this team from policy"** modal, then **Save**. Existing members are retained and the team returns to its standard access mode at the next sync.
|
||||
|
||||
**Policy list team counts:** The **Membership Policies** list page shows the count of channels and teams each policy is applied to — for example, `2 channels, 1 team`. A zero side is omitted (so `1 team` not `0 channels, 1 team`).
|
||||
|
||||
### Configure team-specific membership rules
|
||||
|
||||
In addition to a system-wide parent policy, System Admins can define team-specific rules directly on the per-team System Console page.
|
||||
|
||||
1. Go to **System Console \> User Management \> Teams** and open the team.
|
||||
|
||||
2. Enable the **Manage membership with attribute based membership policies** switch.
|
||||
|
||||
3. Scroll to the **Team-specific membership rules** panel.
|
||||
|
||||
4. Select **Add attribute** to add a condition. For each condition, choose:
|
||||
|
||||
- **Attribute**: The user profile attribute to evaluate.
|
||||
- **Operator**: How the attribute must match. Options: **Is**, **Is not**, **In**, **Starts with**, **Ends with**, **Contains**. For ranked attributes: **Is exactly**, **Is at least**, **Is greater than**, **Is at most**, **Is less than**.
|
||||
- **Value**: The required attribute value.
|
||||
|
||||
All conditions are combined with a logical AND — users must satisfy all of them.
|
||||
|
||||
5. Optionally, check **Auto-add members based on access rules**. This checkbox is enabled once the team has at least one rule **or** a linked parent policy. There is only one auto-add setting per team — it is not configured per linked policy.
|
||||
|
||||
6. Select **Save**. The **Apply membership policy** modal shows the impact counts before changes are applied.
|
||||
|
||||
<Important>
|
||||
|
||||
Saving rules always triggers an immediate team sync — **even when auto-add is off**. On private teams, this immediately enforces the rules by removing members who no longer qualify. Confirm the impact in the save modal before proceeding.
|
||||
|
||||
</Important>
|
||||
|
||||
<Note>
|
||||
|
||||
Team-specific rules compose additively with any parent policy assigned to the team. A user must satisfy **both** the parent policy and the team-specific rules. Team-specific rules cannot weaken or bypass the parent.
|
||||
|
||||
</Note>
|
||||
|
||||
### Sync status footer (System Console)
|
||||
|
||||
A sync status footer is rendered at the bottom of the **Team-specific membership rules** panel — scroll down within the panel to find it.
|
||||
|
||||
- **"Never synced."** with a **Sync now** link — no sync has run for this team yet.
|
||||
- **"Last synced N minutes ago."** with a **Sync now** link — after a prior sync. Older syncs are reported in hours or days.
|
||||
- Clicking **Sync now** changes the link to **"Syncing…"** with a spinner while the job is in-flight, then updates to **"Last synced just now."** on completion.
|
||||
|
||||
### Monitor membership sync
|
||||
|
||||
System Admins can review the results of team sync jobs from the System Console.
|
||||
|
||||
1. Go to **System Console \> System Attributes \> Membership Policies** and scroll to the **Membership Sync Jobs** panel.
|
||||
2. Select a job row to open the **Sync Job Details** modal.
|
||||
3. Select the **Teams** tab.
|
||||
|
||||
The Teams tab shows per-team rows with:
|
||||
|
||||
- A `+N added / −N removed` summary for each governed team.
|
||||
- A **mass-removal warning indicator** on any team row where more than 50% of members would be removed. This is a warning only — the job is not blocked.
|
||||
- An expandable per-team user drill-down showing which users were added and removed.
|
||||
|
||||
<Note>
|
||||
|
||||
The Teams tab appears only when viewing a team sync job directly, or a channel sync job that was chained from a team sync. The system console section heading was renamed from "Channel access control sync jobs" to **"Membership Sync Jobs"** to reflect both job types.
|
||||
|
||||
</Note>
|
||||
|
||||
<Tip>
|
||||
|
||||
The **Run Channel Sync** button on the Membership Policies page runs a channel sync only, not a team sync. To trigger a team sync, use the **Sync now** link on the per-team System Console page or in Team Settings \> Team Membership tab.
|
||||
|
||||
</Tip>
|
||||
|
||||
## Team Admin configuration
|
||||
|
||||
### Team Membership tab
|
||||
|
||||
From Mattermost v11.10, Team Settings has four tabs: **Info**, **Access**, **Team Membership**, and **Channel Membership**.
|
||||
|
||||
<Note>
|
||||
|
||||
The fourth tab was previously labeled **Membership Policies**. It is now labeled **Channel Membership** and covers channel-scope policies only. The new **Team Membership** tab (the third tab) is for policies that control who can join the team itself.
|
||||
|
||||
</Note>
|
||||
|
||||
The **Team Membership** tab lets Team Admins view the system policy applied to their team and configure team-specific custom rules — without requiring System Admin involvement for every change.
|
||||
|
||||
The tab is visible only when:
|
||||
|
||||
- **Enable Attribute-Based Access Control** is on.
|
||||
- The `TeamMembershipAccessControl` feature flag is on (enabled by default).
|
||||
- The user has the `manage_team_access_rules` permission (Team Admin or System Admin).
|
||||
|
||||
To open Team Settings: select the team name in the sidebar → **Team Settings** → **Team Membership** tab.
|
||||
|
||||
### System policy indicator
|
||||
|
||||
When a system-level policy is applied to the team by a System Admin, a non-dismissible information banner appears at the top of the Team Membership tab, showing the name of the applied policy. The banner is absent when no parent policy exists.
|
||||
|
||||
### Configure custom rules
|
||||
|
||||
Team Admins can add attribute rules that apply on top of any system policy. These rules use the same Basic Mode editor as channel rules.
|
||||
|
||||
1. Select **Add attribute**.
|
||||
2. Choose the attribute, operator, and value for each condition.
|
||||
3. Add additional rules as needed. All rules are AND-combined.
|
||||
4. Select **Save** to open the save confirmation modal.
|
||||
|
||||
Clearing every rule and saving opens a **"Remove membership rules?"** modal instead. Confirming removes the team's attribute enforcement; current members keep their access.
|
||||
|
||||
Available operators: **Is**, **Is not**, **In**, **Starts with**, **Ends with**, **Contains**. For ranked attributes: **Is exactly**, **Is at least**, **Is greater than**, **Is at most**, **Is less than**.
|
||||
|
||||
<Important>
|
||||
|
||||
Saving rules always triggers an immediate team sync — **even when auto-add is off**. On private teams, this removes members who no longer satisfy the rules. Review the impact counts in the confirmation modal before saving.
|
||||
|
||||
</Important>
|
||||
|
||||
<Note>
|
||||
|
||||
Team Settings rules are Basic Mode only. Advanced CEL expressions are not available here. For complex rules with nested logic or mixed operators, a System Admin must create a system-wide parent policy in the System Console.
|
||||
|
||||
</Note>
|
||||
|
||||
### Auto-add members
|
||||
|
||||
The **Auto-add members based on access rules** checkbox controls whether the sync job automatically adds qualifying non-members to the team.
|
||||
|
||||
- This checkbox is enabled only when at least one rule exists, **or** when a system-level policy is applied.
|
||||
- Checking it and saving triggers an immediate backfill scan — qualifying non-members are added right away.
|
||||
- Subsequent syncs continue to add newly qualifying users.
|
||||
- Turning auto-add **off** does not remove any members — it is additive only. Toggling auto-add off also does **not** trigger a sync job.
|
||||
|
||||
### Test access rule
|
||||
|
||||
Select **Test access rule** to open a preview modal listing users who match the current rules, before saving. This lets you verify your intended scope without applying the rules.
|
||||
|
||||
<Note>
|
||||
|
||||
If the current rules would exclude the editing Team Admin, the **Test access rule** button is disabled with an explanatory tooltip rather than returning results.
|
||||
|
||||
</Note>
|
||||
|
||||
### Save confirmation and safety
|
||||
|
||||
Selecting **Save** opens a confirmation modal before changes are applied. What it shows depends on the team's mode:
|
||||
|
||||
**Private teams (strict):**
|
||||
|
||||
- The number of users who match the current rules and will have access.
|
||||
- The number of current members who do not match the rules and may be affected — shown only when that number is above zero.
|
||||
|
||||
**Public teams (advisory):**
|
||||
|
||||
- A note that the rules are advisory: no one is blocked or removed.
|
||||
- The number of users who match the current rules.
|
||||
|
||||
The counts are evaluated against the team's rules combined with any parent policy.
|
||||
|
||||
**Empty-team warning**: For private teams, if no user matches the rules, the modal displays a highlighted warning. Save is not blocked, but the team will be empty until sync runs.
|
||||
|
||||
**Self-exclusion hard block**: On private teams, if the rules would exclude the editing Team Admin, a separate error modal appears *before* the confirmation modal and prevents saving. Select **Back to editing** to adjust the rules. Public teams are exempt — advisory mode never removes anyone.
|
||||
|
||||
<Note>
|
||||
|
||||
By design, the self-exclusion block evaluates only the admin's own team rules. A system-level parent policy that excludes the admin is a System Admin decision and is not blocked here.
|
||||
|
||||
</Note>
|
||||
|
||||
### Sync status footer (Team Settings)
|
||||
|
||||
Below the auto-add section, a sync status footer appears whenever the team has at least one rule or a parent policy applied:
|
||||
|
||||
- **"Never synced."** + **Sync now** link.
|
||||
- **"Last synced N minutes ago."** + **Sync now** link.
|
||||
- After clicking **Sync now**: **"Syncing…"** with spinner → **"Last synced just now."** on completion.
|
||||
|
||||
The footer is not shown in the empty state (no rules and no parent policy).
|
||||
|
||||
Clicking **Sync now** creates a team sync job scoped to this team only.
|
||||
|
||||
## End-user experience
|
||||
|
||||
### Browse Teams
|
||||
|
||||
The Browse Teams directory respects team ABAC enforcement:
|
||||
|
||||
**Private + ABAC teams (strict mode):**
|
||||
|
||||
- Non-qualifying users who are not already members do not see the team in Browse Teams at all. The team is simply absent — not greyed out or locked.
|
||||
- Qualifying users and existing members see the team normally.
|
||||
|
||||
**Public + ABAC teams (advisory mode):**
|
||||
|
||||
- All users see the team.
|
||||
- Qualifying users who are not yet members see a **Recommended** chip (lightbulb outline icon) next to the team name.
|
||||
- Users who are already members, or who do not qualify for the policy, do not see the chip.
|
||||
|
||||
**Teams without a policy:**
|
||||
|
||||
- Behave exactly as before — no filtering, no chip.
|
||||
|
||||
### Invite People modal
|
||||
|
||||
The Invite People modal adapts to the team's enforcement mode:
|
||||
|
||||
**Private + ABAC team (strict mode):**
|
||||
|
||||
- Only users who qualify for the team's membership policy appear in search results. Non-qualifying users are filtered out before any results are shown.
|
||||
- A notice titled **"Team access is restricted by user attributes"** informs admins: *"Only users who meet the membership requirements can be added to this team."*
|
||||
- The notice lists the policy's attribute value tags (for example, "Department: Engineering").
|
||||
- The invite link warns: *"People who use this link must meet the membership requirements to join."*
|
||||
|
||||
**Public + ABAC team (advisory mode):**
|
||||
|
||||
- All users appear in search results — there is no filtering because advisory mode never blocks access.
|
||||
- A notice titled **"This team has membership requirements"** is still shown, explaining that users who do not meet them can still join but will not be automatically added.
|
||||
- The invite link warns: *"People who use this link can join even if they do not meet the membership requirements, but will not be automatically added."*
|
||||
|
||||
**Team without a policy:**
|
||||
|
||||
- No change from previous behavior — all users appear, no notice shown.
|
||||
|
||||
### Add Members flow (admin)
|
||||
|
||||
When a System Admin or Team Admin uses the **Add Members** admin flow on a private + ABAC team, non-qualifying users are blocked at selection time rather than at submission:
|
||||
|
||||
- Non-qualifying candidates show an inline **"Does not meet membership requirements"** indicator on their row and cannot be selected, including by keyboard.
|
||||
- Only qualifying users can be added to the selection, so the add never fails partway through.
|
||||
|
||||
On a public + ABAC team (advisory mode), all users can be added without restriction.
|
||||
|
||||
### Team Members modal
|
||||
|
||||
When a team has an active membership policy, a notice banner appears at the top of the Team Members modal:
|
||||
|
||||
- On private teams: **"Team access is restricted by user attributes"** — only people who meet the membership requirements can be members.
|
||||
- On public teams: **"This team has membership requirements"** — people who do not meet them can still join, but will not be automatically added.
|
||||
- The banner lists the policy's attribute value tags (for example, "Department: Engineering"). Values the viewer is not permitted to see are stripped server-side, so a non-holder sees only the notice.
|
||||
|
||||
### Membership notifications
|
||||
|
||||
**Removal notification:** When the sync job removes a non-qualifying member from a private team, that user receives a direct message from the System Bot identifying the team they were removed from. The message does not include policy names, attribute values, or CEL expressions.
|
||||
|
||||
**Auto-add notification:** When the sync job auto-adds a qualifying user to a team, that user receives a direct message from the System Bot identifying the team they were added to. Removal and auto-add notifications use different icons so users can distinguish them at a glance.
|
||||
|
||||
## Policy inheritance and composition
|
||||
|
||||
When a parent (system-level) policy and custom team rules both apply:
|
||||
|
||||
- The parent policy is shown in a non-dismissible info banner at the top of the Team Membership tab (read-only).
|
||||
- The user must satisfy **both** the parent policy and the custom team rules to be admitted.
|
||||
- Custom rules can only add restrictions — they cannot weaken or bypass the parent policy.
|
||||
- Deleting a parent policy does not unlink it from the team or delete the team's own rules. Unlink the policy from each team before deleting it.
|
||||
|
||||
## Sync behavior
|
||||
|
||||
A team sync batch runs in the following order:
|
||||
|
||||
1. **Evaluate team membership rules** for all teams that have a policy assigned.
|
||||
2. **Removal pass** (private + ABAC teams only): remove members who no longer qualify. Each removal triggers a channel cascade — the user is removed from all channels within the team. Each removal notifies the user by system-bot DM and is recorded in the audit log. Ordinary, non-ABAC team leaves are not recorded as policy-driven cascades.
|
||||
3. **Auto-add pass** (any mode, when auto-add is enabled): add qualifying non-members to the team. Each addition emits a system-bot DM.
|
||||
4. **Channel membership sync** (chained): evaluate and apply channel policy changes. Deduped against any already-pending or in-progress channel sync job.
|
||||
|
||||
**Mass-removal guardrail**: If a sync pass would remove more than 50% of a team's current members, the job sets a warning flag visible in the Sync Job Details \> Teams tab. The job is not blocked — all removals proceed — but the warning is surfaced for admin review.
|
||||
|
||||
Team sync also runs automatically on a schedule to handle attribute changes propagated from LDAP or SAML. The interval is set by `AccessControlSettings.SyncJobIntervalSeconds` and defaults to 3600 seconds (60 minutes). The minimum accepted value is 60 seconds, and a change requires a server restart to take effect. The same interval governs channel membership sync. User attributes used for access control evaluation are refreshed on a separate schedule controlled by `AccessControlSettings.AttributeRefreshIntervalSeconds`, which defaults to 30 seconds. Set this value to 0 to disable periodic attribute refresh.
|
||||
|
||||
## Mutual exclusivity with group sync
|
||||
|
||||
A team managed by LDAP/AD group synchronization cannot have an ABAC membership policy assigned. Attempting to assign a policy to a group-synced team returns an error.
|
||||
|
||||
You must choose one membership control mechanism per team: group sync or ABAC.
|
||||
|
||||
## Troubleshooting and FAQs
|
||||
|
||||
### Why did the Access tab change and where did the "Allow any user to join" checkbox go?
|
||||
|
||||
From Mattermost v11.10, the "Allow any user to join" checkbox has been permanently replaced by **Public Team** and **Private Team** selection cards on the Access tab. This change applies to all teams on all deployments regardless of ABAC. The cards are functionally equivalent to the old checkbox — Public Team is the same as having the checkbox enabled, Private Team is the same as having it disabled.
|
||||
|
||||
### Why is the Team Membership tab not visible in Team Settings?
|
||||
|
||||
The tab requires all of the following:
|
||||
|
||||
- **Enable Attribute-Based Access Control** is ON in System Console \> System Attributes \> Attribute-Based Access.
|
||||
- The `TeamMembershipAccessControl` feature flag is ON (enabled by default).
|
||||
- The user has the `manage_team_access_rules` permission (Team Admin or System Admin).
|
||||
|
||||
If all three conditions are met but the tab is still missing, confirm the feature flag has not been disabled by checking **System Console \> Experimental \> Feature Flags** or the server configuration.
|
||||
|
||||
### Why are private ABAC teams not appearing in Browse Teams for some users?
|
||||
|
||||
This is intended behavior. When a private team has an ABAC policy and a user does not satisfy that policy, the team is completely hidden from them in Browse Teams and search results. They will not see an error or a locked entry — the team simply does not appear. Qualifying users see the team normally.
|
||||
|
||||
### Can I use advanced CEL expressions in Team Settings?
|
||||
|
||||
No. The Team Membership tab uses Basic Mode only — the same simple attribute editor available in Channel Settings. For rules that require nested logic, mixed `&&` / `||` operators, or grouping, a System Admin must create a system-wide parent policy using the CEL editor in the System Console.
|
||||
|
||||
### Can a group-synced team use ABAC?
|
||||
|
||||
No. Group sync and ABAC are mutually exclusive on a per-team basis. If a team is group-constrained, assigning an ABAC policy returns an error. You must remove the group sync configuration before applying an ABAC policy, or vice versa.
|
||||
|
||||
### How quickly are membership changes applied?
|
||||
|
||||
Saving rules in Team Settings or the System Console triggers an immediate team sync — **including when auto-add is off**, because enforcement (removal on private teams) runs regardless of the auto-add setting. Changes are applied as soon as the sync completes. A scheduled sync also runs on the interval set by `AccessControlSettings.SyncJobIntervalSeconds` (60 minutes by default) to process attribute changes from LDAP or SAML. User attributes used for access control evaluation are refreshed on the interval set by `AccessControlSettings.AttributeRefreshIntervalSeconds` (30 seconds by default).
|
||||
|
||||
### Does System Admin role bypass team ABAC enforcement?
|
||||
|
||||
No. All roles — including System Admin — are subject to team ABAC policy evaluation. A System Admin who does not satisfy a private team's policy cannot join that team as a member through the standard UI, though they can still access the team's configuration in the System Console and make changes.
|
||||
|
||||
### What happens when I switch a team from public to private while a policy is assigned?
|
||||
|
||||
If team ABAC is enabled (Enterprise Advanced license and **Enable Attribute-Based Access Control**) and a policy is assigned to the team, clicking **Private Team** opens a confirmation modal — **"Switch to Private Team?"** — showing how many current members do not meet the policy criteria. Confirming the switch saves the change and triggers an immediate sync to enforce strict mode. Members who don't qualify are removed at that sync.
|
||||
|
||||
If the admin making the switch does not meet the criteria themselves, the switch is blocked with a **"Cannot switch to Private Team"** modal.
|
||||
|
||||
If no policy is assigned, the switch saves directly with no confirmation.
|
||||
|
||||
### What happens when I switch a team from private to public while a policy is assigned?
|
||||
|
||||
Switching from Private to Public always saves directly with no confirmation modal. The team immediately transitions to advisory mode: existing members (including non-qualifying ones) are retained, and no removals occur. The policy continues to drive the Recommended tag and optional auto-add for qualifying users.
|
||||
|
||||
### Can I add a non-qualifying user to a private ABAC team?
|
||||
|
||||
No. The enforcement gate applies to all add paths: the Invite People modal (non-qualifying users are filtered from search results entirely), the Add Members admin flow (non-qualifying users cannot be selected), and direct API calls. The error message is generic and does not expose policy names or attribute details.
|
||||
|
||||
### How does ABAC interact with email-domain restrictions?
|
||||
|
||||
Email-domain restrictions (configured on the Access tab) and ABAC rules (Team Membership tab) apply independently, and a user must satisfy both to join the team. The domain check runs first, so a user rejected on domain grounds never reaches policy evaluation. The two settings are not cross-validated at configuration time, so review them together when debugging unexpected access denials.
|
||||
|
||||
### Why does saving rules trigger a sync even when auto-add is off?
|
||||
|
||||
Auto-add controls only the **add pass** (whether sync adds qualifying non-members). The **enforcement pass** (removing non-qualifying members from private teams) runs whenever rules exist, regardless of auto-add. This ensures that newly saved or changed rules take effect immediately rather than waiting up to 60 minutes for the next scheduled sync.
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: "Attribute-Based Access Control"
|
||||
---
|
||||
<PlanAvailability slug="entry-adv" />
|
||||
|
||||
From Mattermost v10.9, system admins in large or complex organizations who require Zero Trust Security when handling with sensitive information can prevent unauthorized access through attribute-based access controls.
|
||||
|
||||
Enforcing strict access controls based on user attributes eliminates manual role adjustment processes that can lead to security risks, inefficiencies, or inappropriate access, while maintaining security and compliance by ensuring that only authorized users can access specific private Mattermost channels and teams. On public channels and teams, policies are advisory rather than a gate — see the advisory and strict behaviour described below.
|
||||
|
||||
Attribute-based access control (ABAC) can be used with the following policy types:
|
||||
|
||||
- **System-wide access policies** (managed by System Admins): Centralized policies created in the System Console that can be applied across multiple channels and teams. See [System-wide attribute-based access policies](/administration-guide/manage/admin/abac-system-wide-policies).
|
||||
- **Permission policies** (managed by System Admins): Attribute-based restrictions on user actions such as file upload and file download. See [Permission policies](/administration-guide/manage/admin/abac-system-wide-policies#permission-policies).
|
||||
- **Team membership policies** (managed by System Admins and Team Admins): Attribute-based rules that control who can join a team. On private teams, rules gate directory visibility, join evaluation, and removal at sync (strict mode). On public teams, rules drive a "Recommended" tag and optional auto-add without restricting access (advisory mode). See [Team membership access policies](/administration-guide/manage/admin/abac-team-membership).
|
||||
- **Team-scoped channel membership policies** (managed by Team Admins): Channel membership policies that Team Admins can create, edit, and delete directly from the Channel Membership tab in Team Settings for channels in their team. See [Team-level channel membership policies](/administration-guide/manage/admin/abac-team-channel-policies).
|
||||
- **Channel-specific access rules** (managed by Channel Admins): Self-service access rules that Channel Admins can configure directly in Channel Settings for individual channels. See [Channel-specific access rules](/administration-guide/manage/admin/abac-channel-access-rules).
|
||||
|
||||
From Mattermost v11.8, ABAC policies can be applied to **both private and public channels**, with deliberately different semantics for each:
|
||||
|
||||
- **Private channels** are hard-gated by the policy. Non-matching members are removed during synchronization and only matching users can be added or invited.
|
||||
- **Public channels** are advisory. Anyone can still join freely, no member is ever removed by ABAC, and the policy is used either to **auto-add** matching users (when enabled) or to **recommend** the channel under **Browse Channels \> Recommended channels** (when auto-add is disabled).
|
||||
|
||||
Default channels (Town Square, Off-Topic), shared channels, and group-synced channels remain ineligible.
|
||||
|
||||
From Mattermost v11.10, ABAC policies can also be applied to **teams**, with the same advisory/strict model keyed on whether the team is public or private. See [Team membership access policies](/administration-guide/manage/admin/abac-team-membership) for details.
|
||||
|
||||
## Before you begin
|
||||
|
||||
Attribute-based access controls require defined user attributes that are either synchronized from an external system (such as LDAP or SAML) or manually configured and enabled on your Mattermost server. You'll need to [configure user attributes](/administration-guide/manage/admin/user-attributes) in the System Console first before creating access policies.
|
||||
|
||||
Each attribute becomes a user profile option users can populate, unless you disable the **Editable by Users** option, available from Mattermost v11. Admin-managed attributes can be used in addition to the LDAP/SAML synchronized attributes for attribute-based access control rules.
|
||||
|
||||
Once user attributes are defined, go to **System Console \> System Attributes \> Attribute-Based Access** to enable attribute-based access controls for your Mattermost instance. This functionality requires a Mattermost Enterprise Advanced license.
|
||||
|
||||
From Mattermost v10.11, user-managed attributes are excluded from attribute-based access control (ABAC) rules by default for security reasons. This prevents access control policies from being circumvented by users editing their own profile attributes. To include user-managed attributes in ABAC rules, a system admin must explicitly enable the `EnableUserManagedAttributes` configuration setting. See the [user attribute](/administration-guide/manage/admin/user-attributes#before-you-begin) documentation for details on enabling this feature. This configuration setting is available only in Enterprise Edition Advanced and is disabled by default.
|
||||
|
||||
## Configure access policies
|
||||
|
||||
Once enabled, you have multiple ways to configure access policies in Mattermost:
|
||||
|
||||
From Mattermost v11.8.0, admins can configure membership policies for both public and private channels, permission policies for file upload and file download, and simulate policy outcomes before saving.
|
||||
|
||||
**System Admins can:**
|
||||
|
||||
- Create [system-wide access policies](/administration-guide/manage/admin/abac-system-wide-policies) that can be assigned across multiple channels and teams in the System Console. Membership policies can be applied to both public and private channels, with [advisory behavior on public channels](/administration-guide/manage/admin/abac-channel-access-rules#public-and-private-channel-behavior).
|
||||
- Assign [individual channel policies](/administration-guide/manage/admin/abac-system-wide-policies#define-access-controls-per-channel) to specific channels in the System Console.
|
||||
- [Assign membership policies to teams](/administration-guide/manage/admin/abac-team-membership#assign-a-membership-policy-to-a-team) via the per-team System Console page, with advisory enforcement on public teams and strict enforcement on private teams.
|
||||
- Define [permission policies](/administration-guide/manage/admin/abac-system-wide-policies#permission-policies) that restrict actions such as file upload and file download based on user attributes.
|
||||
- [Simulate policy outcomes](/administration-guide/manage/admin/abac-system-wide-policies#simulate-access) to preview whether selected users can perform actions such as joining a channel or uploading and downloading files before saving policy changes.
|
||||
|
||||
**Team Admins can:**
|
||||
|
||||
- View the system policy applied to their team and [configure custom team membership rules](/administration-guide/manage/admin/abac-team-membership#configure-custom-rules) directly from the **Team Membership** tab in Team Settings, when granted the `manage_team_access_rules` permission.
|
||||
- Create and manage [team-level channel membership policies](/administration-guide/manage/admin/abac-team-channel-policies) from the **Channel Membership** tab in Team Settings, scoping attribute-based rules to one or more channels within their team.
|
||||
|
||||
**Channel Admins can:**
|
||||
|
||||
- Configure [channel-specific access rules](/administration-guide/manage/admin/abac-channel-access-rules) directly in Channel Settings without requiring a system admin.
|
||||
225
docs/main/administration-guide/manage/admin/autotranslation.mdx
Normal file
225
docs/main/administration-guide/manage/admin/autotranslation.mdx
Normal file
@@ -0,0 +1,225 @@
|
||||
---
|
||||
title: "Set up auto-translation (Beta)"
|
||||
---
|
||||
<PlanAvailability slug="ent-adv" />
|
||||
|
||||
From Mattermost v11.5, auto-translation automatically translates channel messages into each user's preferred display language. This enables multilingual teams to collaborate without language barriers.
|
||||
|
||||
Auto-translation uses an asynchronous queue-based architecture. When a message is posted in a channel with auto-translation enabled, the message is queued for translation into every configured target language. Translated messages replace the original display for users whose display language matches a target language, and they can view the original text at any time by selecting the translation icon on the message. A user's translation target language is determined by their Mattermost display language (set via **Settings \> Display \> Language**); decoupling the translation target from the display language is not currently supported.
|
||||
|
||||
Two translation provider options are available:
|
||||
|
||||
- **LibreTranslate**: A self-hosted, open-source machine translation engine.
|
||||
- **Agents**: Uses the [Mattermost Agents plugin](/administration-guide/configure/agents-admin-guide) with a configured LLM backend.
|
||||
|
||||
## Before you begin
|
||||
|
||||
- A Mattermost **Enterprise Advanced** license is required.
|
||||
- Choose a translation provider and ensure its infrastructure is available:
|
||||
- **LibreTranslate**: A running LibreTranslate server reachable from the Mattermost server.
|
||||
- **Agents**: The [Mattermost Agents plugin](/administration-guide/configure/agents-admin-guide) installed and configured with at least one LLM service.
|
||||
|
||||
## Set up Auto-translation
|
||||
|
||||
### Configure a translation provider
|
||||
|
||||
Choose one of the following translation providers and follow the setup instructions for your choice.
|
||||
|
||||
#### Set up LibreTranslate
|
||||
|
||||
[LibreTranslate](https://libretranslate.com/) is a self-hosted, open-source machine translation engine. See the [LibreTranslate installation guide](https://docs.libretranslate.com/guides/installation/) for deployment instructions.
|
||||
|
||||
Once your LibreTranslate server is running:
|
||||
|
||||
1. Go to **System Console \> Site Configuration \> Localization**.
|
||||
2. Set **Translation provider** to `libretranslate`.
|
||||
3. Enter the [LibreTranslate URL](/administration-guide/configure/site-configuration-settings#libretranslate-url) (for example, `http://libretranslate.internal:5000`).
|
||||
4. If your LibreTranslate instance requires authentication, enter the [LibreTranslate API key](/administration-guide/configure/site-configuration-settings#libretranslate-api-key).
|
||||
5. Select **Save**.
|
||||
|
||||
<Important>
|
||||
|
||||
The Mattermost server must be able to reach the LibreTranslate URL over the network. Ensure firewall rules and DNS resolution allow connectivity between the Mattermost server and the LibreTranslate instance.
|
||||
|
||||
</Important>
|
||||
|
||||
#### Set up the Agents provider
|
||||
|
||||
The Agents provider uses the Mattermost Agents plugin to translate messages via a configured LLM service.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- The [Mattermost Agents plugin](/administration-guide/configure/agents-admin-guide) is installed and enabled.
|
||||
- At least one LLM service is configured in the Agents plugin.
|
||||
|
||||
To configure:
|
||||
|
||||
1. Go to **System Console \> Site Configuration \> Localization**.
|
||||
2. Set **Translation provider** to `agents`.
|
||||
3. Enter the [Agents LLM service ID](/administration-guide/configure/site-configuration-settings#agents-llm-service-id) matching the LLM service configured in the Agents plugin.
|
||||
4. Select **Save**.
|
||||
|
||||
<Tip>
|
||||
|
||||
**Choosing between LibreTranslate and Agents**: LibreTranslate is a lightweight, self-hosted translation engine. The Agents provider uses an LLM backend and generally produces more accurate translations, especially for languages such as Japanese, Korean, and Chinese where contextual understanding improves quality. Consider your translation quality needs and existing infrastructure when choosing. LibreTranslate does not support direct translation between all language pairs; for unsupported combinations it performs a pivot translation through an intermediate language (typically English), which can reduce accuracy for those pairs.
|
||||
|
||||
**Choosing an LLM for the Agents provider**: Smaller, faster models are recommended for auto-translation. Translation is a well-defined task that doesn't benefit from the extended reasoning capabilities of larger models — larger models may actually overthink the task, adding unnecessary latency without improving quality. Choose a small, low-latency model that is currently supported by your configured LLM provider and validated in your environment.
|
||||
|
||||
</Tip>
|
||||
|
||||
### Enable auto-translation
|
||||
|
||||
1. Go to **System Console \> Site Configuration \> Localization**.
|
||||
2. Set [Enable autotranslation](/administration-guide/configure/site-configuration-settings#enable-auto-translation) to **True**.
|
||||
3. Select a [Translation provider](/administration-guide/configure/site-configuration-settings#translation-provider) (`libretranslate` or `agents`).
|
||||
4. Configure [Languages allowed](/administration-guide/configure/site-configuration-settings#languages-allowed) — every message in auto-translation-enabled channels is translated into each language in this list.
|
||||
5. Select **Save**.
|
||||
|
||||
<Note>
|
||||
|
||||
The languages available in the **Languages allowed** list are controlled by the **Available languages** setting (`AvailableLocales`) under **Site Configuration \> Localization**. If that field is blank, all supported languages are available; otherwise only listed languages appear as selectable auto-translation targets. Note that some locales (such as `zh-Hans`) are in beta — portions of the Mattermost UI may still display in English, which is a UI localization limitation and does not affect message translation.
|
||||
|
||||
</Note>
|
||||
|
||||
Use the [Restrict autotranslation in direct and group messages](/administration-guide/configure/site-configuration-settings#restrict-autotranslation-in-direct-and-group-messages) setting to control whether auto-translation can be enabled in direct and group messages.
|
||||
|
||||
See the [auto-translation configuration reference](../../configure/site-configuration-settings#auto-translation) for all available settings.
|
||||
|
||||
### Enable auto-translation in a channel
|
||||
|
||||
Auto-translation is managed on a per-channel basis and is disabled by default for all channels. System admins and channel admins can enable or disable auto-translation for individual channels.
|
||||
|
||||
- When auto-translation is enabled or disabled in a channel, a system post notifies channel members of the change.
|
||||
|
||||
<Note>
|
||||
|
||||
Enabling auto-translation in a channel only translates new messages going forward. Existing message history is not retroactively translated.
|
||||
|
||||
</Note>
|
||||
|
||||
## Tune worker performance
|
||||
|
||||
For most deployments the default worker settings are sufficient. If your deployment has a high message volume, many configured target languages, or you observe growing translation queue depth, you may need to increase the worker count. This section explains how to calculate and monitor the right values.
|
||||
|
||||
### How the translation queue works
|
||||
|
||||
When a message is posted in a channel with auto-translation enabled, it's added to a per-node translation queue. A worker picks up the post and translates it sequentially into each configured target language. Each completed language translation triggers a websocket broadcast to the channel so users see translations arrive in real time.
|
||||
|
||||
In a high availability deployment, each node runs its own pool of workers and processes its own queue independently.
|
||||
|
||||
### Calculate your worker count
|
||||
|
||||
Use the following formula to estimate how many workers each node needs:
|
||||
|
||||
``` text
|
||||
required_workers = ceil(
|
||||
(posts_per_sec × pct_autotranslated × num_languages × avg_provider_latency_ms / 1000)
|
||||
/ num_app_nodes
|
||||
× 1.2
|
||||
)
|
||||
```
|
||||
|
||||
Where:
|
||||
|
||||
- **posts_per_sec** — average message rate across the server.
|
||||
- **pct_autotranslated** — fraction of posts in auto-translation-enabled channels (0.0–1.0).
|
||||
- **num_languages** — number of configured target languages.
|
||||
- **avg_provider_latency_ms** — mean response time from the translation provider in milliseconds.
|
||||
- **num_app_nodes** — number of Mattermost application nodes.
|
||||
- **1.2** — headroom factor (20%) to absorb traffic bursts.
|
||||
|
||||
The following table shows results from load tests with 6,500 concurrent users, 2 app nodes, ~5 posts/sec, and ~2 s mean provider latency. The provider latency distribution used in testing was realistic, with buckets ranging from 500 ms to 10 s weighted toward the 500 ms–1.5 s range, producing a ~2 s mean.
|
||||
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style={{width: '30%'}} />
|
||||
<col style={{width: '30%'}} />
|
||||
<col style={{width: '40%'}} />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Target languages</th>
|
||||
<th>% autotranslated</th>
|
||||
<th>Workers per node</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>7</td>
|
||||
<td>100%</td>
|
||||
<td>38</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>6</td>
|
||||
<td>100%</td>
|
||||
<td>32</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>75%</td>
|
||||
<td>8</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<Note>
|
||||
|
||||
In the 6-language test the formula yields 33, but workers were capped at 32 due to the configured maximum. Adjust the [Translation workers](/administration-guide/configure/site-configuration-settings#translation-workers) setting to match your calculated value.
|
||||
|
||||
</Note>
|
||||
|
||||
### Scaling considerations
|
||||
|
||||
- **Target languages are the biggest multiplier.** Each additional language increases both worker time per post and the number of websocket broadcasts per post. Reducing the number of target languages is the most effective way to reduce load.
|
||||
- **Websocket traffic scales with languages.** Each target language produces one channel-wide websocket broadcast per translated post. Under high load, the main symptom of saturation is websocket disconnects caused by per-connection send queues filling up.
|
||||
- **Provider latency shapes traffic patterns.** Lower latency means translations complete in quicker bursts, concentrating websocket traffic. Higher latency spreads events over time. Monitor provider response times and factor them into your capacity planning.
|
||||
|
||||
### Monitor with Prometheus
|
||||
|
||||
Mattermost exposes the following Prometheus metrics for auto-translation:
|
||||
|
||||
- `mattermost_autotranslation_queue_depth_total` *(Gauge)* — current tasks waiting in the queue. A steadily rising value means workers can't keep up with incoming posts.
|
||||
|
||||
- `mattermost_autotranslation_provider_call_duration_seconds` *(Histogram; labels: provider, result)* — translation provider latency. This is the `avg_provider_latency_ms` value used in the formula above. Calculate the average with the following PromQL query:
|
||||
|
||||
``` promql
|
||||
rate(mattermost_autotranslation_provider_call_duration_seconds_sum{result="success"}[10m])
|
||||
/
|
||||
rate(mattermost_autotranslation_provider_call_duration_seconds_count{result="success"}[10m])
|
||||
```
|
||||
|
||||
- `mattermost_autotranslation_worker_task_duration_seconds` *(Histogram)* — total time for a worker to process one post across all target languages.
|
||||
|
||||
### Configuration reference
|
||||
|
||||
- [Translation workers](/administration-guide/configure/site-configuration-settings#translation-workers): The number of concurrent workers per node. Default is **6**. Increase this value for high-traffic deployments or decrease it to reduce resource consumption.
|
||||
- [Translation timeout](/administration-guide/configure/site-configuration-settings#translation-timeout): The maximum time in milliseconds for a single translation request. Default is **5000** ms (5 seconds). Increase this value if your translation provider is experiencing timeouts due to network latency or high load.
|
||||
|
||||
You can also update the worker count from the command line using mmctl:
|
||||
|
||||
``` shell
|
||||
mmctl config set AutoTranslationSettings.Workers <number>
|
||||
```
|
||||
|
||||
## Frequently asked questions
|
||||
|
||||
### Why aren't messages being translated?
|
||||
|
||||
- Verify auto-translation is enabled globally in **System Console \> Site Configuration \> Localization**.
|
||||
- Verify auto-translation is enabled for the specific channel.
|
||||
- Confirm a translation provider is selected and properly configured.
|
||||
- Check that the Mattermost server can reach the translation provider (LibreTranslate URL or Agents plugin).
|
||||
- Review Mattermost server logs for translation errors.
|
||||
|
||||
### What happens when the translation provider is unavailable?
|
||||
|
||||
Messages are still posted normally. Translations that fail due to provider downtime are skipped, and users see the original untranslated message. When the provider recovers, new messages are translated as expected.
|
||||
|
||||
### What languages are supported?
|
||||
|
||||
Supported languages depend on the translation provider:
|
||||
|
||||
- **LibreTranslate**: Supports the languages available in your LibreTranslate deployment. See the [LibreTranslate language list](https://libretranslate.com/languages) for details.
|
||||
- **Agents**: Language support depends on the capabilities of the configured LLM. Most modern LLMs support a wide range of languages.
|
||||
|
||||
Configure the [Languages allowed](/administration-guide/configure/site-configuration-settings#languages-allowed) setting to specify which languages all messages are translated into.
|
||||
218
docs/main/administration-guide/manage/admin/content-flagging.mdx
Normal file
218
docs/main/administration-guide/manage/admin/content-flagging.mdx
Normal file
@@ -0,0 +1,218 @@
|
||||
---
|
||||
title: "Data Spillage Handling"
|
||||
---
|
||||
<PlanAvailability slug="entry-adv" />
|
||||
|
||||
Data Spillage Handling helps prevent accidental data spillage and helps system administrators respond quickly to potential leaks without disrupting collaboration. Enabling this feature empowers Mattermost users to report messages that may contain sensitive, regulated, or inappropriate information, and enables designated content reviewers to assess and take appropriate action by removing or dismissing quarantined messages.
|
||||
|
||||
By making every team member a first line of defense against sensitive-data exposure, Data Spillage Handling strengthens mission-critical, secure deployments and supports compliance with organizational and regulatory data-handling standards.
|
||||
|
||||
## Before you begin
|
||||
|
||||
You must be a System Admin in Mattermost. You need to identify who will be content reviewers for quarantined messages, and you need to decide whether quarantined messages should be hidden from users in Mattermost while under review.
|
||||
|
||||
## Enable
|
||||
|
||||
Data Spillage Handling isn't enabled by default. To enable Data Spillage Handling:
|
||||
|
||||
1. Go to **System Console \> Site Configuration \> Data Spillage Handling**.
|
||||
2. Set **Enable Data Spillage Handling** to **True**.
|
||||
|
||||
Alternatively, you can configure Data Spillage Handling via the [config.json file or through environment variables](/administration-guide/configure/site-configuration-settings#content-flagging).
|
||||
|
||||
## Configure
|
||||
|
||||
1. Under **Content Reviewers**, define who should review quarantined content:
|
||||
|
||||
- **Same reviewers for all teams**: Set to **True** to apply one global reviewer list across all teams, or **False** to configure reviewers per team.
|
||||
- **Reviewers**: Start typing to search for users to assign as content reviewers.
|
||||
|
||||
<Important>
|
||||
|
||||
Choose reviewers carefully. Assigning reviewer roles grants access to potentially sensitive information and may expose data from private channels.
|
||||
|
||||
- A global reviewer can view quarantined messages from all teams and channels, including private channels they’re not a member of.
|
||||
- Team-specific reviewers can view quarantined messages from their assigned teams, including private channels within those teams they’re not members of.
|
||||
|
||||
</Important>
|
||||
|
||||
- **Additional reviewers**: Optionally include:
|
||||
- **System Administrators**: System admins receive quarantined messages for all teams they are part of.
|
||||
- **Team Administrators**: Team admins receive quarantined messages for their respective teams.
|
||||
|
||||
2. Under **Notification Settings**, specify who receives updates at each stage of the quarantine workflow when content is quarantined or reviewed:
|
||||
|
||||
- **Notify when content is quarantined**: Reviewer(s), Author.
|
||||
- **Notify when a reviewer is assigned**: Reviewer(s).
|
||||
- **Notify when content is removed**: Reviewer(s), Author, Reporter.
|
||||
- **Notify on dismissal**: Reviewer(s), Author, Reporter.
|
||||
|
||||
All notifications are sent via the **Data Spillage Bot** as direct messages.
|
||||
|
||||
3. Under **Additional Settings**, configure how the quarantine workflow behaves:
|
||||
|
||||
- **Reasons for quarantine**: Define the preset categories that appear in the quarantine dialog for users (for example: **Classification mismatch**, **Need-to-know violation**, **Personally identifiable information (PII) exposure**, **Operational security (OPSEC) concern**, **Controlled Unclassified Information (CUI) violation**).
|
||||
- **Require reporters to add comment**: Set to **True** to require users to add a short explanation when quarantining a message.
|
||||
- **Require reviewers to add comment**: Set to **True** to require reviewers to add a comment when resolving a quarantine.
|
||||
- **Hide message from channel while it is being reviewed**: Set to **True** to automatically hide quarantined messages from the channel until reviews are complete. If a root post is quarantined, the entire thread is hidden.
|
||||
|
||||
<Tip>
|
||||
|
||||
We recommend enabling **Hide message from channel while it is being reviewed** and require comments from both reporters and reviewers to maintain transparency, accountability, and an auditable record of actions.
|
||||
|
||||
</Tip>
|
||||
|
||||
## Monitor quarantined messages
|
||||
|
||||
When [a user quarantines a message](/end-user-guide/collaborate/flag-messages), the **Data Spillage Bot** sends a direct message to all content reviewers.
|
||||
|
||||
Direct messages from the **Data Spillage Bot** is a centralized moderation queue, where reviewers can view, assign, and act on quarantined messages without leaving Mattermost. Reviewers can use it to monitor potential data spills, coordinate response, and maintain an auditable record of review activity.
|
||||
|
||||
Each quarantined message appears as a card-formatted message that includes:
|
||||
|
||||
- **Quarantined by**: The user who reported the message.
|
||||
- **Status**: The current state of the review. All quarantined content starts in **Pending** status.
|
||||
- **Reason**: The reason selected by the reporter (for example, **Classification mismatch**, **Need-to-know violation**).
|
||||
- **Message preview**: A snippet of the quarantined message, including the author, timestamp, and original channel.
|
||||
- **Reviewer**: The user currently assigned to review the message (initially **Unassigned**).
|
||||
- **Channel**: The name of the channel where the message was originally posted.
|
||||
- **Team**: The team context for the quarantined message.
|
||||
- **Comment**: Any reporter-provided context.
|
||||
- **Post ID**: The system identifier for the original message for auditing purposes.
|
||||
|
||||
Reviewers can select **View details** to take action as follows:
|
||||
|
||||
- Assign a **Reviewer** responsible for reviewing the quarantined message.
|
||||
- **Remove message**: Permanently delete the quarantined message from its original channel for all users. The status of the quarantined message changes to **Removed**.
|
||||
- **Keep message**: Dismiss the quarantine and restore the message if it was hidden. The status of the quarantined message changes to **Retained**.
|
||||
- **Add a comment**: Record the reason for the decision when required.
|
||||
- **Generate a report**: Download a report of the quarantined message and review activity for record-keeping or incident response. See [Generate a quarantined message report](#generate-a-quarantined-message-report) for details.
|
||||
|
||||
Once an action is taken, the **Status** field updates automatically. The **Data Spillage Bot** sends follow-up notifications to the reporter, author, and other reviewers based on how Data Spillage Handling is configured.
|
||||
|
||||
### Generate a quarantined message report
|
||||
|
||||
Reviewers can generate a downloadable report that captures the full context of a quarantined message and the associated review activity. Reports are useful for record-keeping, incident response, and preserving evidence before a message is permanently removed.
|
||||
|
||||
A report can be generated from any of the following entry points:
|
||||
|
||||
- **From the quarantined message details**: Select **Download report** from the message details panel. This option is available regardless of the quarantine's status, including **Pending**, **Reviewer Assigned**, **Removed**, or **Retained**.
|
||||
- **From the Remove message flow**: When you select **Remove message**, the confirmation dialog includes a **Download quarantined message report** checkbox, selected by default. With the checkbox selected, Mattermost generates and downloads the report before you can permanently remove the message. This safeguard ensures the record is preserved on your device before the message contents are deleted.
|
||||
- **From the Keep message flow**: When you select **Keep message**, the confirmation dialog includes the same checkbox. With the checkbox selected, Mattermost generates and downloads the report in the background while the keep action completes.
|
||||
|
||||
If you choose to skip the report download from the **Remove message** or **Keep message** flow, Mattermost asks you to confirm that you're proceeding without a report. The skip decision is recorded in the audit log.
|
||||
|
||||
If report generation fails (for example, due to a network interruption or session timeout), the dialog displays an error and offers a retry option. You can also skip the report and proceed with the action, or cancel and download the report later from the message details.
|
||||
|
||||
Each time a reviewer generates a report, the **Data Spillage Bot** notifies all content reviewers so an auditable record exists whenever a copy of the potentially spilled data is obtained. If the report was generated without the message's file attachments, the bot sends an additional notification to the reviewer who requested it, explaining why. See [File attachments and download policies](#file-attachments-and-download-policies) for details.
|
||||
|
||||
<Tip>
|
||||
|
||||
We recommend generating a report before removing a message. Once a message is removed, its content, attachments, and edit history are permanently deleted and can't be recovered.
|
||||
|
||||
</Tip>
|
||||
|
||||
#### Report contents and format
|
||||
|
||||
Each report is a ZIP archive containing YAML metadata files and the original file attachments. YAML is used because it's both human-readable and machine-parseable, which makes the report suitable for manual review and for ingestion by downstream compliance or incident-response tooling.
|
||||
|
||||
The archive has the following structure:
|
||||
|
||||
``` text
|
||||
/
|
||||
├── report_metadata.yaml
|
||||
├── content_review.yaml
|
||||
├── ATTACHMENTS_OMITTED.txt # only when attachments are withheld
|
||||
├── post/
|
||||
│ ├── post.yaml
|
||||
│ └── attachments/
|
||||
│ └── <original attachment files>
|
||||
└── edit_history/
|
||||
└── <edit_post_id>/
|
||||
├── post.yaml
|
||||
└── attachments/
|
||||
└── <original attachment files>
|
||||
```
|
||||
|
||||
- **report_metadata.yaml**: Identifies the report itself, including the user ID and username of the reviewer who generated the report, the generation timestamp, and the report format version (used for forward compatibility if the report format changes in future releases).
|
||||
- **content_review.yaml**: Captures the data spillage event, including the reporter's user ID, username, selected reason, and comment; the report timestamp; whether the message was hidden during review; and, once the quarantine is resolved, the reviewer's user ID, username, comment, and action timestamp. For unresolved quarantines, reviewer fields are omitted.
|
||||
- **post/post.yaml**: Describes the quarantined message, including the post ID, author ID, author name, author email, message content, channel ID, channel display name, team ID, team display name, creation and update timestamps, pinned status, root ID, post properties, post metadata, reply count (for root posts), and the ordered list of edit history post IDs.
|
||||
- **post/attachments/**: The original files attached to the quarantined message, included verbatim. This directory is left out entirely when the reviewer isn't permitted to download attachments from the channel.
|
||||
- **edit_history/\<edit_post_id\>/**: One subdirectory per previous version of the message, each containing a `post.yaml` and an `attachments/` directory in the same format as the base post directory.
|
||||
- **ATTACHMENTS_OMITTED.txt**: Present only when attachments were withheld from the report. See [File attachments and download policies](#file-attachments-and-download-policies).
|
||||
|
||||
To avoid duplication, attachment files are deduplicated across the entire archive by their file ID. Each unique attachment appears exactly once — under the base post if it exists in the current version of the message, or under the earliest edit-history entry that referenced it.
|
||||
|
||||
#### File attachments and download policies
|
||||
|
||||
If your deployment restricts who can download file attachments using [channel-level permission policies](/administration-guide/manage/admin/abac-channel-access-rules#channel-level-permission-policies) or [system-wide permission policies](/administration-guide/manage/admin/abac-system-wide-policies#permission-policies), those policies apply to content reviewers generating a report as well. Being a content reviewer doesn't exempt someone from the channel's file download rules.
|
||||
|
||||
Before building the archive, Mattermost evaluates the requesting reviewer against the quarantined message's channel for the file download action:
|
||||
|
||||
- **When the reviewer satisfies the policy**, the report includes attachments as described above.
|
||||
- **When the reviewer doesn't satisfy the policy**, the report is still generated and downloaded, but the attachment files are withheld. The `post/attachments/` and `edit_history/<edit_post_id>/attachments/` directories are left out, and the attachment metadata is removed from `post.yaml` in each of those sections, so the archive carries no reference to the withheld files. Every other part of the report - message content, review activity, and report metadata - is unaffected.
|
||||
|
||||
Reports aren't blocked when attachments are withheld, so reviewers who can't access the files can still capture the message and its review history for record-keeping.
|
||||
|
||||
When attachments are withheld, Mattermost signals it in three places:
|
||||
|
||||
- **`ATTACHMENTS_OMITTED.txt`**: A plain-text file added at the root of the archive, in place of the attachment directories, reading *"The attachments on this message were omitted from this report because you do not comply with the channel's file download policy."* Its presence makes an incomplete report self-describing for anyone who receives the archive later.
|
||||
- **A notification to the requesting reviewer**: The **Data Spillage Bot** posts a message in that reviewer's content review thread for the quarantined message, explaining that the report didn't include the file attachments. Only the reviewer who generated the report receives it; the other reviewers see the usual report generation notification.
|
||||
- **The audit log**: The `generateFlaggedPostReport` audit entry records an `omitted_attachment_count` parameter holding the number of withheld attachments. The parameter is only present when attachments were actually withheld, so its absence indicates a complete report.
|
||||
|
||||
<Note>
|
||||
|
||||
Permission policies require a Mattermost Enterprise Advanced license and the `PermissionPolicies` feature flag. Where they aren't in use, reviewers who can generate a report always receive its attachments, and none of the behavior in this section applies.
|
||||
|
||||
</Note>
|
||||
|
||||
### Deleted messages
|
||||
|
||||
When a reviewer permanently removes a quarantined message, the message and all associated data are deleted from the database and file system and can't be recovered. The deletion covers:
|
||||
|
||||
- **Post record**: The text of the message and any associated post properties. The content is scrubbed before the post is deleted.
|
||||
- **File attachments**: The files stored in Mattermost's file storage (local, S3, etc.).
|
||||
- **File attachment records**: The file info database rows for the message, including file names, IDs, and links to storage.
|
||||
- **Edit history**: Every prior revision of the message, along with file metadata from each revision.
|
||||
- **Priority metadata**: Any message priority or importance settings.
|
||||
- **Persistent notifications**: Any recurring notifications attached to the message.
|
||||
- **Acknowledgements**: Records of users who acknowledged the message.
|
||||
- **Reminders**: Any reminders created for the message.
|
||||
- **Thread, replies, and reactions**: The thread record, replies, and reaction data, if any, associated with the message.
|
||||
|
||||
#### Post deletion report
|
||||
|
||||
When a reviewer selects **Remove message**, the **Data Spillage Bot** posts a **Post Deletion Report** into the reviewer's content review thread for that quarantined message. The report is delivered to every reviewer who received the original quarantine notification, and is localized to each reviewer's language. Each post includes a short summary rendered inline, and a full report attached as a Markdown file named `deletion_report_<post_id>.md`.
|
||||
|
||||
The report records every cleanup step performed against the message and its associated data. The steps map directly to the data scope listed in [Deleted messages](#deleted-messages):
|
||||
|
||||
- **File attachments**: Files removed from file storage.
|
||||
- **File attachment records**: File info database rows for the message.
|
||||
- **Edit history**: Every prior revision of the message. Each revision is reported as its own sub-step so that reviewers can see exactly which revisions were cleared.
|
||||
- **Priority metadata**: Message priority and importance settings.
|
||||
- **Persistent notifications**: Recurring notifications attached to the message.
|
||||
- **Acknowledgements**: Records of users who acknowledged the message.
|
||||
- **Reminders**: Reminders set on the message.
|
||||
- **Thread, replies, and reactions**: The thread record, replies, and reaction data associated with the message.
|
||||
- **Post record**: The post itself. The content is scrubbed before the post is deleted.
|
||||
|
||||
Each step is assigned one of the following statuses:
|
||||
|
||||
- **Removed** ✅: The data was successfully deleted.
|
||||
- **Not applicable** ➖: There was no data of this type to delete.
|
||||
- **Partial** ⚠️: Some items of this type were deleted, but at least one failed. This status most often appears under **Edit history** when one revision can't be deleted.
|
||||
- **Failed** ❌: The step didn't complete. The report includes an error log so reviewers and System Administrators can inspect what went wrong.
|
||||
|
||||
When every step is **Removed** or **Not applicable**, no further action is required. The report serves as the auditable record of the deletion.
|
||||
|
||||
When any step reports **Partial** or **Failed**, the report displays an *incomplete* warning. Reviewers should escalate to a System Administrator, who can use the attached `deletion_report_<postId>.md` file - including the full per-step error log - to perform manual remediation and confirm that the data is fully removed.
|
||||
|
||||
<Note>
|
||||
|
||||
The post deletion report is the single source of truth for post-removal auditing. It isn't stored elsewhere in the System Console, so the reviewer thread containing the report should be retained in line with your organization's audit retention policy.
|
||||
|
||||
</Note>
|
||||
|
||||
## Best practice recommendations
|
||||
|
||||
Before rolling out Data Spillage Handling organization-wide, we recommend communicating that the feature protects both users and the organization from accidental data spillage. Start with a pilot team to validate reviewer notifications and workflows, integrate the process with existing data-handling or incident-response playbooks, and require reporter and reviewer comments to ensure every decision is transparent and auditable.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: "Customize branding"
|
||||
---
|
||||
Whether you’re customizing the appearance of your workspace, utilizing branding tools, or managing code signing for custom builds, this section of documentation has you covered and provides everything you need to customize the branding of Mattermost to align with your organization’s identity. Use the navigation below to access detailed instructions for each customization option.
|
||||
|
||||
- [Customize Mattermost](/administration-guide/configure/customize-mattermost) - Learn how to customize the Mattermost server.
|
||||
- [Custom branding tools](/administration-guide/configure/custom-branding-tools) - Learn about custom branding tools for Mattermost.
|
||||
- [Code signing custom builds](/administration-guide/manage/code-signing-custom-builds) - Learn about code signing custom builds of Mattermost.
|
||||
36
docs/main/administration-guide/manage/admin/error-codes.mdx
Normal file
36
docs/main/administration-guide/manage/admin/error-codes.mdx
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: "Mattermost error codes"
|
||||
---
|
||||
<PlanAvailability slug="all-commercial" />
|
||||
|
||||
Mattermost is designed to deploy in private networks which may be disconnected or “air-gapped” from the internet. In these deployments, links to Mattermost’s online documentation may be unavailable.
|
||||
|
||||
Therefore, Mattermost error codes are provided to enable administrators to use internet connected machines to look up error messages and resolutions in online resources, including Mattermost documentation and user forums, as well as providing Mattermost support teams a concise way to identify issues.
|
||||
|
||||
Mattermost error codes are used in logs, administrative tools, as well as on-screen messages to concisely identify issues and connect them with additional resources.
|
||||
|
||||
In advanced deployments, error codes can be overwritten by administrators to reference internal documentation and guides.
|
||||
|
||||
## ERROR_SAFETY_LIMITS_EXCEEDED
|
||||
|
||||
A safety limits exceeded error (`ERROR_SAFETY_LIMITS_EXCEEDED`) displays in the [free version of Mattermost](/product-overview/editions-and-offerings#mattermost-team-edition), and certain functionality may be limited, when usage grossly exceeds the recommended limit for users in a safe deployment, including more than 250 users are registered on the server.
|
||||
|
||||
250 users represents a “high upper limit” for deployments that are approximately 5 times the recommended size, which is far beyond the intended design of the product.
|
||||
|
||||
The free version of Mattermost is intended for approximately 50 users. If your Mattermost materially exceeds this recommended size, system admins should seek to either [purchase a commercial license](https://mattermost.com/pricing/), or apply for a [non-profit subscription](/product-overview/non-profit-subscriptions) license. Alternatively, admins can [deactivate users](/administration-guide/configure/user-management-configuration-settings#deactivate-users) until the user count falls below the high upper limit.
|
||||
|
||||
## ERROR_LICENSED_USERS_LIMIT_EXCEEDED
|
||||
|
||||
A licensed user limit exceeded error (`ERROR_LICENSED_USERS_LIMIT_EXCEEDED`) displays when attempting to create or activate users on a licensed Mattermost server where the user count would exceed the maximum number of users allowed by the license.
|
||||
|
||||
This error occurs when:
|
||||
|
||||
- Creating new users would exceed the license user limit.
|
||||
- Activating deactivated users would exceed the license user limit.
|
||||
|
||||
To resolve this error, system administrators can:
|
||||
|
||||
- [Deactivate users](/administration-guide/configure/user-management-configuration-settings#deactivate-users) to reduce the active user count below the license limit.
|
||||
- Contact [Mattermost Sales](https://mattermost.com/contact-sales/) to request an updated license that increases the number of licensed users.
|
||||
|
||||
[Book a live demo](https://mattermost.com/request-demo/) or [talk to a Mattermost expert](https://mattermost.com/contact-sales/) to explore tailored solutions for your organization's secure collaboration needs. Or try Mattermost yourself with a [1-hour preview](https://mattermost.com/sign-up/) for instant access to a live sandbox environment.
|
||||
145
docs/main/administration-guide/manage/admin/fips-migration.mdx
Normal file
145
docs/main/administration-guide/manage/admin/fips-migration.mdx
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
title: "Migrate an existing deployment to FIPS-compliant images"
|
||||
sidebar_label: "Migrate to FIPS-compliant images"
|
||||
---
|
||||
<PlanAvailability slug="entry-ent" />
|
||||
|
||||
From Mattermost v11, each release ships in two image variants: a standard Enterprise build (`mattermost/mattermost-enterprise-edition`) and a FIPS-compliant build (`mattermost/mattermost-enterprise-fips-edition`). Migrating an existing deployment to the FIPS image is primarily a matter of replacing the image and restarting Mattermost. No data migration is required, and rollback is symmetric.
|
||||
|
||||
This guide covers migrating an existing Mattermost Server deployment running on Docker or Kubernetes. Linux package and tarball installations have no FIPS-compliant equivalent.
|
||||
|
||||
Mattermost's FIPS offering also covers the Mattermost Operator controller and a self-hosted Push Proxy. Migrating those components to their FIPS variants is out of scope for this guide.
|
||||
|
||||
For background on what the FIPS build is and how it's constructed, see the **FIPS/STIG** tab on [Deploy Mattermost using Containers](/deployment-guide/server/deploy-containers).
|
||||
|
||||
## Before you begin
|
||||
|
||||
1. **Back up your database and configuration.** Always take a full backup before changing the image. See the [backup and disaster recovery](/deployment-guide/backup-disaster-recovery) documentation.
|
||||
|
||||
2. **Confirm your deployment type.** FIPS images are supported on Docker, Docker Compose, and Kubernetes only. If you're running Mattermost from a Linux package or tarball, you can't migrate in place.
|
||||
|
||||
3. **Check your Mattermost version.** FIPS images are available from v11.0 onward and use the same release tags as the standard Enterprise images. Plan to migrate to the matching FIPS tag for your current version.
|
||||
|
||||
4. **Plan for additional plugins.** The FIPS image includes Boards, Playbooks, and Agents prepackaged and running in FIPS mode. Any additional plugins you've installed will continue to run inside the FIPS image, but they run in non-FIPS mode. This is expected behavior, not a configuration error.
|
||||
|
||||
5. **PostgreSQL password length.** Beginning with Mattermost v11.7, the Postgres password used by the Mattermost server must be at least 14 characters when running the FIPS image. If your current password is shorter, complete these steps before swapping the image:
|
||||
|
||||
a. Rotate the password in PostgreSQL. For example, connecting as a Postgres superuser and replacing `mmuser` and `<new password>` to match your deployment:
|
||||
|
||||
```sql
|
||||
ALTER USER mmuser WITH PASSWORD '<new password>';
|
||||
```
|
||||
|
||||
b. Update the password where your deployment stores it: in the official Docker deployment, the `POSTGRES_PASSWORD` value in `.env`; in Operator-managed Kubernetes deployments, the database Secret referenced by your `Mattermost` custom resource (commonly under `spec.database.external.secret`).
|
||||
|
||||
c. Proceed to the image swap procedure for your deployment type below. Recreating the container as part of the migration applies the new credentials — no separate restart is required.
|
||||
|
||||
6. **Plan for downtime.** The migration requires pulling the new image and restarting the Mattermost container or pod.
|
||||
|
||||
## Migrate a Kubernetes deployment
|
||||
|
||||
These steps assume your deployment is managed by the Mattermost Operator using a `Mattermost` custom resource.
|
||||
|
||||
In the steps below, replace `<namespace>` with the namespace your Mattermost installation runs in. If you omit `-n <namespace>`, `kubectl` uses your current context's default namespace.
|
||||
|
||||
1. Find the name of your Mattermost custom resource:
|
||||
|
||||
```sh
|
||||
kubectl -n <namespace> get mattermost
|
||||
```
|
||||
|
||||
2. Edit the `Mattermost` custom resource to point at the FIPS image. You can edit the live resource directly:
|
||||
|
||||
```sh
|
||||
kubectl -n <namespace> edit mattermost <installation-name>
|
||||
```
|
||||
|
||||
Or update your manifest file and re-apply it.
|
||||
|
||||
3. In `spec.image`, change the value from `mattermost/mattermost-enterprise-edition` to `mattermost/mattermost-enterprise-fips-edition`. Keep `spec.version` aligned with your current release tag. For example:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
image: mattermost/mattermost-enterprise-fips-edition
|
||||
version: 11.6.1
|
||||
```
|
||||
|
||||
4. If you edited a manifest file, apply it:
|
||||
|
||||
```sh
|
||||
kubectl -n <namespace> apply -f <your-mattermost-manifest>.yaml
|
||||
```
|
||||
|
||||
5. Watch the Mattermost pods until the new ones report `Ready` and the old ones terminate. A pod showing `Running` isn't necessarily serving traffic yet — wait until its `READY` column shows all containers ready (for example, `1/1`):
|
||||
|
||||
```sh
|
||||
kubectl -n <namespace> get pods -l app=mattermost -w
|
||||
```
|
||||
|
||||
6. Verify the running pods are using the FIPS image:
|
||||
|
||||
```sh
|
||||
kubectl -n <namespace> get pods -l app=mattermost -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].image}{"\n"}{end}'
|
||||
```
|
||||
|
||||
## Migrate a Docker or Docker Compose deployment
|
||||
|
||||
These steps are written for the official [Mattermost Docker deployment](https://github.com/mattermost/docker), which selects the Mattermost image and tag through `.env` variables (`MATTERMOST_IMAGE` and `MATTERMOST_IMAGE_TAG`) referenced from `docker-compose.yml` as `mattermost/${MATTERMOST_IMAGE}:${MATTERMOST_IMAGE_TAG}`. If you're using a custom Docker setup that hardcodes the image in `docker-compose.yml` or uses different variable names, adapt these steps accordingly.
|
||||
|
||||
1. Stop the Mattermost container:
|
||||
|
||||
```sh
|
||||
docker compose stop mattermost
|
||||
```
|
||||
|
||||
2. Edit your `.env` file. Change `MATTERMOST_IMAGE` from `mattermost-enterprise-edition` to `mattermost-enterprise-fips-edition`. Leave `MATTERMOST_IMAGE_TAG` set to your current release tag — FIPS images are published under the same tags as the standard Enterprise images.
|
||||
|
||||
```text
|
||||
MATTERMOST_IMAGE=mattermost-enterprise-fips-edition
|
||||
MATTERMOST_IMAGE_TAG=<tag>
|
||||
```
|
||||
|
||||
3. Pull the FIPS image:
|
||||
|
||||
```sh
|
||||
docker compose pull mattermost
|
||||
```
|
||||
|
||||
4. Recreate the Mattermost container so the new `.env` values are applied:
|
||||
|
||||
```sh
|
||||
docker compose up -d --force-recreate mattermost
|
||||
```
|
||||
|
||||
5. Verify the running container is using the FIPS image:
|
||||
|
||||
```sh
|
||||
docker inspect mattermost --format '{{.Config.Image}}'
|
||||
```
|
||||
|
||||
## Verify the migration
|
||||
|
||||
1. Confirm Mattermost starts cleanly. Tail the logs and watch for startup errors. On Kubernetes, target a specific Mattermost pod:
|
||||
|
||||
```sh
|
||||
kubectl -n <namespace> logs -f <pod-name>
|
||||
```
|
||||
|
||||
On Docker:
|
||||
|
||||
```sh
|
||||
docker compose logs -f mattermost
|
||||
```
|
||||
|
||||
2. Sign in and verify core functionality (sending messages, file uploads, search).
|
||||
|
||||
3. Confirm the prepackaged plugins (Boards, Playbooks, Agents) load successfully. In the System Console, go to **Plugins > Plugin Management** and confirm they're enabled and healthy.
|
||||
|
||||
4. If you have additional plugins installed, confirm they still load. They'll run in non-FIPS mode inside the FIPS image — this is expected.
|
||||
|
||||
## Roll back
|
||||
|
||||
If the migration doesn't go as planned, rolling back is symmetric: revert the image reference and redeploy. No data migration is involved.
|
||||
|
||||
- **Kubernetes:** Edit the `Mattermost` custom resource and change `spec.image` back to `mattermost/mattermost-enterprise-edition`. Re-apply or save, and watch the rollout.
|
||||
- **Docker / Docker Compose:** Restore the original `MATTERMOST_IMAGE` value in `.env` (`mattermost-enterprise-edition`) and run `docker compose pull mattermost` followed by `docker compose up -d --force-recreate mattermost`.
|
||||
@@ -0,0 +1,384 @@
|
||||
---
|
||||
title: "Generate a support packet"
|
||||
---
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
<PlanAvailability slug="all-commercial" />
|
||||
|
||||
The Support Packet is used to help customers diagnose and troubleshoot issues. Use the System Console or the [mmctl system supportpacket](/administration-guide/manage/mmctl-command-line-tool#mmctl-system-supportpacket) command to generate a zip file that includes configuration information, logs, plugin details, and data on external dependencies across all nodes in a high-availability cluster. Confidential data, such as passwords, are automatically stripped.
|
||||
|
||||
## Generate
|
||||
|
||||
<Important>
|
||||
|
||||
- Before generating a Support Packet, go to **System Console \> Environment \> Logging** and ensure **Output logs to file** is set to **true**, and set **File Log Level** to **DEBUG**
|
||||
- From Mattermost v11.4, support packet generation is recorded in the audit log (when [audit logging is enabled and configured](/administration-guide/manage/logging#audit-logging)). The audit event includes the username, timestamp, success/failure status, whether logs were included, plugin packets requested, and the output filename. This audit trail helps track access to potentially sensitive log data for compliance purposes.
|
||||
|
||||
</Important>
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="web-desktop" label="Web/Desktop">
|
||||
|
||||
1. Go to the System Console, and select **Commercial Support** from the System Console menu.
|
||||
|
||||

|
||||
|
||||
2. Select **Download Support Packet**. A zip file is downloaded to the local machine. You'll be notified if any packet files are unavailable during packet generation. See the `warning.txt` file for details.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mmctl" label="mmctl">
|
||||
|
||||
Run the [mmctl system supportpacket](/administration-guide/manage/mmctl-command-line-tool#mmctl-system-supportpacket) command to generate and download a Support Packet to share with Mattermost Support.
|
||||
|
||||
``` sh
|
||||
go run ./cmd/mmctl system supportpacket
|
||||
```
|
||||
|
||||
``` text
|
||||
Downloading Support Packet
|
||||
Downloaded Support Packet to mattermost_support_packet_.zip
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## Sanitize confidential data
|
||||
|
||||
Please sanitize any confidential data you wish to exclude before sharing the packet with Mattermost.
|
||||
|
||||
When present, the following information is automatically sanitized during packet generation: `LdapSettings.BindPassword`, `FileSettings.PublicLinkSalt`, `FileSettings.AmazonS3SecretAccessKey`, `EmailSettings.SMTPPassword`, `GitLabSettings.Secret`, `GoogleSettings.Secret`, `Office365Settings.Secret`, `OpenIdSettings.Secret`, `SqlSettings.DataSource`, `SqlSettings.AtRestEncryptKey`, `ElasticsearchSettings.Password`, `All SqlSettings.DataSourceReplicas`, `All SqlSettings.DataSourceSearchReplicas`, `MessageExportSettings.GlobalRelaySettings.SmtpPassword`, `ServiceSettings.SplitKey`, `FileSettings.ExportAmazonS3SecretAccessKey`, `ServiceSettings.GoogleDeveloperKey`, and `ServiceSettings.GiphySdkKey` (from Mattermost v11.6.0).
|
||||
|
||||
<Important>
|
||||
|
||||
Plugins may not be sanitized during packet generation.
|
||||
|
||||
- From Mattermost v10.1, plugins can mark their configuration as hidden. If a plugin marks its configuration as hidden, the configuration is sanitized during packet generation.
|
||||
- Otherwise, ensure you sanitize any additional confidential details in the `plugin.json` file before sharing it with Mattermost. Replace details with example strings that contain the same special characters if possible, as special characters are common causes of configuration errors.
|
||||
|
||||
</Important>
|
||||
|
||||
## Share the packet with Mattermost
|
||||
|
||||
Add the generated Support Packet to a [standard support request](https://support.mattermost.com/hc/en-us/requests/new), or share with with the Mattermost team you're working with.
|
||||
|
||||
<Important>
|
||||
|
||||
Disable debug logging once you've generated the Support Packet. Debug logging can cause log files to expand substantially, and may adversely impact server performance. We recommend enabling it temporarily, or in development environments, but not production environments.
|
||||
|
||||
</Important>
|
||||
|
||||
## Contents of a Support Packet
|
||||
|
||||
The contents of a Mattermost Support Packet can differ by server version. Select the tab that corresponds to your Mattermost version to see the files included in the Support Packet.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="v11-0-and-later" label="v11.0 and later">
|
||||
|
||||
### Cluster-wide files
|
||||
|
||||
The following cluster-wide files are located in the root directory of the Support Packet:
|
||||
|
||||
- [metadata.yaml](#metadata)
|
||||
- `plugins.json` (all active and inactive plugins)
|
||||
- `sanitized_config.json` (sanitized copy of the Mattermost configuration)
|
||||
- `stats.yaml` (Mattermost usage statistics)
|
||||
- `jobs.yaml` (last runs of important jobs)
|
||||
- `diagnostics.yaml` (system and plugin diagnostics)
|
||||
- `permissions.yaml` (role and scheme information)
|
||||
- `postgres_schema_dump.sql` (PostgreSQL database schema information including tables, indexes, constraints, and other metadata to assist with database configuration diagnosis)
|
||||
- `warning.txt` (present when issues are encountered during packet generation)
|
||||
- `tsdb_dump.tar.gz` (present when the Metrics plugin is installed and the **Performance metrics** option is selected when generating the Support Packet)
|
||||
|
||||
### Node-specific files
|
||||
|
||||
The following node-specific files are located in node subdirectories:
|
||||
|
||||
- `<node-id>/mattermost.log` (Mattermost logs for each node)
|
||||
- `<node-id>/audit.log` (Mattermost audit logs for each node)
|
||||
- `<node-id>/ldap.log` (AD/LDAP logs for each node)
|
||||
- `<node-id>/notifications.log` (notifications logs for each node)
|
||||
- `<node-id>/cpu.prof` ([Go performance metrics](#go-performance-metrics) for each node)
|
||||
- `<node-id>/heap.prof` ([Go performance metrics](#go-performance-metrics) for each node)
|
||||
- `<node-id>/goroutines` ([Go performance metrics](#go-performance-metrics) for each node)
|
||||
|
||||
### Diagnostics highlights
|
||||
|
||||
The Support Packet `diagnostics.yaml` file includes system and plugin diagnostics to support troubleshooting and configuration validation.
|
||||
|
||||
### Plugin diagnostic data
|
||||
|
||||
The following additional plugin diagnostic data is available when the plugin is enabled and operational:
|
||||
|
||||
- GitHub: `/github/diagnostics.yaml`
|
||||
- GitLab: `/com.github.manland.mattermost-plugin-gitlab/diagnostics.yaml`
|
||||
- Jira: `/jira/diagnostics.yaml`
|
||||
- Calls: `/com.mattermost.calls/diagnostics.yaml`
|
||||
- Boards: `/focalboard/diagnostics.yaml`
|
||||
- Playbooks: `/playbooks/diagnostics.yaml`
|
||||
- MSCalendar: `/com.mattermost.mscalendar/diagnostics.yaml`
|
||||
- Google Calendar: `/com.mattermost.gcal/diagnostics.yaml`
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="v10-11" label="v10.11">
|
||||
|
||||
From v10.11, Support Packets include PostgreSQL database schema dump information that provides comprehensive metadata to help diagnose database configuration issues, performance problems, collation mismatches, and other database-related issues.
|
||||
|
||||
**Cluster-wide files (root directory):**
|
||||
|
||||
- [metadata.yaml](#metadata)
|
||||
- `plugins.json` (all active and inactive plugins)
|
||||
- `sanitized_config.json` (sanitized copy of the Mattermost configuration)
|
||||
- `stats.yaml` (Mattermost usage statistics)
|
||||
- `jobs.yaml` (last runs of important jobs)
|
||||
- `diagnostics.yaml` (core plugin diagnostics data)
|
||||
- `permissions.yaml` (role & scheme information)
|
||||
- `postgres_schema_dump.sql` (PostgreSQL database schema information including tables, indexes, constraints, and other database metadata to assist with database configuration diagnosis)
|
||||
- `warning.txt` (present when issues are encountered during packet generation)
|
||||
- `tsdb_dump.tar.gz` (present when the Metrics plugin is installed and the **Performance metrics** option is selected when generating the Support Packet)
|
||||
|
||||
**Cluster-specific files (in node subdirectories):**
|
||||
|
||||
- `<node-id>/mattermost.log` (Mattermost logs for each node)
|
||||
- `<node-id>/audit.log` (Mattermost audit logs for each node)
|
||||
- `<node-id>/ldap.log` (AD/LDAP logs for each node)
|
||||
- `<node-id>/notifications.log` (notifications logs for each node)
|
||||
- `<node-id>/cpu.prof` ([Go performance metrics](#go-performance-metrics) for each node)
|
||||
- `<node-id>/heap.prof` ([Go performance metrics](#go-performance-metrics) for each node)
|
||||
- `<node-id>/goroutines` ([Go performance metrics](#go-performance-metrics) for each node)
|
||||
|
||||
The following additional plugin diagnostic data is included in the generated Support Packet when the plugin is enabled and operational:
|
||||
|
||||
- GitHub: `/github/diagnostics.yaml`
|
||||
- GitLab: `/com.github.manland.mattermost-plugin-gitlab/diagnostics.yaml`
|
||||
- Jira: `/jira/diagnostics.yaml`
|
||||
- Calls: `/com.mattermost.calls/diagnostics.yaml`
|
||||
- Boards: `/focalboard/diagnostics.yaml`
|
||||
- Playbooks: `/playbooks/diagnostics.yaml`
|
||||
- MSCalendar: `/com.mattermost.mscalendar/diagnostics.yaml`
|
||||
- Google Calendar: `/com.mattermost.gcal/diagnostics.yaml`
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="v10-10" label="v10.10">
|
||||
|
||||
From Mattermost v10.10, Support Packets from [high availability](/deployment-guide/server/high-availability-cluster-based-deployment) deployments organize cluster-specific files (such as log files) in subdirectories named after each cluster node, while cluster-wide files remain in the root directory.
|
||||
|
||||
Support packet file organization has been improved to make it easier to identify cluster-wide versus cluster-specific files:
|
||||
|
||||
- **Cluster-wide files** (identical across all nodes in a [high-availability cluster](/deployment-guide/server/high-availability-cluster-based-deployment)) remain in the root directory of the support packet.
|
||||
- **Cluster-specific files** (unique per node) are now organized in subdirectories named after each cluster node.
|
||||
|
||||
**Cluster-wide files (root directory):**
|
||||
|
||||
- [metadata.yaml](#metadata)
|
||||
- `plugins.json` (all active and inactive plugins)
|
||||
- `sanitized_config.json` (sanitized copy of the Mattermost configuration)
|
||||
- `stats.yaml` (Mattermost usage statistics)
|
||||
- `jobs.yaml` (last runs of important jobs)
|
||||
- `diagnostics.yaml` (core plugin diagnostics data)
|
||||
- `permissions.yaml` (role & scheme information)
|
||||
- `warning.txt` (present when issues are encountered during packet generation)
|
||||
- `tsdb_dump.tar.gz` (present when the Metrics plugin is installed and the **Performance metrics** option is selected when generating the Support Packet)
|
||||
|
||||
**Cluster-specific files (in node subdirectories):**
|
||||
|
||||
- `<node-id>/mattermost.log` (Mattermost logs for each node)
|
||||
- `<node-id>/audit.log` (Mattermost audit logs for each node)
|
||||
- `<node-id>/ldap.log` (AD/LDAP logs for each node)
|
||||
- `<node-id>/notifications.log` (notifications logs for each node)
|
||||
- `<node-id>/cpu.prof` ([Go performance metrics](#go-performance-metrics) for each node)
|
||||
- `<node-id>/heap.prof` ([Go performance metrics](#go-performance-metrics) for each node)
|
||||
- `<node-id>/goroutines` ([Go performance metrics](#go-performance-metrics) for each node)
|
||||
|
||||
The following additional plugin diagnostic data is included in the generated support packet when the plugin is enabled and operational:
|
||||
|
||||
- GitHub: `/github/diagnostics.yaml`
|
||||
- GitLab: `/com.github.manland.mattermost-plugin-gitlab/diagnostics.yaml`
|
||||
- Jira: `/jira/diagnostics.yaml`
|
||||
- Calls: `/com.mattermost.calls/diagnostics.yaml`
|
||||
- Boards: `/focalboard/diagnostics.yaml`
|
||||
- Playbooks: `/playbooks/diagnostics.yaml`
|
||||
- MSCalendar: `/com.mattermost.mscalendar/diagnostics.yaml`
|
||||
- Google Calendar: `/com.mattermost.gcal/diagnostics.yaml`
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="v10-5-to-v10-9" label="v10.5 to v10.9">
|
||||
|
||||
Prior to v10.10, each node in the cluster of a high availability deployment has its own `mattermost.log` file and advanced logging files included directly in the Support Packet.
|
||||
|
||||
From v10.5, the following Support Packet data has changed:
|
||||
|
||||
- The `support_packet.yaml` file has been removed and split into `diagnostics.yaml` and `stats.yaml` files.
|
||||
- All fields in `diagnostics.yaml` have been moved into their own objects for improved readability.
|
||||
- Field names are normalized.
|
||||
- New data includes server statistics, logs, permissions, and extended job list details.
|
||||
- Mattermost-supported plugin diagnostic data is included where applicable.
|
||||
|
||||
The contents of a support packet include:
|
||||
|
||||
- [metadata.yaml](#metadata)
|
||||
- `mattermost.log` (Mattermost logs)
|
||||
- `audit.log` (Mattermost audit logs)
|
||||
- `ldap.log` (AD/LDAP logs)
|
||||
- `notifications.log` (notifications logs)
|
||||
- `plugins.json` (all active and inactive plugins)
|
||||
- `sanitized_config.json` (sanitized copy of the Mattermost configuration)
|
||||
- `stats.yaml` (Mattermost usage statistics)
|
||||
- `jobs.yaml` (last runs of important jobs)
|
||||
- `diagnostics.yaml` (core plugin diagnostics data)
|
||||
- `permissions.yaml` (role & scheme information)
|
||||
- [Go performance metrics](#go-performance-metrics), including: `cpu.prof`, `heap.prof`, and `goroutines`
|
||||
- `warning.txt` (present when issues are encountered during packet generation)
|
||||
- `tsdb_dump.tar.gz` (present when the Metrics plugin is installed and the **Performance metrics** option is selected when generating the Support Packet)
|
||||
|
||||
The following additional plugin diagnostic data is included in the generated support packet when the plugin is enabled and operational:
|
||||
|
||||
- GitHub: `/github/diagnostics.yaml`
|
||||
- GitLab: `/com.github.manland.mattermost-plugin-gitlab/diagnostics.yaml`
|
||||
- Jira: `/jira/diagnostics.yaml`
|
||||
- Calls: `/com.mattermost.calls/diagnostics.yaml`
|
||||
- Boards: `/focalboard/diagnostics.yaml`
|
||||
- Playbooks: `/playbooks/diagnostics.yaml`
|
||||
- MSCalendar: `/com.mattermost.mscalendar/diagnostics.yaml`
|
||||
- Google Calendar: `/com.mattermost.gcal/diagnostics.yaml`
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="prior-to-v10-5" label="Prior to v10.5">
|
||||
|
||||
From Mattermost v10.4, a new `diagnostics.yaml` file includes Mattermost Calls diagostics data, including plugin version, calls and active session counts, as well as average duration and participant counts.
|
||||
|
||||
- [metadata.yaml](#metadata)
|
||||
- `mattermost.log`
|
||||
- `plugins.json`
|
||||
- `sanitized_config.json`
|
||||
- `support_packet.yaml`
|
||||
- `diagnostics.yaml` (core plugin diagnostics data)
|
||||
- [Go performance metrics](#go-performance-metrics), including: `cpu.prof`, `heap.prof`, and `goroutines`
|
||||
- `warning.txt` (present when issues are encountered during packet generation)
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
<Note>
|
||||
|
||||
- LDAP groups are not included during Support Packet generation. Only `LDAP Version` and `LDAP Vendor` are included when present. These values are included in the `support_packet.yaml` file.
|
||||
- From Mattermost v9.11, `LDAP Vendor` errors are included in the Support Packet. If fetching the LDAP Vendor name fails, the Support Packet generation includes the error in `warning.txt`. If no LDAP Vendor name is found, the Support Packet lists them as `unknown`.
|
||||
|
||||
</Note>
|
||||
|
||||
## Metadata
|
||||
|
||||
From Mattermost v9.11, generated Support Packets include a `metadata.yaml` file that contains the following information.
|
||||
|
||||
<table style={{width: '99%'}}>
|
||||
<colgroup>
|
||||
<col style={{width: '12%'}} />
|
||||
<col style={{width: '12%'}} />
|
||||
<col style={{width: '59%'}} />
|
||||
<col style={{width: '14%'}} />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><strong>Field name</strong></th>
|
||||
<th><strong>Required/Optional</strong></th>
|
||||
<th><strong>Description</strong></th>
|
||||
<th><blockquote>
|
||||
<p><strong>Example</strong></p>
|
||||
</blockquote></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>version</td>
|
||||
<td>Required</td>
|
||||
<td>Version of the schema that the current metadata file is compatible with. Current version is 1.</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>type</td>
|
||||
<td>Required</td>
|
||||
<td>The type of the packet. Each type of Support Packet can be mapped to a specific component generating the Support Packet.</td>
|
||||
<td>mattermost</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>generated_at</td>
|
||||
<td>Required</td>
|
||||
<td>The date and time the packet was created. Value is in epoch (ms).</td>
|
||||
<td>1707473288731</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>server_version</td>
|
||||
<td>Required</td>
|
||||
<td>Version of the server that the Support Packet was generated at. Semver is expected.</td>
|
||||
<td>9.1.1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>server_id</td>
|
||||
<td>Required</td>
|
||||
<td>Unique identifier of the server. Expected to be 26 characters or longer.</td>
|
||||
<td>9qpiszyjr3g8bxda35abcd1234</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>license_id</td>
|
||||
<td>Optional</td>
|
||||
<td>Unique identifier of the current server's license. Expected to be 26 characters or longer. This field is empty when there's no license.</td>
|
||||
<td>abcdejisd67yigqhmkz4ho1234</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>customer_id</td>
|
||||
<td>Optional</td>
|
||||
<td>The id of the customer, as defined in the license file. Expected to be 26 characters or longer. Empty when there's no license.</td>
|
||||
<td>a1b2c3d4qbbr5cpkbpbmef123h</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>extras</td>
|
||||
<td>Optional</td>
|
||||
<td>Key/value of any additional information, specific to the plugin/component that generated the file. Can be useful for identifying the contents of the data. Consider adding plugin (or component) versions in order to set expectation regarding the contents of this object.</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>extras.plugin_id</td>
|
||||
<td>Required for plugins</td>
|
||||
<td>The ID of the plugin.</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>extras.plugin_version</td>
|
||||
<td>Required for plugins</td>
|
||||
<td>The version of the plugin.</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
For example:
|
||||
|
||||
``` yaml
|
||||
version: 1
|
||||
type: support-packet
|
||||
generated_at: 1622569200
|
||||
server_version: 9.1.1
|
||||
server_id: 8fqk9rti13fmpxdd5934a3xsxh
|
||||
license_id: 3g3pqn8in3brzjkozcn1kdidgr
|
||||
customer_id: 74cmws7gf3ykpj31car7zahsny
|
||||
extras:
|
||||
plugin_version: 0.1.0
|
||||
```
|
||||
|
||||
## Go performance metrics
|
||||
|
||||
The Support Packet contains 3 go runtime profiling files:
|
||||
|
||||
- `cpu.prof` contains a 5-second CPU profile
|
||||
- `heap.prof` contains a heap profile
|
||||
- `goroutines` contains a dump of all the running go routines
|
||||
|
||||
These files can be read using [pprof](https://golang.google.cn/cmd/pprof/).
|
||||
|
||||
Use `go tool pprof -web X` to open a visualization of the profile in your browser, replacing `X` with the profile's file name.
|
||||
|
||||
## Load metric
|
||||
|
||||
From Mattermost v10.10, the **Load Metric** field under **Product Menu \> About Mattermost** displays monthly active users relative to the total number of licensed users. This value gives Mattermost support teams a contextual reference point for understanding deployment active usage for troubleshooting and guidance. It isn’t a comprehensive performance monitoring tool or health indicator, but serves as a supplementary data point when traditional diagnostic methods aren’t available.
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: "Install a license key"
|
||||
---
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
<PlanAvailability slug="all-commercial" />
|
||||
|
||||
You can use the System Console or the mmctl tools to add or change a Mattermost license key.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="use-system-console" label="Use System Console">
|
||||
|
||||
1. Go to **System Console \> About \> Edition and License**.
|
||||
2. Upload your license key file.
|
||||
|
||||
Once the key is uploaded and installed, the details of your license are displayed.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="use-mmctl" label="Use mmctl">
|
||||
|
||||
Use the [mmctl license upload](/administration-guide/manage/mmctl-command-line-tool#mmctl-license-upload) command to upload a new license or replace an existing license file with a new one. When complete, restart the Mattermost server. If you're running in a [High Availability](/deployment-guide/server/high-availability-cluster-based-deployment) environment, the new license file must be updated to every node.
|
||||
|
||||
``` sh
|
||||
mmctl license upload [license] [flags]
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
<Note>
|
||||
|
||||
- From Mattermost v10.11, the option to add a license is disabled when the license is set using an [environment variable](/administration-guide/configure/environment-configuration-settings#license-file-location).
|
||||
- Enterprise customers with the Premier Support add-on can request a staging license for testing.
|
||||
- Removing a Mattermost Enterprise or Professional license key won't remove the configuration for Enterprise settings; however, these features won't function until an Enterprise or Professional license key is applied.
|
||||
- When you're using [High Availability](/deployment-guide/server/high-availability-cluster-based-deployment), it's critical to ensure that all servers in the cluster have same Enterprise license properly installed to prevent multi-node clusters from failing. An Enterprise license is required for High Availability to work.
|
||||
- When you apply an Enterprise license key to a server previously licensed for Professional, Professional features retain their configuration settings in Enterprise.
|
||||
- When you apply a Professional license to a server previously licensed for Enterprise, Enterprise features retain their configuration but will no longer be accessible for use.
|
||||
|
||||
</Note>
|
||||
|
||||
## Change an existing license key
|
||||
|
||||
You don't need to wait for your current license key to expire before replacing it with a new license from Mattermost. The server only checks for seat count and license end date, not the start date, so you can apply a new license as soon as you receive it. However, ensure your new license is for a seat count that's greater than or equal to your current total number of Mattermost users.
|
||||
|
||||
<Tip>
|
||||
|
||||
To review license usage before uploading a new key, go to **System Console \> Reporting \> System Statistics**. The **Total Activated Users** field shows the primary paid seat count used for license validation. Review **Single-channel Guests** separately because guests in exactly one active channel are tracked outside the primary paid seat count, are free up to a 1:1 ratio with licensed seats, and generate warnings instead of hard enforcement when that allowance is exceeded. Only active channels count toward guest channel access for billing. Archived channels are excluded.
|
||||
|
||||
</Tip>
|
||||
|
||||
Follow these steps to change your license key:
|
||||
|
||||
1. Go to **System Console \> About \> Edition and License**.
|
||||
2. From Mattermost v6.7, simply upload your new license key file.
|
||||
|
||||
<Note>
|
||||
|
||||
If you're running a Mattermost release older than v6.7, select **Remove Enterprise License and Downgrade Server** to clear the license from the server and refresh the System Console first before uploading the new key.
|
||||
|
||||
</Note>
|
||||
|
||||
## License key storage
|
||||
|
||||
Once you've uploaded your license key to your Mattermost server, it's stored in your SQL database at `mattermost.Licenses`. You can check what keys are on your server by running `select * from mattermost.Licenses;`.
|
||||
|
||||
[Book a live demo](https://mattermost.com/request-demo/) or [talk to a Mattermost expert](https://mattermost.com/contact-sales/) to explore tailored solutions for your organization's secure collaboration needs. Or try Mattermost yourself with a [1-hour preview](https://mattermost.com/sign-up/) for instant access to a live sandbox environment.
|
||||
@@ -0,0 +1,802 @@
|
||||
---
|
||||
title: "Manually migrate from MySQL to PostgreSQL"
|
||||
---
|
||||
<PlanAvailability slug="all-commercial" />
|
||||
|
||||
Migrating a MySQL database to PostgreSQL manually involves the following steps:
|
||||
|
||||
- [install recommended tools](#tool-recommendations)
|
||||
- [familiarize yourself with system requirements and configurations](#system-requirements-and-configurations-for-manual-migrations)
|
||||
- [ensure migration prerequisites are met](#before-a-manual-migration)
|
||||
- [review schema differences](#schema-diffs)
|
||||
- [prepare a target database](#prepare-target-database)
|
||||
- [migrate the data](#migrate-the-data)
|
||||
- [complete post-migration wrap-up activities](#after-the-migration)
|
||||
|
||||
<Tip>
|
||||
|
||||
See the [plugin migrations](#plugin-migrations) section for details on migrating collaborative playbooks and Boards.
|
||||
|
||||
</Tip>
|
||||
|
||||
Not sure upgrading manually is the right path forward? Mattermost customers looking for tailored guidance based on their Mattermost deployment can contact a [Mattermost Expert](https://mattermost.com/contact-sales/).
|
||||
|
||||
## Tool recommendations
|
||||
|
||||
If you prefer to migrate to Postgres manually, we recommend the following tools for the migration process:
|
||||
|
||||
- [pgloader](#pgloader)
|
||||
- [morph](#morph)
|
||||
- [dbcmp (optional)](#dbcmp-optional)
|
||||
|
||||
This page includes instructions on how to install each of these tools, and then proceed with the database migration.
|
||||
|
||||
Once you've installed the necessary tools, review the [system requirements and configurations](#system-requirements-and-configurations-for-manual-migrations) documentation, and know what's required [before starting your migration](#before-a-manual-migration) to prepare for your migration.
|
||||
|
||||
Start your migration by [preparing your target database](#prepare-target-database), then [migrate the data](#migrate-the-data), and complete all [post-migration steps](#after-the-migration).
|
||||
|
||||
See the [plugin migrations](#plugin-migrations) documentation for details on migrating collaborative playbooks and boards.
|
||||
|
||||
### pgloader
|
||||
|
||||
Use the `pgloader` tool to migrate your data from MySQL to PostgreSQL.
|
||||
|
||||
#### Install pgloader
|
||||
|
||||
To install `pgloader`, see the official [installation guide](https://pgloader.readthedocs.io/en/latest/install.html).
|
||||
|
||||
<Note>
|
||||
|
||||
If you are using MySQL v8: Due to a [known bug](https://github.com/dimitri/pgloader/issues/1183) in pgloader-compiled binaries, you need to compile pgloader from the source. Please follow the steps [here](https://pgloader.readthedocs.io/en/latest/install.html#build-from-sources) to build from the source.
|
||||
|
||||
</Note>
|
||||
|
||||
Alternatively, you may want to use our `mattermost-pgloader` Docker image to avoid installing or building `pgloader`. See the documentation below for details.
|
||||
|
||||
#### Use pgloader
|
||||
|
||||
##### Pull the Docker image and verify pgloader
|
||||
|
||||
For a manual migration, run the following command to pull the `mattermost-pgloader` image and verify that pgloader is working correctly:
|
||||
|
||||
``` sh
|
||||
docker run -it --rm -v $(pwd):/home/migration mattermost/mattermost-pgloader:latest pgloader --version
|
||||
```
|
||||
|
||||
This command pulls the `mattermost/mattermost-pgloader:latest` image and runs `pgloader` to check its version and ensure it works as expected.
|
||||
|
||||
##### Map local directory
|
||||
|
||||
Use the `-v $(pwd):/home/migration` flag to map your current working directory to the Docker container. This allows you to use your local directory for storing logs and other files.
|
||||
|
||||
##### Set network configuration
|
||||
|
||||
Depending on your network requirements, set the `--network` flag accordingly. For example, to access localhost, you need to set the network to `host`.
|
||||
|
||||
### morph
|
||||
|
||||
The `morph` tool creates the PostgreSQL schema.
|
||||
|
||||
<Note>
|
||||
|
||||
Both `morph` and `dbcmp` requires Go toolchain. To install Go compiler, follow the [Go documentation](https://go.dev/doc/install).
|
||||
|
||||
</Note>
|
||||
|
||||
#### Install morph
|
||||
|
||||
You can install morph CLI by running the following command:
|
||||
|
||||
``` sh
|
||||
go install github.com/mattermost/morph/cmd/morph@v1
|
||||
```
|
||||
|
||||
### dbcmp (Optional)
|
||||
|
||||
The `dbcmp` tool enables you to compare the data following the migration by comparing every table and reporting whether there is a diversion between two schemas.
|
||||
|
||||
#### Install dbcmp
|
||||
|
||||
You can install [dbcmp](https://github.com/mattermost/dbcmp) by running the following command:
|
||||
|
||||
``` sh
|
||||
go install github.com/mattermost/dbcmp/cmd/dbcmp@latest
|
||||
```
|
||||
|
||||
## System requirements and configurations for manual migrations
|
||||
|
||||
Before starting a manual migration process, it's essential to ensure that your system meets the necessary requirements for a smooth and efficient migration. We strongly recommend the following system specifications and adjustments:
|
||||
|
||||
- Ensure you have enough system memory resources. 16GB of RAM is recommended as a default. In scenarios where system memory is insufficient, users can fine-tune pgloader settings, such as the `number of workers`, `prefetch rows`, and especially `rows per range` if `concurrency` is set above `1`. These adjustments can help optimize resource utilization based on available system resources. For further detail see [pgloader documentation](https://pgloader.readthedocs.io/en/latest/batches.html).
|
||||
- A multi-core processor with sufficient processing power is recommended for the migration process, especially when dealing with large datasets.
|
||||
- Ensure that there is enough disk space available for storing both the MySQL and PostgreSQL databases, as well as any temporary files generated during the migration process. The amount of required disk space depends on the size of the databases being migrated.
|
||||
- To reduce migration time further, users may choose to manually drop indexes on the target PostgreSQL database before initiating the migration process. This approach can potentially accelerate the migration by reducing overhead with index builds during data insertion.
|
||||
|
||||
### Before a manual migration
|
||||
|
||||
<Important>
|
||||
|
||||
This guide requires a schema of v7.1 ESR or later. So, if you have an earlier version and planning to migrate, please update your Mattermost Server to v7.1 at a minimum. See the [extended support releases](/product-overview/release-policy#extended-support-releases) documentation for details.
|
||||
|
||||
- Back up your MySQL data.
|
||||
- Confirm your Mattermost version. See the **About** modal for details.
|
||||
- Schedule the migration window. This process requires you to stop the Mattermost Server during the migration.
|
||||
- See the [schema-diffs](#schema-diffs) section to ensure data compatibility between schemas.
|
||||
- Prepare your PostgreSQL environment by creating a database and user. See the [database](/deployment-guide/server/prepare-database) documentation for details.
|
||||
- On [newer versions](https://www.postgresql.org/docs/release/15.0/) of PostgreSQL, newly created users do not have access to `public` schema. The access should be explicitly granted by running `GRANT ALL ON SCHEMA public to mmuser`.
|
||||
|
||||
</Important>
|
||||
|
||||
### Schema diffs
|
||||
|
||||
Before the manual migration, due to differences between the two schemas, some manual steps may be required for an error-free migration.
|
||||
|
||||
#### Text to character varying
|
||||
|
||||
We encourage you to check if the sizes are consistent within the PostgreSQL schema limits since the Mattermost MySQL schema uses the `text` column type in the various tables instead of `varchar` representation in the PostgreSQL schema.
|
||||
|
||||
You can check if there are any required deletions or updates. For example, to do so in the `Audits` table/`Action` column; run:
|
||||
|
||||
``` sql
|
||||
SELECT FROM mattermost.Audits where LENGTH(Action) > 512;
|
||||
```
|
||||
|
||||
The following table shows the deletions or updates you can proceed with that don't incur further consequences.
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Table</th>
|
||||
<th>Column</th>
|
||||
<th>Data type casting</th>
|
||||
<th>Consequence on deletion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Audits</td>
|
||||
<td>Action</td>
|
||||
<td>text -> varchar(512)</td>
|
||||
<td>No side effect on how the application works (The affected row needs to be deleted).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Audits</td>
|
||||
<td>ExtraInfo</td>
|
||||
<td>text -> varchar(1024)</td>
|
||||
<td>No side effect on how the application works (The affected row needs to be deleted).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ClusterDiscovery</td>
|
||||
<td>HostName</td>
|
||||
<td>text -> varchar(512)</td>
|
||||
<td>No side effect on how the application works (The affected row needs to be deleted).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Commands</td>
|
||||
<td>IconURL</td>
|
||||
<td>text -> varchar(1024)</td>
|
||||
<td>The field can be deleted or updated with a new URL.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Commands</td>
|
||||
<td>AutoCompleteDesc</td>
|
||||
<td>text -> varchar(1024)</td>
|
||||
<td>The field can be deleted or rewritten.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Commands</td>
|
||||
<td>AutoCompleteHint</td>
|
||||
<td>text -> varchar(1024)</td>
|
||||
<td>The field can be deleted or rewritten.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>RemoteClusters</td>
|
||||
<td>Topics</td>
|
||||
<td>text -> varchar(512)</td>
|
||||
<td>The field can be removed.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Systems</td>
|
||||
<td>Value</td>
|
||||
<td>text -> varchar(1024)</td>
|
||||
<td>Edge case, ideally should never happen.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
The following table shows several occurrences where the schema can differ and data size constraints within the PostgreSQL schema can result in errors. Each table/row requires individual inspection hence we added the possible consequence of deletion.
|
||||
|
||||
<Tip>
|
||||
|
||||
Several reports have been received from our community that the `LinkMetadata` and `FileInfo` tables had some overflows, so we recommend checking these tables in particular. Please check if your data in the MySQL schema exceeds these limitations.
|
||||
|
||||
</Tip>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Table</th>
|
||||
<th>Column</th>
|
||||
<th>Data type casting</th>
|
||||
<th>Consequence on deletion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Compliances</td>
|
||||
<td>Keywords</td>
|
||||
<td>text -> varchar(512)</td>
|
||||
<td>The filter for compliance needs to be updated.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Compliances</td>
|
||||
<td>Emails</td>
|
||||
<td>text -> varchar(1024)</td>
|
||||
<td>The filter for compliance needs to be updated.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>FileInfo</td>
|
||||
<td>Path</td>
|
||||
<td>text -> varchar(512)</td>
|
||||
<td>Previous links to this file won't work (The affected row needs to be deleted).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>FileInfo</td>
|
||||
<td>ThumbnailPath</td>
|
||||
<td>text -> varchar(512)</td>
|
||||
<td>Previous links to this file won't work (The affected row needs to be deleted).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>FileInfo</td>
|
||||
<td>PreviewPath</td>
|
||||
<td>text -> varchar(512)</td>
|
||||
<td>Previous links to this file won't work (The affected row needs to be deleted).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>FileInfo</td>
|
||||
<td>Name</td>
|
||||
<td>text -> varchar(256)</td>
|
||||
<td>Previous links to this file won't work (The affected row needs to be deleted).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>FileInfo</td>
|
||||
<td>MimeType</td>
|
||||
<td>text -> varchar(256)</td>
|
||||
<td>Previous links to this file won't work (The affected row needs to be deleted).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>LinkMetadata</td>
|
||||
<td>URL</td>
|
||||
<td>text -> varchar(2048)</td>
|
||||
<td>Previous links to this file won't work (The affected row needs to be deleted).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>RemoteClusters</td>
|
||||
<td>SiteURL</td>
|
||||
<td>text -> varchar(512)</td>
|
||||
<td>Previous remote cluster will be removed (The affected row needs to be deleted).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sessions</td>
|
||||
<td>DeviceId</td>
|
||||
<td>text -> varchar(512)</td>
|
||||
<td>Users will be logged out on these devices (The affected row needs to be deleted).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>UploadSessions</td>
|
||||
<td>FileName</td>
|
||||
<td>text -> varchar(256)</td>
|
||||
<td>The upload session will be lost (The affected row needs to be deleted).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>UploadSessions</td>
|
||||
<td>Path</td>
|
||||
<td>text -> varchar(512)</td>
|
||||
<td>The upload session will be lost (The affected row needs to be deleted).</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
#### Full-text indexes
|
||||
|
||||
It's possible that some words in the `Posts` and `FileInfo` tables can exceed the [limits of the maximum token length](https://www.postgresql.org/docs/11/textsearch-limitations.html) for full-text search indexing. In these cases, we are dropping the `idx_posts_message_txt` and `idx_fileinfo_content_txt` indexes from the PostgreSQL schema, and creating these indexes after the migration by running the following queries:
|
||||
|
||||
To prevent errors during the migration, we have included following queries:
|
||||
|
||||
``` text
|
||||
DROP INDEX IF EXISTS {{ .source_db }}.idx_posts_message_txt;
|
||||
DROP INDEX IF EXISTS {{ .source_db }}.idx_fileinfo_content_txt;
|
||||
```
|
||||
|
||||
#### Unsupported unicode sequences
|
||||
|
||||
There is a specific unicode sequence that is [disallowed](https://www.postgresql.org/docs/16/datatype-json.html#DATATYPE-JSON) in PostgreSQL which is `\u0000`. There is a chance that this sequence may appear in several rows across a bunch of tables in your MySQL database. If it is the case, during the migration you will likely receive an error as following: `unsupported Unicode escape sequence: \u0000 cannot be converted to text.`. To prevent this from happening, we advise to sanitize your data before starting to the migration. You can use the following query to replace `\u0000` sequence with empty string.
|
||||
|
||||
<Note>
|
||||
|
||||
You can use this query as-is in a script, or you may need to set the delimiter to something else (e.g., `DELIMITER //`) when defining it in the MySQL console. Once you are done defining the procedure, please set the delimiter back to the original (i.e., `DELIMITER ;`).
|
||||
|
||||
</Note>
|
||||
|
||||
``` text
|
||||
CREATE PROCEDURE SanitizeUnsupportedUnicode()
|
||||
BEGIN
|
||||
DECLARE done INT DEFAULT FALSE;
|
||||
DECLARE curTableName text;
|
||||
DECLARE curColumnName text;
|
||||
DECLARE cursors CURSOR FOR
|
||||
SELECT table_name, column_name
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE data_type = 'json'
|
||||
AND table_schema = DATABASE();
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
|
||||
|
||||
OPEN cursors;
|
||||
|
||||
WHILE NOT DONE DO
|
||||
FETCH cursors INTO curTableName, curColumnName;
|
||||
SET @query_string = CONCAT('UPDATE ', curTableName, ' SET ', curColumnName, ' = REPLACE(', curColumnName, ', \'\\\\u0000\', \'\') WHERE ', curColumnName, ' LIKE \'%\\u0000%\';');
|
||||
|
||||
PREPARE dynamic_query FROM @query_string;
|
||||
EXECUTE dynamic_query;
|
||||
DEALLOCATE PREPARE dynamic_query;
|
||||
END WHILE;
|
||||
|
||||
CLOSE cursors;
|
||||
END;
|
||||
|
||||
CALL SanitizeUnsupportedUnicode();
|
||||
|
||||
DROP PROCEDURE IF EXISTS SanitizeUnsupportedUnicode;
|
||||
```
|
||||
|
||||
<Note>
|
||||
|
||||
There is also a specific byte sequence value that is not allowed and will cause an `invalid byte sequence for encoding 'UTF8': 0x00"` error during the migration. To prevent this error, you can add the `remove-null-characters` clause to the text casting rules. However, since pgloader will modify the data on the fly, there may be differences between the tables (if any are affected) during the comparison phase.
|
||||
|
||||
</Note>
|
||||
|
||||
#### Artifacts may remain from previous configurations/versions
|
||||
|
||||
Prior to `v6.4`, Mattermost was using [golang-migrate](https://github.com/golang-migrate/migrate) to handle the schema migrations. Since we don't use it anymore, we exclude the table `schema_migrations`. If you were using Mattermost before `v6.4` consider dropping this table and excluding it from comparison as well.
|
||||
|
||||
``` sql
|
||||
DROP TABLE mattermost.schema_migrations;
|
||||
```
|
||||
|
||||
Some community members have reported that they had `description` and `nextsyncat` columns in their `SharedChannelRemotes` table. These columns should be removed from the table. Consider running the following DDL to drop the columns. (This migration will be added to future versions of Mattermost).
|
||||
|
||||
``` sql
|
||||
ALTER TABLE SharedChannelRemotes DROP COLUMN description, DROP COLUMN nextsyncat;
|
||||
```
|
||||
|
||||
An error has been identified in the 96th migration that was previously released. Before proceeding with the migration, it is necessary to remove a specific column. To ensure the Threads table reaches the expected state, execute the following prepared statement:
|
||||
|
||||
``` sql
|
||||
SET @preparedStatement = (SELECT IF(
|
||||
(
|
||||
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE table_name = 'Threads'
|
||||
AND table_schema = DATABASE()
|
||||
AND column_name = 'TeamId'
|
||||
) > 0,
|
||||
'ALTER TABLE Threads DROP COLUMN TeamId;',
|
||||
'SELECT 1'
|
||||
));
|
||||
|
||||
PREPARE alterIfExists FROM @preparedStatement;
|
||||
EXECUTE alterIfExists;
|
||||
DEALLOCATE PREPARE alterIfExists;
|
||||
```
|
||||
|
||||
#### Configuration in database
|
||||
|
||||
If you were previously utilizing a database for handling the [Mattermost configuration](/administration-guide/configure/configuration-in-your-database), those tables will not be migrated from your MySQL database with the migration [script](#migrate-the-data).
|
||||
|
||||
Two migrations are necessary:
|
||||
|
||||
- migrate database configuration to the file system
|
||||
- migrate file system configuration back to the database
|
||||
|
||||
##### Migrate database configuration to the file system
|
||||
|
||||
Use the `mmctl config migrate` command to [migrate your config](/administration-guide/manage/mmctl-command-line-tool#mmctl-config-migrate) to the file system, as follows:
|
||||
|
||||
``` sh
|
||||
mmctl config migrate "<DB_USER>:<DB_PASS>@tcp(<DB_HOST>:3306)/<DB_NAME>?charset=utf8mb4,utf8&readTimeout=30s&writeTimeout=30s&multiStatements=true" /opt/mattermost/config/config.json --local
|
||||
```
|
||||
|
||||
Where `<DB_USER>`, `<DB_PASS>`, `<DB_HOST>`, and `<DB_NAME>` are replaced with your environment values. Ensure you use `--local` when running this command. The first parameters (`<DB_USER>`, `<DB_PASS>`) is the database the configuration is stored in, the second parameter (`<DB_HOST>`, `<DB_NAME>`) is the file we are saving the configuration to.
|
||||
|
||||
In the configuration file, update the `SqlSettings.DataSource` and `SqlSettings.DriverName` fields to reflect new changes. To do so, open the `json` file and change the respective fields.
|
||||
|
||||
##### Migrate file system configuration back to the database
|
||||
|
||||
To save configuration back to the database, Use the `mmctl config migrate` command again and reverse the parameters. Ensure you use the new database credentials moving it back to the target database.
|
||||
|
||||
``` sql
|
||||
SELECT * FROM Configurations WHERE Active = 't';
|
||||
```
|
||||
|
||||
You should update the `SqlSettings.DataSource` and `SqlSettings.DriverName` fields accordingly. Also, note that the `MM_CONFIG` environment variable should point to the new DSN after the migration is completed.
|
||||
|
||||
## Prepare target database
|
||||
|
||||
It is essential to create tables and indexes to ensure that the PostgreSQL database schema is properly structured according to the required specifications. Since Mattermost repository contains all of the required SQL queries to achieve that, we can leverage this by running the following steps:
|
||||
|
||||
- Clone the `mattermost` repository for your specific version:
|
||||
|
||||
``` sh
|
||||
git clone -b <your current version (eg. release-7.8)> git@github.com:mattermost/mattermost.git --depth=1
|
||||
```
|
||||
|
||||
- Run all schema migrations\* on your PostgreSQL database using morph CLI with the following command:
|
||||
|
||||
``` sh
|
||||
morph apply up --driver postgres --dsn "postgres://user:pass@localhost:5432/<target_db_mame>?sslmode=disable" --path ./mattermost/db/migrations/postgres --number -1
|
||||
```
|
||||
|
||||
\* After `v8`, due to project re-organization, the migrations directory has been changed to `./mattermost/server/channels/db/migrations/postgres/` relative to where you cloned Mattermost repository. Please set `--path` flag accordingly.
|
||||
|
||||
## Migrate the data
|
||||
|
||||
Once we set the schema to a desired state, we can start migrating the **data** by running `pgloader`
|
||||
|
||||
<Note>
|
||||
|
||||
In the example below, the hosts for both databases are assumed to be in the same instance. Please update addresses accordingly if they are on different machines. Also you can test the `.load` file by simply running `pgloader` with `--dry-run` flag. For instance `pgloader --dry-run migration.load` command.
|
||||
|
||||
</Note>
|
||||
|
||||
Use the following configuration for the baseline of the data migration:
|
||||
|
||||
``` text
|
||||
LOAD DATABASE
|
||||
FROM mysql://{{ .mysql_user }}:{{ .mysql_password }}@{{ .mysql_address }}/{{ .source_db }}
|
||||
INTO pgsql://{{ .pg_user }}:{{ .pg_password }}@{{ .postgres_address }}/{{ .target_db }}
|
||||
|
||||
WITH data only,
|
||||
workers = 8, concurrency = 1,
|
||||
multiple readers per thread, rows per range = 10000,
|
||||
prefetch rows = 10000, batch rows = 2500,
|
||||
create no tables, create no indexes,
|
||||
preserve index names
|
||||
|
||||
SET PostgreSQL PARAMETERS
|
||||
maintenance_work_mem to '128MB',
|
||||
work_mem to '12MB'
|
||||
|
||||
SET MySQL PARAMETERS
|
||||
net_read_timeout = '120',
|
||||
net_write_timeout = '120'
|
||||
|
||||
CAST column Channels.Type to "channel_type" drop typemod,
|
||||
column Teams.Type to "team_type" drop typemod,
|
||||
column UploadSessions.Type to "upload_session_type" drop typemod,
|
||||
column ChannelBookmarks.Type to "channel_bookmark_type" drop typemod,
|
||||
column Drafts.Priority to text,
|
||||
type int when (= precision 11) to integer drop typemod,
|
||||
type bigint when (= precision 20) to bigint drop typemod,
|
||||
type text to varchar drop typemod using remove-null-characters,
|
||||
type tinyint when (<= precision 4) to boolean using tinyint-to-boolean,
|
||||
type json to jsonb drop typemod using remove-null-characters
|
||||
|
||||
EXCLUDING TABLE NAMES MATCHING ~<IR_>, ~<focalboard>, 'schema_migrations', 'db_migrations', 'db_lock',
|
||||
'Configurations', 'ConfigurationFiles', 'db_config_migrations', 'calls'
|
||||
|
||||
BEFORE LOAD DO
|
||||
$$ ALTER SCHEMA public RENAME TO {{ .source_db }}; $$,
|
||||
$$ TRUNCATE TABLE {{ .source_db }}.systems; $$,
|
||||
$$ DROP INDEX IF EXISTS {{ .source_db }}.idx_posts_message_txt; $$,
|
||||
$$ DROP INDEX IF EXISTS {{ .source_db }}.idx_fileinfo_content_txt; $$
|
||||
|
||||
AFTER LOAD DO
|
||||
$$ UPDATE {{ .source_db }}.db_migrations set name='add_createat_to_teamembers' where version=92; $$,
|
||||
$$ ALTER SCHEMA {{ .source_db }} RENAME TO public; $$,
|
||||
$$ SELECT pg_catalog.set_config('search_path', '"$user", public', false); $$,
|
||||
$$ ALTER USER {{ .pg_user }} SET SEARCH_PATH TO 'public'; $$;
|
||||
```
|
||||
|
||||
Once you save this configuration file, e.g. `migration.load`, you can run the `pgloader` with the following command:
|
||||
|
||||
``` sh
|
||||
pgloader migration.load > migration.log
|
||||
```
|
||||
|
||||
Feel free to contribute to and/or report your findings through your migration to us.
|
||||
|
||||
## After the migration
|
||||
|
||||
### Restore full-text indexes
|
||||
|
||||
To avoid performance regression on `Posts` and `FileInfo` table access, following queries should be executed once the migration finishes:
|
||||
|
||||
``` sql
|
||||
CREATE INDEX IF NOT EXISTS idx_posts_message_txt ON public.posts USING gin(to_tsvector('english', message));
|
||||
CREATE INDEX IF NOT EXISTS idx_fileinfo_content_txt ON public.fileinfo USING gin(to_tsvector('english', content));
|
||||
```
|
||||
|
||||
<Note>
|
||||
|
||||
If any of the entries in your `Posts` and `FileInfo` tables exceed the limit mentioned above, index creation query will warn with the `ERROR: string is too long for tsvector` log while trying to create these indexes. This means the content that didn't fit into a `tsvector` was ignored. If you still want to index the truncated content, you can use `substring()` function on the content while creating the indexes.
|
||||
|
||||
Start with a substring length of 1000000 and gradually decrease if the error persists:
|
||||
|
||||
</Note>
|
||||
|
||||
``` sql
|
||||
-- Start with 1000000
|
||||
CREATE INDEX IF NOT EXISTS idx_fileinfo_content_txt ON public.fileinfo USING gin(to_tsvector('english', substring(content,0,1000000)));
|
||||
|
||||
-- If that fails, try 800000
|
||||
CREATE INDEX IF NOT EXISTS idx_fileinfo_content_txt ON public.fileinfo USING gin(to_tsvector('english', substring(content,0,800000)));
|
||||
|
||||
-- Continue decreasing as needed (e.g., 600000, 500000) until the index creates successfully
|
||||
```
|
||||
|
||||
### Compare the data
|
||||
|
||||
We developed an internal tool called `dbcmp` to simplify database comparison. It checks every table in two databases and reports any differences in their schemas. However, `dbcmp` does not compare individual rows. Instead, it calculates checksum values based on a specified page-size and compares them. This means it cannot generate row-level diffs.
|
||||
|
||||
We recommend using `dbcmp` as an additional check to verify data integrity, especially if custom casting rules (beyond the defaults or those provided by us) were used during migration. Otherwise, running this tool is not necessary.
|
||||
|
||||
The tool includes a few flags to run a comparison:
|
||||
|
||||
``` sh
|
||||
Usage:
|
||||
dbcmp [flags]
|
||||
|
||||
Flags:
|
||||
--exclude strings exclude tables from comparison, takes comma-separated values.
|
||||
--include strings include only matching tables for comparison, takes comma-separated values.
|
||||
-h, --help help for dbcmp
|
||||
--source string source database dsn
|
||||
--target string target database dsn
|
||||
-v, --version version for dbcmp
|
||||
```
|
||||
|
||||
<Note>
|
||||
|
||||
`--exclude` and `--include` flags are mutually exclusive and they can't be used together.
|
||||
|
||||
</Note>
|
||||
|
||||
For our case, we can simply run the following command:
|
||||
|
||||
``` sh
|
||||
dbcmp --source "${MYSQL_DSN}" --target "${POSTGRES_DSN} " --include="posts,users"
|
||||
```
|
||||
|
||||
An example command would look like: `dbcmp --source "user:password@tcp(address:3306)/db_name --target "postgres://user:password@address:5432/db_name`
|
||||
|
||||
<Note>
|
||||
|
||||
`POSTGRES_DSN` should start with a `postgres://` prefix. This way `dbcmp` decides which driver to use while connecting to a database.
|
||||
|
||||
</Note>
|
||||
|
||||
Another exclusion we are making is in the `db_migrations` table which has a small difference (a typo in a single migration name) and creates a diff. Since we created the PostgreSQL schema with morph, and the official `mattermost` source, we can skip it safely without concerns. On the other hand, `systems` table may contain additional diffs if there were extra keys added during some of the migrations. Consider excluding the `systems` table if you run into issues, and perform a manual comparison as the data in the `systems` table is relatively smaller in size.
|
||||
|
||||
<Note>
|
||||
|
||||
If the `remove-null-characters` transform function is utilized during the migration and there were `0x00` byte sequences in the MySQL database, those tables will have differences during the comparison phase.
|
||||
|
||||
</Note>
|
||||
|
||||
### Restore the search path
|
||||
|
||||
If you closely examine the `pgloader` configuration file (e.g., `migration.load`), you will notice that the `search_path` of the database user is set to `public`. This is the only requirement for the Mattermost application. However, if you need to include other schemas in the search path, you should modify the `search_path` accordingly to meet your specific requirements.
|
||||
|
||||
## Plugin migrations
|
||||
|
||||
On the plugin side, we are going to take a different approach from what we have done above. We are not going to use `morph` tool to create tables and indexes this time. We are going to utilize `pgloader` to create the tables on behalf of us. The reason for doing so is that collaborative playbooks and boards leverage application logic to facilitate SQL queries. But we don't want to use any level of application at this point.
|
||||
|
||||
### Collaborative playbooks
|
||||
|
||||
The `pgloader` configuration provided for playbooks is based on `v1.38.1` and the plugin should be at least `v1.36.0` to perform the migration.
|
||||
|
||||
Once we are ready to migrate, we can start migrating the **schema** and the **data** by running `pgloader`
|
||||
|
||||
Use the following configuration for the baseline of the data migration:
|
||||
|
||||
``` text
|
||||
LOAD DATABASE
|
||||
FROM mysql://{{ .mysql_user }}:{{ .mysql_password }}@{{ .mysql_address }}/{{ .source_db }}
|
||||
INTO pgsql://{{ .pg_user }}:{{ .pg_password }}@{{ .postgres_address }}/{{ .target_db }}
|
||||
|
||||
WITH include drop, create tables, create indexes, no foreign keys,
|
||||
workers = 8, concurrency = 1,
|
||||
multiple readers per thread, rows per range = 50000,
|
||||
preserve index names
|
||||
|
||||
SET PostgreSQL PARAMETERS
|
||||
maintenance_work_mem to '128MB',
|
||||
work_mem to '12MB'
|
||||
|
||||
SET MySQL PARAMETERS
|
||||
net_read_timeout = '120',
|
||||
net_write_timeout = '120'
|
||||
|
||||
CAST column IR_ChannelAction.ActionType to text drop typemod,
|
||||
column IR_ChannelAction.TriggerType to text drop typemod,
|
||||
column IR_Incident.ChecklistsJSON to "json" drop typemod
|
||||
|
||||
INCLUDING ONLY TABLE NAMES MATCHING
|
||||
~/IR_/
|
||||
|
||||
BEFORE LOAD DO
|
||||
$$ ALTER SCHEMA public RENAME TO {{ .source_db }}; $$
|
||||
|
||||
AFTER LOAD DO
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_ChannelAction ALTER COLUMN ActionType TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_ChannelAction ALTER COLUMN TriggerType TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN ReminderMessageTemplate TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN ReminderMessageTemplate SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN ConcatenatedInvitedUserIDs TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN ConcatenatedInvitedUserIDs SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN ConcatenatedWebhookOnCreationURLs TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN ConcatenatedWebhookOnCreationURLs SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN ConcatenatedInvitedGroupIDs TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN ConcatenatedInvitedGroupIDs SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN Retrospective TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN Retrospective SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN MessageOnJoin TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN MessageOnJoin SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN ConcatenatedWebhookOnStatusUpdateURLs TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN ConcatenatedWebhookOnStatusUpdateURLs SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN CategoryName TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN CategoryName SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN ConcatenatedBroadcastChannelIds TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN ConcatenatedBroadcastChannelIds SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN ChannelIDToRootID TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Incident ALTER COLUMN ChannelIDToRootID SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ReminderMessageTemplate TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ReminderMessageTemplate SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ConcatenatedInvitedUserIDs TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ConcatenatedInvitedUserIDs SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ConcatenatedWebhookOnCreationURLs TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ConcatenatedWebhookOnCreationURLs SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ConcatenatedInvitedGroupIDs TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ConcatenatedInvitedGroupIDs SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN MessageOnJoin TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN MessageOnJoin SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN RetrospectiveTemplate TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN RetrospectiveTemplate SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ConcatenatedWebhookOnStatusUpdateURLs TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ConcatenatedWebhookOnStatusUpdateURLs SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ConcatenatedSignalAnyKeywords TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ConcatenatedSignalAnyKeywords SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN CategoryName TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN CategoryName SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ChecklistsJSON TYPE JSON USING ChecklistsJSON::JSON; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ConcatenatedBroadcastChannelIds TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ConcatenatedBroadcastChannelIds SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN RunSummaryTemplate TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN RunSummaryTemplate SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ChannelNameTemplate TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Playbook ALTER COLUMN ChannelNameTemplate SET DEFAULT ''::text; $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_PlaybookMember ALTER COLUMN Roles TYPE varchar(65536); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Category_Item ADD CONSTRAINT ir_category_item_categoryid FOREIGN KEY (CategoryId) REFERENCES {{ .source_db }}.IR_Category(Id); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Metric ADD CONSTRAINT ir_metric_metricconfigid FOREIGN KEY (MetricConfigId) REFERENCES {{ .source_db }}.IR_MetricConfig(Id); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Metric ADD CONSTRAINT ir_metric_incidentid FOREIGN KEY (IncidentId) REFERENCES {{ .source_db }}.IR_Incident(Id); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_MetricConfig ADD CONSTRAINT ir_metricconfig_playbookid FOREIGN KEY (PlaybookId) REFERENCES {{ .source_db }}.IR_Playbook(Id); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_PlaybookAutoFollow ADD CONSTRAINT ir_playbookautofollow_playbookid FOREIGN KEY (PlaybookId) REFERENCES {{ .source_db }}.IR_Playbook(Id); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_PlaybookMember ADD CONSTRAINT ir_playbookmember_playbookid FOREIGN KEY (PlaybookId) REFERENCES {{ .source_db }}.IR_Playbook(Id); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_Run_Participants ADD CONSTRAINT ir_run_participants_incidentid FOREIGN KEY (IncidentId) REFERENCES {{ .source_db }}.IR_Incident(Id); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_StatusPosts ADD CONSTRAINT ir_statusposts_incidentid FOREIGN KEY (IncidentId) REFERENCES {{ .source_db }}.IR_Incident(Id); $$,
|
||||
$$ ALTER TABLE {{ .source_db }}.IR_TimelineEvent ADD CONSTRAINT ir_timelineevent_incidentid FOREIGN KEY (IncidentId) REFERENCES {{ .source_db }}.IR_Incident(Id); $$,
|
||||
$$ CREATE UNIQUE INDEX IF NOT EXISTS ir_playbookmember_playbookid_memberid_key on {{ .source_db }}.IR_PlaybookMember(PlaybookId,MemberId); $$,
|
||||
$$ CREATE INDEX IF NOT EXISTS ir_statusposts_incidentid_postid_key on {{ .source_db }}.IR_StatusPosts(IncidentId,PostId); $$,
|
||||
$$ CREATE INDEX IF NOT EXISTS ir_playbookmember_playbookid on {{ .source_db }}.IR_PlaybookMember(PlaybookId); $$,
|
||||
$$ ALTER SCHEMA {{ .source_db }} RENAME TO public; $$,
|
||||
$$ SELECT pg_catalog.set_config('search_path', '"$user", public', false); $$,
|
||||
$$ ALTER USER {{ .pg_user }} SET SEARCH_PATH TO 'public'; $$;
|
||||
```
|
||||
|
||||
``` sh
|
||||
pgloader playbooks.load > playbooks_migration.log
|
||||
```
|
||||
|
||||
### Focalboard
|
||||
|
||||
As of `v9.0` Boards will transition to being fully community supported as the Focalboard plugin. Hence this guide covers only version `v7.10.x` of the schema. [Official announcement](/product-overview/deprecated-features#mattermost-server-v900).
|
||||
|
||||
Once we are ready to migrate, we can start migrating the **schema** and the **data** by running `pgloader`
|
||||
|
||||
Use the following configuration for the baseline of the data migration:
|
||||
|
||||
``` text
|
||||
LOAD DATABASE
|
||||
FROM mysql://{{ .mysql_user }}:{{ .mysql_password }}@{{ .mysql_address }}/{{ .source_db }}
|
||||
INTO pgsql://{{ .pg_user }}:{{ .pg_password }}@{{ .postgres_address }}/{{ .target_db }}
|
||||
|
||||
WITH include drop, create tables, create indexes, reset sequences,
|
||||
workers = 8, concurrency = 1,
|
||||
multiple readers per thread, rows per range = 50000,
|
||||
preserve index names
|
||||
|
||||
SET PostgreSQL PARAMETERS
|
||||
maintenance_work_mem to '128MB',
|
||||
work_mem to '12MB'
|
||||
|
||||
SET MySQL PARAMETERS
|
||||
net_read_timeout = '120',
|
||||
net_write_timeout = '120'
|
||||
|
||||
CAST column focalboard_blocks.fields to "json" drop typemod,
|
||||
column focalboard_blocks_history.fields to "json" drop typemod,
|
||||
column focalboard_schema_migrations.name to "varchar" drop typemod,
|
||||
column focalboard_sessions.props to "json" drop typemod,
|
||||
column focalboard_teams.settings to "json" drop typemod,
|
||||
column focalboard_users.props to "json" drop typemod,
|
||||
type int when (= precision 11) to int4 drop typemod,
|
||||
type json to jsonb drop typemod
|
||||
|
||||
INCLUDING ONLY TABLE NAMES MATCHING
|
||||
~/focalboard/
|
||||
|
||||
BEFORE LOAD DO
|
||||
$$ ALTER SCHEMA public RENAME TO {{ .source_db }}; $$
|
||||
|
||||
AFTER LOAD DO
|
||||
$$ UPDATE {{ .source_db }}.focalboard_blocks SET "fields" = '{}'::json WHERE "fields"::text = ''; $$,
|
||||
$$ UPDATE {{ .source_db }}.focalboard_blocks_history SET "fields" = '{}'::json WHERE "fields"::text = ''; $$,
|
||||
$$ UPDATE {{ .source_db }}.focalboard_sessions SET "props" = '{}'::json WHERE "props"::text = ''; $$,
|
||||
$$ UPDATE {{ .source_db }}.focalboard_teams SET "settings" = '{}'::json WHERE "settings"::text = ''; $$,
|
||||
$$ UPDATE {{ .source_db }}.focalboard_users SET "props" = '{}'::json WHERE "props"::text = ''; $$,
|
||||
$$ ALTER SCHEMA {{ .source_db }} RENAME TO public; $$,
|
||||
$$ SELECT pg_catalog.set_config('search_path', '"$user", public', false); $$,
|
||||
$$ ALTER USER {{ .pg_user }} SET SEARCH_PATH TO 'public'; $$;
|
||||
```
|
||||
|
||||
``` sh
|
||||
pgloader focalboard.load > focalboard_migration.log
|
||||
```
|
||||
|
||||
### Calls
|
||||
|
||||
If you are running a version of Mattermost that is greater than `v9.9` or the Calls plugin above `v0.27`, you can opt to migrate the data for the plugin. We are going to take a similar approach with Boards and Playbooks migration and let pgloader create the tables.
|
||||
|
||||
Once we are ready to migrate, we can start migrating the **schema** and the **data** by running `pgloader`
|
||||
|
||||
Use the following configuration for the baseline of the data migration:
|
||||
|
||||
``` text
|
||||
LOAD DATABASE
|
||||
FROM mysql://{{ .mysql_user }}:{{ .mysql_password }}@{{ .mysql_address }}/{{ .source_db }}
|
||||
INTO pgsql://{{ .pg_user }}:{{ .pg_password }}@{{ .postgres_address }}/{{ .target_db }}
|
||||
|
||||
WITH include drop, create tables, create indexes, reset sequences,
|
||||
workers = 8, concurrency = 1,
|
||||
multiple readers per thread, rows per range = 50000,
|
||||
preserve index names
|
||||
|
||||
SET PostgreSQL PARAMETERS
|
||||
maintenance_work_mem to '128MB',
|
||||
work_mem to '12MB'
|
||||
|
||||
SET MySQL PARAMETERS
|
||||
net_read_timeout = '120',
|
||||
net_write_timeout = '120'
|
||||
|
||||
CAST type json to jsonb drop typemod
|
||||
|
||||
INCLUDING ONLY TABLE NAMES MATCHING
|
||||
~/calls/
|
||||
|
||||
BEFORE LOAD DO
|
||||
$$ ALTER SCHEMA public RENAME TO {{ .source_db }}; $$
|
||||
|
||||
AFTER LOAD DO
|
||||
$$ ALTER SCHEMA {{ .source_db }} RENAME TO public; $$,
|
||||
$$ SELECT pg_catalog.set_config('search_path', '"$user", public', false); $$,
|
||||
$$ ALTER USER {{ .pg_user }} SET SEARCH_PATH TO 'public'; $$;
|
||||
```
|
||||
|
||||
``` sh
|
||||
pgloader calls.load > calls_migration.log
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
See [troubleshooting errors during migration from MySQL to PostgreSQL](/administration-guide/manage/admin/postgres-migration#troubleshooting)
|
||||
12
docs/main/administration-guide/manage/admin/migration.mdx
Normal file
12
docs/main/administration-guide/manage/admin/migration.mdx
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title: "Migrate"
|
||||
---
|
||||
Getting data in and out of a Mattermost deployment, moving onto Mattermost from another platform, and moving an existing deployment onto different infrastructure.
|
||||
|
||||
- [Bulk import data](/administration-guide/onboard/bulk-loading-data) - Import users, teams, channels, and posts from a JSONL file.
|
||||
- [Bulk export data](/administration-guide/manage/bulk-export-tool) - Export users, teams, channels, and posts to a JSONL file.
|
||||
- [Migrate from MySQL to PostgreSQL](/administration-guide/manage/admin/postgres-migration) - Move your database, using the migration assist tool or by hand.
|
||||
- [Migrate to FIPS-compliant images](/administration-guide/manage/admin/fips-migration) - Switch an existing Docker or Kubernetes deployment to the FIPS-compliant Mattermost images.
|
||||
- [Migrate from another platform](/administration-guide/onboard/migrating-to-mattermost) - Move from Slack, Rocket.Chat, or GitLab Omnibus, with the [migration announcement email template](/administration-guide/onboard/migration-announcement-email) for telling your users.
|
||||
|
||||
Import and export share the same JSONL format, so the [bulk import data](/administration-guide/onboard/bulk-loading-data) page is also the format reference for export files.
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
title: "Monitoring and performance"
|
||||
---
|
||||
Keeping a running deployment healthy: what to measure, where to send it, and what to collect when something goes wrong.
|
||||
|
||||
## Metrics and dashboards
|
||||
|
||||
- [Collect performance metrics](/administration-guide/scale/collect-performance-metrics) - Turn on the metrics endpoint and decide what to scrape.
|
||||
- [Deploy Prometheus and Grafana for performance monitoring](/administration-guide/scale/deploy-prometheus-grafana-for-performance-monitoring) - Stand up the monitoring stack.
|
||||
- [Performance monitoring metrics](/administration-guide/scale/performance-monitoring-metrics) - What each metric means.
|
||||
- [Performance alerting](/administration-guide/scale/performance-alerting) - Alert thresholds worth setting, and why.
|
||||
- [Push notification health targets](/administration-guide/scale/push-notification-health-targets) - Healthy delivery latency and failure rates.
|
||||
|
||||
## Logging
|
||||
|
||||
- [Mattermost logging](/administration-guide/manage/logging) - Configure what the server logs and where.
|
||||
- [Deploy Grafana Loki for centralized logging](/administration-guide/scale/deploy-grafana-loki-for-centralized-logging) - Aggregate logs across servers.
|
||||
|
||||
## Health and diagnostics
|
||||
|
||||
- [Optimize your Mattermost workspace](/administration-guide/configure/optimize-your-workspace) - Run the System Console health check and act on what it finds.
|
||||
- [Statistics](/administration-guide/manage/statistics) - Site and team usage statistics.
|
||||
- [Configure server health check probes](/administration-guide/manage/configure-health-check-probes) - Liveness and readiness endpoints for your orchestrator.
|
||||
- [Request a server health check](/administration-guide/manage/request-server-health-check) - Have Mattermost review your deployment.
|
||||
- [Generate a support packet](/administration-guide/manage/admin/generating-support-packet) - Collect logs and configuration for a support ticket.
|
||||
- [Mattermost error codes](/administration-guide/manage/admin/error-codes) - Look up an error a user reported.
|
||||
|
||||
## Telemetry
|
||||
|
||||
- [Telemetry](/administration-guide/manage/telemetry) - What Mattermost collects, and how to turn it off.
|
||||
- [Ensure releases perform at scale](/administration-guide/scale/ensuring-releases-perform-at-scale) - How Mattermost load-tests each release.
|
||||
@@ -0,0 +1,206 @@
|
||||
---
|
||||
title: "Automated PostgreSQL migration"
|
||||
---
|
||||
<PlanAvailability slug="all-commercial" />
|
||||
|
||||
Migrating databases can be a daunting task, and it can be easy to overlook or misinterpret some of the required steps if you haven't performed a migration before. Our `migration-assist` tool provides an efficient, error-free migration experience that automates the [tasks to be executed](/administration-guide/manage/admin/manual-postgres-migration), even in air-gapped deployment environments.
|
||||
|
||||
Not sure this tool is right for your Mattermost deployment? Mattermost customers looking for tailored guidance based on their Mattermost deployment can contact a [Mattermost Expert](https://mattermost.com/contact-sales/).
|
||||
|
||||
## Install
|
||||
|
||||
Download the Mattermost `migration-assist` tool from the GitHub repository [releases page](https://github.com/mattermost/migration-assist/releases).
|
||||
|
||||
While you can run the `migration-assist` tool on the same server as your Mattermost deployment, we recommend running the tool in a virtual machine on the same network as your Mattermost server instead. The tool itself is lightweight and does not require a large server. A server with 2 CPU cores and 16 GB of RAM should be sufficient. If preferred, you can download and [compile](#compile-the-migration-assist-tool) the `migration-assist` tool yourself.
|
||||
|
||||
You'll also need to install the `pgloader` tool to migrate your data from MySQL to PostgreSQL. We recommend running `pgloader` in a virtual machine on the same network as your Mattermost server. You can use our official Mattermost Docker image for pgloader (`mattermost/pgloader:latest`); please note that it does **not** currently support MySQL’s `caching_sha2_password` authentication plugin. If you require `caching_sha2_password` support, you’ll need to build your own image and include the [qitab/qmynd](https://github.com/qitab/qmynd) library. See the [pgloader](/administration-guide/manage/admin/manual-postgres-migration#install-pgloader) installation documentation for details.
|
||||
|
||||
## Usage
|
||||
|
||||
<Important>
|
||||
|
||||
- If you encounter heap exhaustion errors in `pgloader`, edit your generated `migration.load` and under the `WITH` block set: `prefetch rows = 1000` and consider reducing it if the issue persists.
|
||||
- Please make sure you have the necessary environment to perform the migration. Ensure that the MySQL and PostgreSQL databases are running and accessible. To set up a PostgreSQL instance, see the [prepare your Mattermost database](/deployment-guide/server/prepare-database) documentation for details.
|
||||
- If you were previously utilizing a database for handling the [Mattermost configuration](/administration-guide/configure/configuration-in-your-database), those tables will not be migrated from your MySQL database with the migration [script](#migrate-the-data). You will need to manually migrate those configuration settings to your PostgreSQL database after completing the migration process. See the [configuration in database](/administration-guide/manage/admin/manual-postgres-migration#configuration-in-database) documentation for details.
|
||||
|
||||
</Important>
|
||||
|
||||
### Step 1 - Check the MySQL database schema
|
||||
|
||||
Run the following command to check the MySQL database schema:
|
||||
|
||||
``` sh
|
||||
migration-assist mysql "<MYSQL_DSN>" # example DSN: "user:password@tcp(address:3306)/db_name"
|
||||
```
|
||||
|
||||
This command outputs the readiness status and prints required fixes for common issues. The flags for fixes are as follows (you can combine all of them):
|
||||
|
||||
``` text
|
||||
--fix-artifacts Removes the artifacts from older versions of Mattermost
|
||||
--fix-unicode Strips unsupported Unicode characters from MySQL tables
|
||||
--fix-varchar Removes rows exceeding column lengths
|
||||
```
|
||||
|
||||
### Step 2 - Create the PostgreSQL database schema
|
||||
|
||||
Before running migrations, ensure the `public` schema is owned by your migration user:
|
||||
|
||||
``` sql
|
||||
sudo -u postgres psql -d mattermost -c "ALTER SCHEMA public OWNER TO mmuser; GRANT ALL ON SCHEMA public TO mmuser;"
|
||||
```
|
||||
|
||||
Then run:
|
||||
|
||||
``` sh
|
||||
migration-assist postgres "<POSTGRES_DSN>" \
|
||||
--run-migrations \
|
||||
--mattermost-version="<MATTERMOST_VERSION>"
|
||||
```
|
||||
|
||||
- `<POSTGRES_DSN>` example: `postgres://user:password@address:5432/db_name`
|
||||
- `<MATTERMOST_VERSION>` example: `10.5.4`
|
||||
|
||||
By default, two pre-checks run before migration:
|
||||
|
||||
- `--check-schema-owner=true`
|
||||
- `--check-tables-empty=true`
|
||||
|
||||
To disable them:
|
||||
|
||||
``` sh
|
||||
--check-schema-owner=false \
|
||||
--check-tables-empty=false
|
||||
```
|
||||
|
||||
### Step 3 - Generate a pgloader configuration
|
||||
|
||||
Run the following command to emit a pgloader configuration file:
|
||||
|
||||
``` sh
|
||||
migration-assist pgloader \
|
||||
--mysql="<MYSQL_DSN>" \
|
||||
--postgres="<POSTGRES_DSN>" \
|
||||
--remove-null-chars \
|
||||
> migration.load
|
||||
```
|
||||
|
||||
- By default, null characters in text columns are stripped. Disable with `--remove-null-chars=false`.
|
||||
|
||||
- If you run out of heap, edit `migration.load` and under the `WITH` block set:
|
||||
|
||||
``` text
|
||||
prefetch rows = 1000
|
||||
```
|
||||
|
||||
### Step 4 - Run pgloader
|
||||
|
||||
[Run pgloader](/administration-guide/manage/admin/manual-postgres-migration#pgloader) with the generated configuration file:
|
||||
|
||||
``` sh
|
||||
pgloader migration.load > migration.log
|
||||
```
|
||||
|
||||
Carefully review <code>migration.log</code> for errors (e.g., duplicate-key or missing-table warnings). Use the `mattermost/pgloader:latest` Docker image to avoid build/auth issues.
|
||||
|
||||
### Step 5 - Restore full-text indexes & create all indexes
|
||||
|
||||
Run:
|
||||
|
||||
``` sh
|
||||
migration-assist postgres post-migrate --create-indexes "<POSTGRES_DSN>"
|
||||
```
|
||||
|
||||
- The `--create-indexes` flag rebuilds full-text indexes on `Posts` and `FileInfo`, plus all other Mattermost indexes.
|
||||
- Omitting that flag only restores full-text indexes for `Posts` and `FileInfo`.
|
||||
|
||||
See the [Restore full-text indexes](/administration-guide/manage/admin/manual-postgres-migration#restore-full-text-indexes) documentation for details.
|
||||
|
||||
### Step 6 - Complete plugin migrations
|
||||
|
||||
Generate pgloader configs for Playbooks, Boards, and Calls:
|
||||
|
||||
``` sh
|
||||
migration-assist pgloader boards \
|
||||
--mysql="<MYSQL_DSN>" \
|
||||
--postgres="<POSTGRES_DSN>" > boards.load
|
||||
migration-assist pgloader playbooks \
|
||||
--mysql="<MYSQL_DSN>" \
|
||||
--postgres="<POSTGRES_DSN>" > playbooks.load
|
||||
migration-assist pgloader calls \
|
||||
--mysql="<MYSQL_DSN>" \
|
||||
--postgres="<POSTGRES_DSN>" > calls.load
|
||||
```
|
||||
|
||||
Run each:
|
||||
|
||||
``` sh
|
||||
pgloader boards.load > boards_migration.log
|
||||
pgloader playbooks.load > playbooks_migration.log
|
||||
pgloader calls.load > calls_migration.log
|
||||
```
|
||||
|
||||
Skip any plugin you don't use; check logs for JSON or missing-table errors. See the [Plugin migrations](/administration-guide/manage/admin/manual-postgres-migration#plugin-migrations) guide for more.
|
||||
|
||||
### Step 7 - Configure Mattermost to use PostgreSQL
|
||||
|
||||
In your `config.json` or via environment variables, update:
|
||||
|
||||
``` json
|
||||
"SqlSettings": {
|
||||
"DriverName": "postgres",
|
||||
"DataSource": "postgres://mmuser:pass@db:5432/mattermost?sslmode=disable"
|
||||
}
|
||||
```
|
||||
|
||||
If your config was stored in the database, update `MM_CONFIG` accordingly. See the [environment configuration settings](/administration-guide/configure/environment-configuration-settings#database) documentation for details.
|
||||
|
||||
<Note>
|
||||
|
||||
If your Mattermost deployment was initially configured with MySQL, there's a good chance your systemd service file has a `BindsTo=mysql.service` directive in it. This will cause the Mattermost server to be shut down if you deactivate your MySQL service. To fix this, update all references to `mysql.service` in your service file to use `postgresql.service` instead. This is only an issue if your Database and Mattermost are running on the same system.
|
||||
|
||||
</Note>
|
||||
|
||||
## Air-gapped environments
|
||||
|
||||
Follow these steps to migrate within an air-gapped environment:
|
||||
|
||||
1. **Verify** that the <code>migration-assist</code> binary is the latest version available to benefit from improvements and fixes.
|
||||
|
||||
2. **Transfer** the latest <code>migration-assist</code> binary into the air-gapped environment (e.g., via secure media).
|
||||
|
||||
3. **Generate** the MySQL schema+data output using fix flags. This produces `mysql.output`:
|
||||
|
||||
``` sh
|
||||
migration-assist mysql "user:pass@tcp(localhost:3306)/mattermost" \
|
||||
--fix-artifacts --fix-unicode --fix-varchar > mysql.output
|
||||
```
|
||||
|
||||
4. **Apply** migrations using the generated output:
|
||||
|
||||
``` sh
|
||||
migration-assist postgres \
|
||||
"postgres://mmuser:pass@localhost:5432/imported?sslmode=disable" \
|
||||
--run-migrations --applied-migrations="./mysql.output"
|
||||
```
|
||||
|
||||
5. **Continue** from **Step 3** through **Step 7** above to complete data transfer and configuration.
|
||||
|
||||
## Tool commands
|
||||
|
||||
The `migration-assist` tool offers 3 core commands:
|
||||
|
||||
1. `migration-assist mysql` — Checks MySQL schema readiness and offers fixes.
|
||||
2. `migration-assist postgres` — Builds the PostgreSQL schema and applies migrations.
|
||||
3. `migration-assist pgloader` — Generates a pgloader config for data transfer.
|
||||
|
||||
## Compile the migration-assist tool
|
||||
|
||||
Requires Go ≥ v1.22. Install with:
|
||||
|
||||
``` shell
|
||||
go install github.com/mattermost/migration-assist/cmd/migration-assist@latest
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
See [troubleshooting errors during migration from MySQL to PostgreSQL](/administration-guide/manage/admin/postgres-migration#troubleshooting).
|
||||
@@ -0,0 +1,234 @@
|
||||
---
|
||||
title: "Migration guidelines from MySQL to PostgreSQL"
|
||||
---
|
||||
<PlanAvailability slug="all-commercial" />
|
||||
|
||||
From Mattermost v8.0, [PostgreSQL](/deployment-guide/software-hardware-requirements#database-software) is our database of choice for Mattermost to enhance the platform’s performance and capabilities. Recognizing the importance of supporting the community members who are interested in migrating from a MySQL database, we have taken proactive measures to provide guidance and best practices.
|
||||
|
||||
- [Automated migration from MySQL to PostgreSQL](/administration-guide/manage/admin/postgres-migration-assist-tool) - A comprehensive set of guidelines and a `migration-assist` tool to streamline the migration process, alleviate potential challenges, and faciliate a smooth transition.
|
||||
- [Manually migrate from MySQL to PostgreSQL](/administration-guide/manage/admin/manual-postgres-migration) - A good option if your organization has database administrators to own the migration process, or if you want to learn what the `migration-assist` tool automates for you.
|
||||
|
||||
## Frequently asked questions
|
||||
|
||||
### Why is Mattermost dropping support for MySQL?
|
||||
|
||||
Mattermost has decided to drop support for MySQL databases to streamline development and focus on delivering more efficient and optimized features. By supporting only PostgreSQL, which is generally considered more advanced and better-suited for enterprise environments, Mattermost can reduce complexity, improve performance, and better allocate resources to enhance the product. This change helps ensure more consistent, robust, and scalable database interactions for all Mattermost deployments.
|
||||
|
||||
### Is migrating to PostgreSQL before upgrading Mattermost recommended?
|
||||
|
||||
Yes. We also recommend upgrading to Mattermost server v8.x or later before migrating to PostgreSQL.
|
||||
|
||||
### Can the migration-assist be run on the Mattermost server?
|
||||
|
||||
Technically, yes. The `migration-assist` tool can be run on the Mattermost server. However, it is recommended to run the tool on a separate server to avoid any performance issues. We advise running the migration against a copy of the MySQL database to ensure that the migration process does not impact the production environment.
|
||||
|
||||
### How large should the PostgreSQL server be?
|
||||
|
||||
The size of the PostgreSQL server should match that of the MySQL server initially. We recommend monitoring the performance of the PostgreSQL server and adjusting the resources as needed.
|
||||
|
||||
### How large should the server running `migration-assist` server be?
|
||||
|
||||
The tool itself is lightweight and does not require a large server. A server with 2 CPU cores and 16 GB of RAM should be sufficient for default configurations. However, you may need to adjust the resources based on the size of the MySQL database, your downtime limitations, and the configuration of `pgloader`.
|
||||
|
||||
### Do we/will Mattermost bundle pgloader or is that a separate install?
|
||||
|
||||
Mattermost doesn't bundle pgloader with the Mattermost server. You will need to install pgloader separately. For more information, see the [install pgloader](/administration-guide/manage/admin/manual-postgres-migration#install-pgloader) documentation.
|
||||
|
||||
### Are there any other migrations available for plugins, or just Boards, Playbooks, and Calls?
|
||||
|
||||
We're developing migrations for other plugins, such as NPS-plugin. Please stay tuned for updates.
|
||||
|
||||
### Do these processes support AWS RDS databases?
|
||||
|
||||
Yes, the processes support AWS RDS databases. However, you may need to adjust the security group settings to allow the migration process to access the databases.
|
||||
|
||||
### Does Mattermost support MariaDB? If not, why not?
|
||||
|
||||
Mattermost does not support MariaDB. The primary reason for this decision is to streamline development and maintenance by focusing on a single database technology. By standardizing on PostgreSQL, Mattermost can deliver optimized features, improved performance, and a more focused engineering effort. PostgreSQL is well-regarded for its robustness, advanced features, and suitability for enterprise use, making it the chosen database for Mattermost.
|
||||
|
||||
MariaDB, while compatible with MySQL to a large extent, introduces additional complexity and potential inconsistency, which the Mattermost development team aims to avoid by limiting database support.
|
||||
|
||||
Because of these reasons we also don't support migrations directly from MariaDB to PostgreSQL. Oracle provides documentation on \[how to migrate from MariaDB to MySQL\]([https://blogs.oracle.com/mysql/post/how-to-migrate-from-mariadb-to-mysql-80](https://blogs.oracle.com/mysql/post/how-to-migrate-from-mariadb-to-mysql-80)) which can be used as a starting point for the migration to PostgreSQL.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Unsupported authentication for MySQL
|
||||
|
||||
If you are facing an error due to authentication with MySQL v8, it may be related to a [known issue](https://github.com/dimitri/pgloader/issues/782) with pgloader. The fix is to set the default authentication method to `mysql_native_password` in your MySQL configuration. To do so, add the `default-authentication-plugin=mysql_native_password` value to your `mysql.cnf` file. Also, do not forget to update your user to use this authentication method.
|
||||
|
||||
``` sql
|
||||
ALTER USER '<mysql_user>'@'%' IDENTIFIED WITH mysql_native_password BY '<mysql_password>';
|
||||
```
|
||||
|
||||
### Errors during the pgloader command execution
|
||||
|
||||
If you encounter errors during the execution of the `pgloader` command, ensure that both of the databases are accessible and that the users have the necessary permissions to access the database. Do not continue with the migration if there are errors during the execution of the `pgloader` command.
|
||||
|
||||
<Note>
|
||||
|
||||
For experienced users, it is recoverable to run the `pgloader` without requiring a restart of the migration from scratch. In this case, you will need to manually fix the issues with the table, and run the `pgloader` command with a tailored configuration specifically for those tables. Also ensure that the schema name is reverted back to `public`, and the `search_path` is restored (or remove necessary clauses from the configuration).
|
||||
|
||||
</Note>
|
||||
|
||||
The following sections detail how to resolve some common errors you may encounter during the execution of the `pgloader` command:
|
||||
|
||||
#### Invalid input syntax for type JSON
|
||||
|
||||
If you receive an error message similar to the following:
|
||||
|
||||
``` text
|
||||
ERROR Database error 22P02: invalid input syntax for type json
|
||||
```
|
||||
|
||||
The data in the MySQL database is not in a valid JSON format. You can fix this issue by updating the data in the MySQL database to be in a valid JSON format. To find out which row is causing the issue, run the following query (where `<table_name>` and `<column_name>` should be replaced with the actual table and column names indicated in the `pgloader` output):
|
||||
|
||||
``` sql
|
||||
SELECT * FROM <table_name> WHERE JSON_VALID(<column_name>) = 0;
|
||||
```
|
||||
|
||||
You can find and update the data in the MySQL database to be in a valid JSON format with the query above. After updating the data, you can run the `pgloader` command again.
|
||||
|
||||
#### Failed to find column or table
|
||||
|
||||
If you receive an error message similar to the following:
|
||||
|
||||
``` text
|
||||
pgloader failed to find column
|
||||
```
|
||||
|
||||
The column or table is missing in the PostgreSQL database. You can fix this issue by checking whether you have created the correct version of Postgres schema. After re-creating the schema, you can run the `pgloader` command again.
|
||||
|
||||
#### Fell through ECASE expression
|
||||
|
||||
If you receive an error message similar to the following:
|
||||
|
||||
``` text
|
||||
ERROR mysql: 76 fell through ECASE expression.
|
||||
```
|
||||
|
||||
It is a [known issue](https://github.com/dimitri/pgloader/issues/1183) with pgloader. You can fix this issue by either compiling `pgloader` from source or simply avoid this by running `pgloader` with our docker image. See: [install pgloader](/administration-guide/manage/admin/manual-postgres-migration#install-pgloader) for more information.
|
||||
|
||||
<Note>
|
||||
|
||||
Also, there may be cases where pgloader continues to migrate remaining tables and skip one or more tables during migration. In such cases, we recommend identifying issues with the table and fixing them before running the `pgloader` command again with a clean database. It is possible to run the `pgloader` command with the `--debug` flag to get more information about the errors.
|
||||
|
||||
</Note>
|
||||
|
||||
### Mattermost can't connect to the PostgreSQL database
|
||||
|
||||
If you are facing an issue where Mattermost can't connect to the PostgreSQL database, ensure that the PostgreSQL server is running and that the database is accessible. If there were errors during the execution of the `pgloader` command, it can fail to revert the schema name back to `public`, or potentially fail to restore the `search_path`. You can manually revert the schema name back to `public` and restore the `search_path` by running the following commands:
|
||||
|
||||
1. Connect to PostgreSQL using `sudo -u postgres psql`.
|
||||
2. Select the `mattermost` database using `\c mattermost`. Verify you are using the right database by running `SELECT current_database();`. The command should output `mattermost`.
|
||||
3. Revert the schema name change (optional)
|
||||
|
||||
``` sql
|
||||
ALTER SCHEMA <schema_name> RENAME TO public;
|
||||
```
|
||||
|
||||
Also ensure that the database user has the necessary settings to have default access to the `public` schema. You can do this by running the following commands:
|
||||
|
||||
4. Set the search_path for the mmuser:
|
||||
|
||||
``` sql
|
||||
ALTER USER mmuser SET search_path TO "$user", public;
|
||||
```
|
||||
|
||||
5. Terminate the connection and connect again to your psql server.
|
||||
|
||||
6. Verify the search_path is set correctly:
|
||||
|
||||
``` sh
|
||||
SHOW search_path;
|
||||
```
|
||||
|
||||
This should return `"$user", public`.
|
||||
|
||||
7. If the issue persists, also run:
|
||||
|
||||
``` sql
|
||||
SELECT pg_catalog.set_config('search_path', '"$user", public', false);
|
||||
```
|
||||
|
||||
### Permission issues when accessing the schema in PostgreSQL
|
||||
|
||||
If you run into a permission issue during step 2 where the command reports a permission issue similar to the following:
|
||||
|
||||
``` text
|
||||
An Error Occurred: could not check schema owner: the user “mmuser” is not owner of the “public” schema
|
||||
```
|
||||
|
||||
Ensure that the `mmuser` user in PostgreSQL is the owner of the schema.
|
||||
|
||||
1. Connect to PostgreSQL using `sudo -u postgres psql`.
|
||||
|
||||
2. Select the `mattermost` database using `\c mattermost`. Verify you are using the right database by running `SELECT current_database();`. The command should output `mattermost`.
|
||||
|
||||
3. Run the following commands:
|
||||
|
||||
``` sql
|
||||
ALTER SCHEMA public OWNER TO mmuser;
|
||||
GRANT USAGE, CREATE ON SCHEMA public TO mmuser;
|
||||
```
|
||||
|
||||
Then, re-run the command from step 2.
|
||||
|
||||
### Incoming webhook channel names become case-sensitive after migration
|
||||
|
||||
After migrating from MySQL to PostgreSQL, incoming webhooks that previously worked with mixed-case channel names (e.g., `"Tech-Talk"`) will fail with the following error:
|
||||
|
||||
``` text
|
||||
GetChannelByName: Channel does not exist.
|
||||
```
|
||||
|
||||
This occurs because Mattermost doesn't allow the creation of channels with uppercase characters. All new channels names must only contain lowercase characters. Additionally, MySQL is case-insensitive when matching channel names, while PostgreSQL is case-sensitive. This means that webhook payloads that worked with MySQL's forgiving case-insensitive matching will fail with PostgreSQL's strict case-sensitive matching. In rare cases, older channels may still exist with mixed-case names in your database. These channels are considered invalid and may cause issues when performing operations like updating channel headers.
|
||||
|
||||
**Solution:**
|
||||
|
||||
Update all webhook payloads to use only lowercase channel names.
|
||||
|
||||
**Prevention:**
|
||||
|
||||
Before migrating to PostgreSQL:
|
||||
|
||||
1. Audit your incoming webhooks and update any that use mixed-case channel names in their payloads.
|
||||
2. Check for any legacy channels with mixed-case names that may have been created in earlier versions.
|
||||
3. Consider renaming any mixed-case channels to lowercase to prevent issues.
|
||||
|
||||
This is expected behavior with PostgreSQL and reflects Mattermost's channel naming requirements.
|
||||
|
||||
### Legacy Boards tables causing migration errors
|
||||
|
||||
During database migration from MySQL to PostgreSQL, while using schema comparison tools (such as dbcmp) you may encounter the following error:
|
||||
|
||||
``` text
|
||||
error during comparison: no pk defined for table: focalboard_file_info
|
||||
```
|
||||
|
||||
This typically relates to legacy tables from older versions of Boards that are no longer in use. If you're using Boards version 8.0.0 or later, these tables are no longer required and can be ignored in the migration process.
|
||||
|
||||
**Applies To:**
|
||||
|
||||
- Boards version 8.0.0 and above
|
||||
- All staging, UAT, QA, or production environments
|
||||
|
||||
<Note>
|
||||
|
||||
Does not apply to standalone Boards versions prior to v8.0.0
|
||||
|
||||
</Note>
|
||||
|
||||
**Solution:**
|
||||
|
||||
If you're using Boards version 8.0.0 or higher, the following legacy tables which were used only in the standalone version and can be safely ignored:
|
||||
|
||||
- `focalboard_file_info` - Used in standalone version; no longer relevant in plugin and later versions. Stores uploaded files and attachments for boards.
|
||||
- `focalboard_sessions` - Used in standalone version. Tracks user login sessions for boards.
|
||||
- `focalboard_teams` - Used in standalone version. Legacy team mapping not used in later versions. Stores team board settings and permissions.
|
||||
- `focalboard_users` - Used in standalone version. Stores user preferences and settings for boards.
|
||||
|
||||
These tables may be empty or unused in your current environment. If so, they do not need to be migrated to PostgreSQL.
|
||||
|
||||
## Contact Support
|
||||
|
||||
Mattermost customers looking for guidance tailored to their Mattermost deployment can contact a [Mattermost Expert](https://mattermost.com/contact-sales/) for guidance.
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: "Self-hosted billing"
|
||||
---
|
||||
<PlanAvailability slug="all-commercial" />
|
||||
|
||||
Mattermost commercial plans are offered as an annual subscription service. With your purchase you're provided with a license key. Depending on which version of Mattermost you're on, you may have to download and apply this license manually via the System Console.
|
||||
|
||||
When you upgrade to commercial plan, the number of user seats you need has to be equal to or greater than your current number of activated users (formerly called active users).
|
||||
|
||||
You can add new users to your deployment at any time. Every quarter, during the true-up review (section 3.5 License True-up Review of Software License Agreement, also referenced in docs), we check the license license seat count relative to number of activated users. If there's a difference, we'll provide you with a new invoice which is payable immediately.
|
||||
|
||||
Please see the [self-hosted subscriptions documentation](/product-overview/self-hosted-subscriptions#buy-a-subscription) for more detailed information.
|
||||
|
||||
[Book a live demo](https://mattermost.com/request-demo/) or [talk to a Mattermost expert](https://mattermost.com/contact-sales/) to explore tailored solutions for your organization's secure collaboration needs. Or try Mattermost yourself with a [1-hour preview](https://mattermost.com/sign-up/) for instant access to a live sandbox environment.
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
title: "Manage Mattermost"
|
||||
---
|
||||
Running Mattermost day to day: who has access to what, the tools you operate the server with, your license and billing, the notices your users see, and getting your data out.
|
||||
|
||||
## Access control
|
||||
|
||||
- [Access control](/administration-guide/manage/admin/user-management) - Permissions, roles, user attributes, team and channel membership, and attribute-based access control.
|
||||
|
||||
## Server operations
|
||||
|
||||
- [mmctl command line tool](/administration-guide/manage/mmctl-command-line-tool) - Administer the server from the command line.
|
||||
- [Command line tools](/administration-guide/manage/command-line-tools) - The `mattermost` binary's own commands.
|
||||
- [Mattermost logging](/administration-guide/manage/logging) - Customize logging options based on your business practices and needs.
|
||||
|
||||
## Licensing and billing
|
||||
|
||||
- [Self-hosted billing](/administration-guide/manage/admin/self-hosted-billing) - Billing and payment options for self-hosted deployments.
|
||||
- [Install a license key](/administration-guide/manage/admin/installing-license-key) - Apply a license to a self-hosted deployment.
|
||||
|
||||
## Cloud, notices, and data
|
||||
|
||||
- [Cloud workspace management](/administration-guide/manage/cloud-workspace-management) - Data residency, IP filtering, encryption keys, and workspace export.
|
||||
- [Data import, export, and migration](/administration-guide/manage/admin/migration) - Bulk import and export, database migration, and FIPS-compliant images.
|
||||
- [Product limits](/administration-guide/manage/product-limits) and [feature labels](/administration-guide/manage/feature-labels) - What each plan includes, and what a feature label means.
|
||||
|
||||
## Related
|
||||
|
||||
- [Backup and disaster recovery](/deployment-guide/backup-disaster-recovery) - Back up and restore a Mattermost deployment.
|
||||
- [Upgrade Mattermost Server](/administration-guide/upgrade/upgrade-index) - Upgrade, downgrade, and post-upgrade rollout.
|
||||
- [Secure Mattermost](/security-guide/secure-mattermost) - Harden your deployment.
|
||||
- [Monitor and troubleshoot](/administration-guide/manage/admin/monitoring-and-performance) - Metrics, health checks, support packets, and error codes.
|
||||
180
docs/main/administration-guide/manage/admin/user-attributes.mdx
Normal file
180
docs/main/administration-guide/manage/admin/user-attributes.mdx
Normal file
@@ -0,0 +1,180 @@
|
||||
---
|
||||
title: "User attributes"
|
||||
---
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
<PlanAvailability slug="ent-plus" />
|
||||
|
||||
From Mattermost v10.8, ensure your teams always have the critical information they need to collaborate effectively by defining and managing organization-specific user profile attributes as system attributes that you can synchronize with your AD/LDAP or SAML identity provider.
|
||||
|
||||
System attributes enable you to customize user profile attributes to match your organization's unique needs and streamline collaboration while keeping user data centralized and consistent.
|
||||
|
||||
From Mattermost v11, you have enhanced control over these user attributes through admin-managed vs user-editable settings. By default, attributes are admin-managed for security, but you can explicitly allow user editing for specific attributes that don't impact access control or sensitive organizational data. These user attributes supplement existing user details visible from the user's profile picture.
|
||||
|
||||

|
||||
|
||||
## Before you begin
|
||||
|
||||
If you plan to synchronize system properties with your AD/LDAP or SAML identity provider, ensure AD/LDAP or SAML synchronization is already enabled and configured. See the [AD/LDAP groups](/administration-guide/onboard/ad-ldap-groups-synchronization) product documentation or [SAML 2.0](/administration-guide/configure/authentication-configuration-settings#saml-20) configuration settings documentation for details.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="mattermost-v11-or-later" label="Mattermost v11 or later">
|
||||
|
||||
From Mattermost v11, user attributes have enhanced admin controls with a security-first approach. All attributes are admin-managed by default, providing better security and governance. System administrators can explicitly enable user editing for specific attributes through the System Console interface.
|
||||
|
||||
If your organization uses [attribute-based access control (ABAC)](/administration-guide/manage/admin/attribute-based-access-control), which requires an Enterprise Edition Advanced license, attributes used in access control policies should remain admin-managed for security reasons. You can enable user-managed attributes for ABAC by setting `EnableUserManagedAttributes` in `config.json`, or via an environment variable as noted below, but this should be done with caution:
|
||||
|
||||
- `config.json` setting: `".AccessControlSettings.EnableUserManagedAttributes: false",` with options `true` and `false`.
|
||||
- Environment variable: `MM_ACCESSCONTROLSETTINGS_ENABLEUSERMANAGEDATTRIBUTES`
|
||||
|
||||
This configuration setting isn't managed using the System Console, and is disabled by default to ensure that access control policies remain secure and can't be circumvented by users modifying their own profile attributes. Learn more about [admin-managed versus user-editable attributes](#admin-managed-vs-user-editable-attributes).
|
||||
|
||||
Additionally, from mobile app v2.31.0, user attributes are fully supported on mobile devices. Mobile users can both view and manage profile attributes (if allowed by admin settings) directly from the mobile app. Mobile attribute support requires the same server-side configuration, and no additional mobile-specific setup is required.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mattermost-v10-11-or-later" label="Mattermost v10.11 or later">
|
||||
|
||||
From Mattermost v10.11, user-managed attributes are excluded from ABAC rules for security reasons. If your organization uses ABAC (which requires Enterprise Edition Advanced), a system admin can explicitly enable user-managed attributes for ABAC by setting `EnableUserManagedAttributes` in `config.json` or via an environment variable as noted below. This configuration setting isn't managed using the System Console, and is disabled by default to ensure that access control policies remain secure and can't be circumvented by users modifying their own profile attributes.
|
||||
|
||||
- `config.json` setting: `".AccessControlSettings.EnableUserManagedAttributes: false",` with options `true` and `false`.
|
||||
- Environment variable: `MM_ACCESSCONTROLSETTINGS_ENABLEUSERMANAGEDATTRIBUTES`
|
||||
|
||||
Additionally, from mobile app v2.31.0, user attributes are fully supported on mobile devices. Mobile users can both view and manage profile attributes directly from the mobile app. Mobile attribute support requires the same server-side configuration, and no additional mobile-specific setup is required.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mattermost-v10-9-or-later" label="Mattermost v10.9 or later">
|
||||
|
||||
From Mattermost Enterprise v10.9, the ability to create and manage system properties as a Mattermost system admin requires no manual setup.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mattermost-v10-8" label="Mattermost v10.8">
|
||||
|
||||
You'll need Mattermost Enterprise v10.8 or later, and be a Mattermost system admin to enable the system properties feature flag, `MM_FEATUREFLAGS_CUSTOMPROFILEATTRIBUTES` to create and manage system properties. See the Mattermost developer documentation for details on [enabling feature flags in a self-hosted deployment](https://developers.mattermost.com/contribute/more-info/server/feature-flags/#self-hosted-and-local-development). Mattermost Cloud customers can request this feature flag be enabled by contacting their Mattermost Account Manager or by [creating a support ticket](https://support.mattermost.com/hc/en-us/requests/new?ticket_form_id=11184911962004) request.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## Admin-managed vs user-editable attributes
|
||||
|
||||
From Mattermost v11, system administrators have enhanced control over user attributes through admin-managed vs user-editable settings. This distinction provides better security and governance for organizational profile data.
|
||||
|
||||
**Admin-managed attributes** (default behavior):
|
||||
|
||||
- Controlled exclusively by system administrators
|
||||
- Users cannot edit these values in their profiles
|
||||
- Recommended for security-sensitive information like roles, clearance levels, or organizational identifiers
|
||||
- Ideal for attributes used in ABAC (Attribute-Based Access Control) policies
|
||||
- Values can only be updated by admins or through identity provider synchronization
|
||||
|
||||
**User-editable attributes**:
|
||||
|
||||
- Users can update these values in their own profiles
|
||||
- System administrators can still modify these attributes
|
||||
- Appropriate for non-sensitive information like personal preferences, skills, or contact details
|
||||
- Should not be used for attributes that impact security access controls
|
||||
|
||||
<Note>
|
||||
|
||||
For security reasons, attributes used in ABAC policies are admin-managed by default and cannot be edited by users unless explicitly enabled by a system administrator through the **Allow user editing** option.
|
||||
|
||||
</Note>
|
||||
|
||||
**Best practices:**
|
||||
|
||||
- Keep attributes that affect access control or contain sensitive organizational data as admin-managed
|
||||
- Only enable user editing for attributes that don't impact security policies
|
||||
- Regularly review user-editable attributes to ensure they remain appropriate for user management
|
||||
- Consider your organization's data governance requirements when determining attribute editability
|
||||
|
||||
## Add attributes
|
||||
|
||||
You can define and manage up to 20 system attributes using the System Console. Each attribute becomes a user profile option users can populate, unless you disable the **Editable by Users** option, available from Mattermost v11. Once you reach the maximum of 20 attributes, you can't create new attributes until you [delete attributes](#manage-attributes) you no longer need.
|
||||
|
||||
From Mattermost v11.8, user attributes include both a **Display Name** and a **Name**. The Display Name is the label shown to users and admins in Mattermost. The Name is the internal attribute identifier used in API references and [attribute-based access control (ABAC)](/administration-guide/manage/admin/attribute-based-access-control) policy expressions. When you create a new user attribute, Mattermost can generate the Name from the Display Name, formatted as a CEL-safe identifier. Saved ABAC policies continue to reference the internal name using `user.attributes.<name>`.
|
||||
|
||||
<Note>
|
||||
|
||||
When you disable the **Editable by Users** option for an attribute, only admins can set its value using [mmctl cpa](/administration-guide/manage/mmctl-command-line-tool#mmctl-cpa) commands.
|
||||
|
||||
</Note>
|
||||
|
||||
1. In the System Console, go to **Site Configuration \> System Attributes \> User Attributes** and select **Add Attribute**.
|
||||
|
||||
2. Enter the following details:
|
||||
|
||||
> - **Display Name**: Enter a display name for the attribute that will be shown in any user facing UI.
|
||||
> - **Attribute name**: Attribute name will be automatically generated from the provided Display Name. It can be overriden and with a preferred unique name for the attribute. Attribute names can be up to 40 characters long. The attribute name is used as the Common Expression Language (CEL) identifier in access control policies. It must start with a letter or underscore, and can contain only letters, numbers, and underscores. Reserved CEL words aren't allowed, including `true`, `false`, `null`, `in`, `as`, `break`, `const`, `continue`, `else`, `for`, `function`, `if`, `import`, `let`, `loop`, `package`, `namespace`, `return`, `var`, `void`, and `while`.
|
||||
> - **Type**: Specify the type of attribute as one of the following:
|
||||
> - **Text** for text-based profile attributes.
|
||||
> - **Phone** for phone number-based profile attributes.
|
||||
> - **URL** for web site address-based profile attributes.
|
||||
> - **Select** for a list of profile attribute values users can choose from. Specify each value followed by pressing **TAB** or **ENTER**. Values can be up to 64 characters long, and users can choose a single value.
|
||||
> - **Multi-select** for a list of profile attribute values users can select from. Specify each value followed by pressing **TAB** or **ENTER**. Values can be up to 64 characters long, and users can choose multiple values.
|
||||
> - **Ranked** for an ordered list of values, where each value ranks higher or lower than the others - for example, clearance or classification levels such as *Public*, *Secret*, and *Top Secret*. Specify each value, then order the values from lowest to highest rank. Users can choose a single value. Because the values are ordered, [access control policies](/administration-guide/manage/admin/abac-system-wide-policies) can compare a ranked attribute against a threshold - for example, clearance *is at least* Secret - rather than only checking for an exact match.
|
||||
|
||||
3. Specify the attribute's visibility as one of the following:
|
||||
|
||||
> - **Always show**: The attribute is always visible in user profiles.
|
||||
> - **Hide when empty**: The attribute is only visible in user profiles when it has a value.
|
||||
> - **Always hide**: The attribute is never visible in user profiles.
|
||||
|
||||
<Note>
|
||||
|
||||
If an attribute is created or updated without explicit visibility or sort order (for example via API or automation), Mattermost returns default values in responses even if those attributes weren't stored. Visibility defaults to the server's default visibility, and sort order defaults to `0`.
|
||||
|
||||
</Note>
|
||||
|
||||
4. Configure user editing permissions (Mattermost v11 or later) by enabling or disabling the **Editable by users** option.
|
||||
5. Save your changes.
|
||||
|
||||
<Tip>
|
||||
|
||||
Duplicate existing attributes by selecting **More** <img src={useBaseUrl('/img/ui/dots-horizontal_F01D8.svg')} alt="Use the More icon to access additional message options." className="theme-icon" /> and selecting **Duplicate attribute**. This creates a new attribute with the same name and type as the original attribute. You can then edit the new attribute to change its name, type, and visibility as needed.
|
||||
|
||||
</Tip>
|
||||
|
||||
## Manage attributes
|
||||
|
||||
- **Modify**: Select the attribute fields to make inline changes to the attribute's name, type, or values. Select **More** <img src={useBaseUrl('/img/ui/dots-horizontal_F01D8.svg')} alt="Use the More icon to access additional message options." className="theme-icon" /> to change a attribute's visibility.
|
||||
|
||||
<Note>
|
||||
|
||||
From Mattermost v11.8, existing user attributes are backfilled so the Display Name initially matches the Name. If the Display Name is empty or unavailable, Mattermost falls back to showing the Name. Duplicate display names are permitted, but internal names remain unique.
|
||||
|
||||
</Note>
|
||||
|
||||
- **Order**: Control the order you want attributes to appear in user profiles by dragging and dropping them in the list.
|
||||
- **Delete**: Delete attributes you no longer need or want by selecting **More** <img src={useBaseUrl('/img/ui/dots-horizontal_F01D8.svg')} alt="Use the More icon to access additional message options." className="theme-icon" /> and selecting **Delete property**.
|
||||
|
||||
<Note>
|
||||
|
||||
When updating user attributes via API or automation, the `attrs` object replaces existing attribute settings rather than merging. If you send only visibility, the sort order resets to `0` unless you include `sort_order` in the same request. If a patch fails, the API may return the error string "Unable to patch User Attribute field".
|
||||
|
||||
</Note>
|
||||
|
||||
- **User Edit Permissions**: From Mattermost v11, all user attributes are admin-managed by default for enhanced security. To allow user editing for specific attributes, administrators can enable this through the **More** <img src={useBaseUrl('/img/ui/dots-horizontal_F01D8.svg')} alt="Use the More icon to access additional message options." className="theme-icon" /> menu and selecting **Allow user editing**. This should only be enabled for attributes that do not impact security access controls or organizational policies. Attributes used in ABAC policies should remain admin-managed unless there's a specific business need and the security implications are fully understood.
|
||||
- **Edit User Attribute Values**: From Mattermost v11.1, you can view and update user attribute values for individual users through the System Console. See the [Manage user attributes](/administration-guide/configure/user-management-configuration-settings#manage-user-attributes) documentation for details.
|
||||
|
||||
In cases where multiple system admins manage system attributes, refresh your web browser instance to see real-time updates to system attributes made by other admins.
|
||||
|
||||
## Attribute access modes
|
||||
|
||||
Each user attribute has an **access mode** that controls who can see its values when those values appear in access control policies. The access mode is set per attribute and determines whether an admin editing a policy can see the raw attribute values referenced in its rules.
|
||||
|
||||
Access modes are not configurable through the System Console. They can only be set programmatically by a Mattermost plugin using the [Property Fields API](https://api.mattermost.com/#tag/properties). Attributes created by system admins through the System Console always use the `public` access mode. A plugin that needs to restrict attribute visibility must create the field with `protected: true` and set the desired `access_mode` — this is typically done by plugins that sync sensitive attributes from an external identity source and need to control which admins can read those values.
|
||||
|
||||
- **Public** (default): The attribute value is visible to all admins editing policies that reference it. No masking is applied.
|
||||
- **Shared only**: The attribute value is only visible to admins who personally hold that same value. Admins who don't hold the value see it masked (`--------`) in the policy editor.
|
||||
- **Source only**: The attribute value is only visible to the system source that set it (for example, the identity provider). All admins see it masked in the policy editor regardless of their own attribute values.
|
||||
|
||||
Masking in the policy editor only applies to attributes with `shared_only` or `source_only` access mode. See [Attribute value masking](/administration-guide/manage/admin/abac-system-wide-policies#attribute-value-masking) for the editor behavior when masked values are present.
|
||||
|
||||
## Sync attributes with your identity provider
|
||||
|
||||
1. Synchronize a attribute with AD/LDAP or SAML by selecting **More** <img src={useBaseUrl('/img/ui/dots-horizontal_F01D8.svg')} alt="Use the More icon to access additional message options." className="theme-icon" /> and selecting **Link attribute to AD/LDAP** or **Link attribute to SAML**. Mattermost takes you directly to the **AD/LDAP** or **SAML 2.0** configuration settings page in the System Console where you can map the attributes you want to synchronize.
|
||||
2. Scroll to the **User attributes sync** section to specify the attribute used to populate the attribute in user profiles.
|
||||
3. Specify the attribute mapping for the attribute by entering the attribute name in the system attribute's **Attribute** field. The attribute name must match the attribute name in your identity provider.
|
||||
4. Save your changes.
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
title: "Access control"
|
||||
---
|
||||
Who can do what in a workspace that's already running: the roles and permissions you grant, the attributes you record about people, and the policies that decide which teams and channels they can reach.
|
||||
|
||||
- [Manage team and channel members](/administration-guide/manage/team-channel-members) - Add, remove, and change the roles of members.
|
||||
- [Advanced permissions](/administration-guide/onboard/advanced-permissions) - Customize what each role is allowed to do.
|
||||
- [Advanced permissions: backend infrastructure](/administration-guide/onboard/advanced-permissions-backend-infrastructure) - How the permissions system is structured.
|
||||
- [Delegated granular administration](/administration-guide/onboard/delegated-granular-administration) - Grant admin access to part of the System Console.
|
||||
- [Attribute-based access control](/administration-guide/manage/admin/attribute-based-access-control) - Grant access based on user attributes rather than membership lists.
|
||||
- [User attributes](/administration-guide/manage/admin/user-attributes) - Define the custom attributes that access rules are written against.
|
||||
- [System-wide attribute-based access policies](/administration-guide/manage/admin/abac-system-wide-policies) - Organization-wide policies System Admins assign to teams and channels.
|
||||
- [Team membership policies](/administration-guide/manage/admin/abac-team-membership) - Control who can join a team.
|
||||
- [Team channel policies](/administration-guide/manage/admin/abac-team-channel-policies) - Channel membership policies Team Admins manage from Team Settings.
|
||||
- [Channel-specific access rules](/administration-guide/manage/admin/abac-channel-access-rules) - Access rules Channel Admins manage from Channel Settings.
|
||||
|
||||
Setting up authentication and creating accounts in the first place is covered in [onboard users](/administration-guide/onboard/onboard-index), including [guest accounts](/administration-guide/onboard/guest-accounts).
|
||||
Reference in New Issue
Block a user