Skip to content

feat: add Mattermost channel integration#877

Draft
Aaronontheweb wants to merge 6 commits intodevfrom
feat/mattermost-channel
Draft

feat: add Mattermost channel integration#877
Aaronontheweb wants to merge 6 commits intodevfrom
feat/mattermost-channel

Conversation

@Aaronontheweb
Copy link
Copy Markdown
Collaborator

Summary

  • Adds complete Mattermost channel implementation with WebSocket gateway, actor hierarchy (gateway → conversation → session binding), ACL enforcement, and routing policies
  • Implements HMAC-SHA256 callback signing for interactive button-based approval prompts with ephemeral per-daemon keys and constant-time signature verification
  • Includes message chunking (16K char limit), bot mention stripping (@username format), file attachment metadata resolution via GetFileDetailsAsync, and URL trust validation for attachment downloads
  • Adds channel-specific LLM tools: send_mattermost_message and lookup_mattermost_user
  • Adds thread history hydration with cursor-based deduplication and prompt injection scanning on backfilled messages
  • Testcontainers-based integration tests for gateway connectivity, reply posting, and thread history fetching

Test plan

  • 123 unit tests covering conversation actor routing (22), session binding contracts (26), gateway contracts (3), ACL contracts, routing policy, approval prompt building, HMAC signing, URL trust, message chunking
  • Integration tests with real Mattermost via Testcontainers
  • dotnet slopwatch analyze clean (3 pre-existing violations in channel tools, not from this branch)
  • Copyright headers verified via Add-FileHeaders.ps1 -Verify
  • Manual smoke test against live Mattermost instance

…ion tests

Implement full Mattermost channel support following the same 3-tier actor
hierarchy pattern as Slack and Discord (Gateway → Conversation → Session
Binding). Uses Mattermost.NET v4.0.4 for WebSocket event delivery and
REST API operations.

Production implementation (~3,400 LOC):
- MattermostChannel (IChannel + IHostedService lifecycle)
- MattermostGatewayActor (4096-entry LRU event dedup)
- MattermostConversationActor (ACL, routing policy, session derivation)
- MattermostSessionBindingActor (ReceivePersistentActor with cursor tracking)
- MattermostNetGatewayClient (WebSocket transport wrapper)
- MattermostNetReplyClient + MattermostNetOutboundClient (REST API)
- MattermostThreadHistoryFetcher (thread backfill with content scanning)
- MattermostApprovalPromptBuilder (letter-based text prompts)
- SendMattermostMessageTool + LookupMattermostUserTool (channel tools)
- ACL policy, routing policy, reminder target resolver
- Config schema, DI wiring, ChannelType enum extension

Unit tests (52 tests):
- Routing policy, ACL contract, approval prompts, reminder resolution,
  config defaults, session ID parsing, user allowlist checks

Integration tests (11 tests against real Mattermost container):
- MattermostFixture with mattermost-preview Docker container
- WebSocket event delivery, thread reply routing
- Reply client posting, thread replies, message updates
- Outbound client DM channel creation, new thread posting
- Thread history fetching, root post inclusion, bot post visibility
Remove duplicated approval prompt builder methods from SessionBindingActor
(now delegates to MattermostApprovalPromptBuilder). Fix BotUserId being
captured too early at DI time by switching to lazy Func<string?> resolution.
Fail loud when ServerUrl is absent on the attachment URL trust path. Add
missing BotToken to JSON config schema. Replace Task.Delay in integration
tests with a polling helper. Cache test user token to avoid re-authenticating
per call. Remove TOCTOU File.Exists before File.Delete, redundant
BotToken.RequireValid, inline DefaultChannelId duplication, and LINQ
allocation in IsMattermostId.
…annel

Replace text-only approval prompts with Mattermost interactive message
attachments using action buttons. Add HTTP callback endpoint for button
clicks and fix several security/correctness issues found in deep review.

Interactive buttons:
- BuildButtonPrompt produces attachments with styled action buttons
- HTTP POST /api/mattermost/actions receives Mattermost button callbacks
- Graceful degradation to text-only when CallbackUrl not configured
- Buttons cleared on resolution via UpdatePostAsync with colored attachment

Security fixes:
- Fix URL trust subdomain bypass (StartsWith without trailing slash)
- Add Bearer auth to mattermost-files HttpClient (was missing)
- Whitelist selectedKey values in callback endpoint
- Fix RootPostId routing in button context (was empty, broke actor lookup)
- Add HMAC-SHA256 callback signing for interactive button webhooks with
  ephemeral per-daemon keys and constant-time signature verification
- Add ACL enforcement on callback endpoint and conversation actor for
  interaction messages
- Add message chunking at 16,000 chars with newline-aware splitting to
  stay under Mattermost's 16,383-char post limit
- Fix bot mention stripping to use @username format instead of @userid
- Resolve file attachment metadata (name, MIME type, size) via
  GetFileDetailsAsync instead of using raw file IDs
- Remove BotToken from config schema for consistency with other channels
- Add comprehensive test coverage: 123 Mattermost tests across
  conversation actor (22), session binding contracts (26), gateway
  contracts (3), URL trust (5), chunking (4), approval signing (5),
  and existing test suites
- Fix wrong ephemeral approval labels in action callback endpoint
  (used "Approve Once"/"Approve for Session"/"Always Approve" instead
  of canonical ApprovalOptionKeys labels)
- Replace raw string literals in IsValidApprovalKey with
  ApprovalOptionKeys constants to prevent drift
- Extract duplicated attachment-to-payload mapping in
  MattermostNetReplyClient into shared MapAttachments helper
- Add Contains guard before Replace in NormalizeInboundText to
  avoid unnecessary string allocation on the per-message path
- Remove TOCTOU File.Exists check before File.Delete in
  DownloadFileViaSdkAsync (Delete is a no-op for absent files)
- Remove section-divider and narration comments that duplicate
  what well-named identifiers already communicate
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant