This is a dual-repo Mattermost enterprise development environment:
| Repository | Location | Purpose |
|---|---|---|
mattermost/mattermost |
/workspace |
Primary monorepo (Go server + React webapp) |
mattermost/enterprise |
$HOME/enterprise |
Private enterprise code (Go, linked via go.work) |
mattermost/mattermost-plugin-agents |
$HOME/mattermost-plugin-agents |
AI plugin for validation/testing |
PostgreSQL 14 is the only required external dependency, run via Docker Compose.
The update script handles: git auth, repo cloning, npm install, Go workspace setup, config.override.mk, and the client symlink. See below for what remains manual.
When editing translation strings, changes must ONLY be made to the relevant en.json. You MUST NOT change any other localization files.
After the update script has run:
- Start Docker daemon (if not already running):
sudo dockerd &>/tmp/dockerd.log &— wait a few seconds, verify withdocker info. - Start server + webapp together:
This single command starts Docker (postgres), builds mmctl, sets up the
cd /workspace/server && \ MM_LICENSE="$TEST_LICENSE" \ MM_PLUGINSETTINGS_ENABLEUPLOADS=true \ MM_PLUGINSETTINGS_ENABLE=true \ MM_SERVICESETTINGS_SITEURL=http://localhost:8065 \ make BUILD_ENTERPRISE_DIR="$HOME/enterprise" run
go.workand client symlink, compiles the Go server with enterprise tags, runs it in the background, then starts the webpack watcher for the webapp. The server listens on:8065. - Restart server after code changes:
This stops the running server and re-runs it with enterprise. Webapp changes are picked up by webpack automatically (browser refresh needed).
cd /workspace/server && \ MM_LICENSE="$TEST_LICENSE" \ make BUILD_ENTERPRISE_DIR="$HOME/enterprise" restart-server
The TEST_LICENSE secret provides a Mattermost Enterprise Advanced license. When set via MM_LICENSE, the server logs "License key from ENV is valid, unlocking enterprise features." and the "TEAM EDITION" badge disappears from the UI.
You MUST pass BUILD_ENTERPRISE_DIR="$HOME/enterprise" to every make command — run, restart-server, run-server, test-server, check-style, etc. Without it, the Makefile defaults to ../../enterprise (which doesn't exist), and the build silently falls back to team edition.
The plugin is deployed from $HOME/mattermost-plugin-agents using:
cd $HOME/mattermost-plugin-agents && MM_SERVICESETTINGS_SITEURL=http://localhost:8065 make deployTo configure a service and agent, patch the Mattermost config API. The ANTHROPIC_API_KEY environment variable must be set.
Critical gotcha: The config field under mattermost-ai must be a JSON object, not a JSON string. If stored as a string, the plugin logs LoadPluginConfiguration API failed to unmarshal.
Example config patch (use python to safely inject the API key from env):
import json, os
config = {
"PluginSettings": {
"Plugins": {
"mattermost-ai": {
"config": { # MUST be an object, NOT json.dumps(...)
"services": [{
"id": "anthropic-svc-001",
"name": "Anthropic Claude",
"type": "anthropic",
"apiKey": os.environ["ANTHROPIC_API_KEY"],
"defaultModel": "claude-sonnet-4-6",
"tokenLimit": 200000,
"outputTokenLimit": 16000,
"streamingTimeoutSeconds": 300
}],
"bots": [{
"id": "claude-bot-001",
"name": "claude",
"displayName": "Claude Assistant",
"serviceID": "anthropic-svc-001",
"customInstructions": "You are a helpful AI assistant.",
"enableVision": True,
"disableTools": False,
"channelAccessLevel": 0,
"userAccessLevel": 0,
"reasoningEnabled": True,
"thinkingBudget": 1024
}],
"defaultBotName": "claude"
}
}
}
}
}
# Write to temp file, then: curl -X PUT http://localhost:8065/api/v4/config/patch -H "Authorization: Bearer $TOKEN" -d @file.jsonSupported service types: openai, openaicompatible, azure, anthropic, asage, cohere, bedrock, mistral. The API key goes in services[].apiKey. Never log or print it.
- "TEAM EDITION" means no license, not no enterprise code. The webapp shows "TEAM EDITION" when
license.IsLicensed === 'false', regardless ofBuildEnterpriseReady. Fix: passMM_LICENSE="$TEST_LICENSE"when starting the server. To verify enterprise code is loaded independently: check server logs for"Enterprise Build", enterprise_build: trueor the API at/api/v4/config/client?format=oldforBuildEnterpriseReady: true. - The server auto-generates
server/config/config.jsonon first run; default SQL points topostgres://mmuser:mostest@localhost/mattermost_testmatching Docker Compose. - The first user created via
/api/v4/usersgetssystem_adminrole automatically. - SMTP errors and plugin directory warnings on startup are expected in dev — non-blocking.
- License errors in logs ("Failed to read license set in environment") are normal — enterprise features requiring a license won't be available but the server runs fine.
- The enterprise repo must be on a compatible branch with the main repo.
- The VM's global gitconfig may have
url.*.insteadOfrules embedding the default Cursor agent token, which only has access tomattermost/mattermost. The update script cleans these and sets upgh authwithCURSOR_GH_TOKENinstead.
Server (with enterprise): all commands from /workspace/server/, always include BUILD_ENTERPRISE_DIR="$HOME/enterprise":
- Run:
make BUILD_ENTERPRISE_DIR="$HOME/enterprise" run - Restart:
make BUILD_ENTERPRISE_DIR="$HOME/enterprise" restart-server - Lint:
make BUILD_ENTERPRISE_DIR="$HOME/enterprise" check-style - Tests:
make BUILD_ENTERPRISE_DIR="$HOME/enterprise" test-server(needs Docker). Quick:go test ./public/model/... - Standalone build:
make BUILD_ENTERPRISE_DIR="$HOME/enterprise" build-linux(or usego build -tags 'enterprise sourceavailable' ...directly)
Webapp: run from /workspace/webapp/
- Lint:
npm run check - Tests:
npm run test(Jest 30) - Type check:
npm run check-types - Build:
npm run build
agent-browser (Vercel) is installed globally. It provides a higher-level CLI for browser automation — navigation, clicking, typing, screenshots, accessibility snapshots, and visual diffs. Usage: agent-browser <command>. See the agent-browser skill for more information.
- Node.js: see
.nvmrc;nvm usefrom workspace root. - Go: see
server/go.mod.