Skip to content

Set up Mattermost & Nextcloud Talk

Set up self-hosted team-chat channels: a Mattermost bot and a webhook-based Nextcloud Talk bot.

This guide connects Revka to two self-hosted team-chat platforms: Mattermost and Nextcloud Talk. Both keep your agent’s conversation history inside infrastructure you control, which makes them a good fit for private, regulated, or air-gapped deployments. Use this page when you want a Revka bot to read and reply in a Mattermost channel or a Nextcloud Talk room.

The two integrations work differently:

  • Mattermost polls the Mattermost REST API v4 with a bot access token. It needs no public inbound port — Revka pulls new posts on a schedule.
  • Nextcloud Talk runs in webhook mode. Nextcloud pushes events to your gateway at POST /nextcloud-talk, so the gateway must be reachable over public HTTPS.

For the broader picture of how channels work, see Channels overview and the workspace-chat reference in Matrix, Mattermost & Nextcloud Talk. For the encrypted-room option, see Set up Telegram & Matrix.

Both channels are configured under [channels_config] in ~/.revka/config.toml. You can edit the file directly, or run revka onboard --channels-only to set them interactively. Apply changes by restarting the daemon.

Mattermost is the simpler of the two: create a bot account, grab the channel ID, drop both into config, and run the daemon.

  1. In Mattermost, open Main Menu → Integrations → Bot Accounts.
  2. Click Add Bot Account.
  3. Set a username, for example revka-bot.
  4. Grant the bot post and channel-read permissions (for example post:all and channel:read, or the equivalent scopes for your server).
  5. Save the generated Access Token — this is the bot_token value. It is shown once.
  1. Open the channel you want the bot to monitor.
  2. Click the channel name in the header and choose View Info.
  3. Copy the ID value (for example 7j8k9l...). This is the channel_id.

The channel_id is required for listen mode. Omit it to listen on every channel the bot account can access.

Add this section to ~/.revka/config.toml:

[channels_config.mattermost]
url = "https://mm.your-domain.com"
bot_token = "your-bot-access-token"
channel_id = "your-channel-id"
allowed_users = ["*"]
thread_replies = true
mention_only = false
FieldTypeDefaultMeaning
urlstringBase URL of your Mattermost server. Required.
bot_tokenstringThe bot account’s personal access token. Required.
channel_idstringChannel to listen to. Required for listen mode; omit to listen on all accessible channels.
allowed_usersarrayMattermost user IDs allowed to interact. [] denies all; ["*"] allows everyone.
thread_repliesbooltrueReply to top-level messages in a thread on that post.
mention_onlyboolfalseWhen true, only process messages that @-mention the bot.
proxy_urlstringOptional per-channel HTTP/SOCKS5 proxy.

Mattermost also transcribes audio attachments when the shared [transcription] section is configured (25 MB audio limit). See Voice, TTS & transcription.

  • If a user replies inside an existing thread, Revka always replies in that same thread.
  • With thread_replies = true (the default), Revka answers a top-level message by threading on it. With thread_replies = false, it answers at the channel root.
  • With mention_only = true, Revka applies an extra filter after the allowed_users check: messages without an explicit @bot_username mention are ignored, and the mention token is stripped before the text reaches the model. This is useful in busy shared channels to cut unnecessary model calls.

Start the full runtime so the channel supervisor begins polling:

Terminal window
revka daemon

Send a message in the target channel and confirm the bot replies. For a first test, set allowed_users = ["*"], then tighten it to explicit user IDs once you have confirmed delivery.

Nextcloud Talk uses a webhook bot: Nextcloud posts each room event to your gateway, Revka verifies the signature, runs the agent, and replies through the Nextcloud Talk OCS API. Because Nextcloud must reach your gateway, you need a public HTTPS URL.

Add this section to ~/.revka/config.toml:

[channels_config.nextcloud_talk]
base_url = "https://cloud.example.com"
app_token = "nextcloud-talk-app-token"
bot_name = "revka"
webhook_secret = "your-webhook-secret"
allowed_users = ["*"]
FieldTypeDefaultMeaning
base_urlstringYour Nextcloud instance URL. Required. Trailing slash is stripped.
app_tokenstringBot app token, sent as Authorization: Bearer <token> on outbound OCS replies. Required.
bot_namestringrevkaThe bot’s Talk display name. Used for self-echo prevention (see below).
webhook_secretstringShared HMAC secret for verifying inbound webhook signatures. Recommended.
allowed_usersarrayAllowed Nextcloud actor IDs. [] denies all; ["*"] allows everyone.
proxy_urlstringOptional per-channel HTTP/SOCKS5 proxy.

Expose the gateway and register the webhook

Section titled “Expose the gateway and register the webhook”

Run the gateway or daemon, then point your Nextcloud Talk bot’s webhook URL at the gateway’s ingress path:

Terminal window
revka daemon
https://<your-public-url>/nextcloud-talk

The gateway logs the registered route on startup (POST /nextcloud-talk — Nextcloud Talk bot webhook). Since Talk pushes events to you, the gateway must be reachable over public HTTPS — see Expose your gateway with a tunnel for a tunnel setup, and Network deployment, Raspberry Pi & proxy for which channels need a public port.

The inbound webhook is part of the gateway’s public ingress surface (no bearer token — it is authenticated by signature instead).

POST /nextcloud-talk
Content-Type: application/json
X-Nextcloud-Talk-Random: <random-seed>
X-Nextcloud-Talk-Signature: <hmac-hex>
ResponseWhen
404[channels_config.nextcloud_talk] is not configured.
401Signature verification failed (only checked when a secret is set).
200Accepted. Also returned when the payload contains no actionable user message (bot/system/non-allowed events are acknowledged silently).

Outbound replies go to the Talk room from the webhook payload via the OCS chat API:

POST <base_url>/ocs/v2.php/apps/spreed/api/v1/chat/<room-token>?format=json
Authorization: Bearer <app_token>
OCS-APIRequest: true

Revka handles both the Activity Streams 2.0 Create payload that current Nextcloud Talk bots send and the legacy message format, and it normalizes both millisecond and second timestamps.

When webhook_secret is set, every inbound request is authenticated by an HMAC-SHA256 signature. Revka reads two headers and computes:

hex( hmac_sha256( secret, X-Nextcloud-Talk-Random + raw_request_body ) )

The result is compared in constant time against the X-Nextcloud-Talk-Signature header. A leading sha256= prefix on the signature is accepted and stripped. The match is over the raw request body plus the random seed — not a re-serialized JSON object — so do not modify the body in any proxy between Nextcloud and the gateway. If the random header is missing, the signature is malformed, or the digest does not match, the gateway rejects the request with 401.

The webhook secret can come from config or the environment. The environment variable takes priority:

Terminal window
export REVKA_NEXTCLOUD_TALK_WEBHOOK_SECRET="your-webhook-secret"

Resolution order:

  1. REVKA_NEXTCLOUD_TALK_WEBHOOK_SECRET (environment) — used when set and non-empty.
  2. webhook_secret under [channels_config.nextcloud_talk] — used otherwise.

Use the environment variable when you would rather keep the secret out of config.toml. The same secret must be configured on the Nextcloud Talk bot so both sides compute the same HMAC.

A webhook bot can see its own replies echoed back as new room events. Left unchecked, that creates a feedback loop where the agent answers itself forever. Revka drops self-originated events before they ever reach the model:

  • Events whose actor type is bots or application are ignored.
  • Events whose actor ID carries the bots/ prefix are ignored.
  • Events whose actor name matches the configured bot_name (compared case-insensitively) are ignored. The literal name revka is always treated as the bot, even if you set a different bot_name.

The name check is the safety net: Nextcloud does not always set the actor type reliably for bot-sent messages, so matching on bot_name catches echoes that slip through the type check. Set bot_name to the exact display name your bot uses in Talk so this filter works.

After the self-echo and event-type filters, the allowed_users allowlist applies: only messages from listed actor IDs are processed (["*"] allows all). Actor IDs are matched after stripping any users/ or bots/ prefix.

  1. Set allowed_users = ["*"] for the first run.
  2. Send a test message in the target Talk room.
  3. Confirm Revka receives the event and replies in the same room.
  4. Tighten allowed_users to explicit actor IDs.
  • Mattermost: no reply. Check that bot_token is the bot account’s access token, that url has no typo, and that the sender’s user ID is in allowed_users. With mention_only = true, only @-mentions are processed.
  • Nextcloud Talk: 404 Nextcloud Talk not configured. The [channels_config.nextcloud_talk] section is missing or the daemon was not restarted after editing config.
  • Nextcloud Talk: 401. The signature did not verify. Confirm the same secret is set on both sides, that the X-Nextcloud-Talk-Random header is forwarded, and that nothing rewrites the raw request body in transit.
  • Nextcloud Talk: webhook returns 200 but no reply. The event was filtered — a bot/system event, a non-Note object, an unparseable message, or a sender not in allowed_users. The bot’s own messages are dropped by the self-echo filter.
  • Nextcloud Talk: the bot answers itself. Set bot_name to the bot’s exact Talk display name so the self-echo filter recognizes its own messages.

Run revka doctor or the dashboard Doctor page for structured diagnostics, and revka channel doctor to check channel wiring.