Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Nextcloud Talk

Nextcloud Talk integration via the Talk Bot webhook protocol. Self-hosted, federated, and E2E-capable: another sovereign-communication option alongside Matrix and Mattermost.

Who can talk to the agent

Inbound senders are gated against the peer set resolved for the bound agent, drawn from the peer_groups config the agent belongs to. Matching strips a leading @ and is case-insensitive against the channel’s native sender identifier. An empty set denies everyone; a set containing "*" accepts anyone; otherwise only the listed external peers (and peer agents) are accepted. This is separate from gateway pairing (gateway.require_pairing), which authenticates HTTP/WebSocket clients, not chat-channel senders.

A peer group for nextcloud sets channel to nextcloud, lists the allowed senders in external_peers (for nextcloud, the Nextcloud actor ID; ["*"] accepts anyone), optionally names peer agents for cross-agent dispatch, an ignore blocklist, and an output_modality (mirror, voice, or text). See Peer Groups for the field reference.

Where to set this:

Gateway dashboard

Open /config/peer_groups in the web dashboard.

zerocode

In the Config pane, under Peer groups.

What this integration does

  • Receives inbound Talk events via POST /nextcloud-talk/<alias> on the gateway (bare /nextcloud-talk still works as a deprecated fallback)
  • Requires and verifies webhook signatures (HMAC-SHA256) with the installed bot secret
  • Sends replies back to Talk rooms via the signed Nextcloud Talk Bot API

Prerequisites

  • Nextcloud server 27.1 or later with Talk 17.1 or later. This is a hard minimum, not a recommendation: the signed Talk Bot API this integration uses to send replies was introduced in Talk 17.1, and occ talk:bot:install below is unavailable on earlier releases.

  • Bot installed with both the webhook and response features, which let Nextcloud deliver room messages to ZeroClaw and let ZeroClaw send replies:

    sudo -u www-data php occ talk:bot:install \
      -f webhook -f response \
      zeroclaw-bot '<shared-secret>' \
      'https://<your-public-url>/nextcloud-talk/<alias>'
    
  • Bot secret from that installation. Nextcloud issues one shared secret per bot, used both to verify inbound webhook signatures and to sign outbound bot-API replies. Set it as webhook_secret, which is canonical. bot_token is a deprecated alias for the same value: if both are set they must be identical. It cannot hold a different outbound secret. Conflicting non-empty values are not silently resolved in favour of one; the conflict is logged and the alias resolves to no secret, so the channel then behaves exactly as if unconfigured: inbound 401, no outbound send.

  • Publicly-reachable gateway: see Setup → Container for tunnel options if self-hosted

Both directions fail closed on a missing secret, and there is no unauthenticated mode:

  • Inbound: signature verification is mandatory. With no resolved secret the webhook endpoint returns 401 and never reaches the agent. There is no “public” mode that accepts unverified webhooks.
  • Outbound: no request is sent at all, so misconfiguration never puts an unsigned or wrongly-signed request on the wire.

Upgrading is a breaking change. A deployment that previously ran without a secret accepted webhooks; it now rejects every one of them with 401. Install the bot with occ talk:bot:install, then set that secret as webhook_secret before upgrading, or inbound messages stop being processed.

Configuration

app_token 🔑 secret · default null

Deprecated, unused. Nextcloud Talk sends do not authenticate via OCS bearer auth (see webhook_secret); this field is only accepted so existing configs that set it don’t fail to parse. Safe to remove from your config.

Set it on any surface:

Gateway dashboard

Open /config/channels/nextcloud_talk and set the channels.nextcloud_talk.<alias>.app_token field.

zerocode

In the Config pane, set the channels.nextcloud_talk.<alias>.app_token field.

zeroclaw config

zeroclaw config set channels.nextcloud_talk.<alias>.app_token    # masked input, stored encrypted

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__nextcloud_talk__<alias>__app_token=
base_url* string · default

Nextcloud base URL (e.g. "https://cloud.example.com").

Set it on any surface:

Gateway dashboard

Open /config/channels/nextcloud_talk and set the channels.nextcloud_talk.<alias>.base_url field.

zerocode

In the Config pane, set the channels.nextcloud_talk.<alias>.base_url field.

zeroclaw config

zeroclaw config set channels.nextcloud_talk.<alias>.base_url <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__nextcloud_talk__<alias>__base_url=
bot_name string? · default null

Display name of the bot in Nextcloud Talk (e.g. “zeroclaw”). Used to filter out the bot’s own messages and prevent feedback loops. If not set, defaults to an empty string (no self-message filtering by name).

Set it on any surface:

Gateway dashboard

Open /config/channels/nextcloud_talk and set the channels.nextcloud_talk.<alias>.bot_name field.

zerocode

In the Config pane, set the channels.nextcloud_talk.<alias>.bot_name field.

zeroclaw config

zeroclaw config set channels.nextcloud_talk.<alias>.bot_name <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__nextcloud_talk__<alias>__bot_name=
bot_token 🔑 secret · default

DEPRECATED alias for webhook_secret, kept for migration. Nextcloud issues ONE secret per installed bot and uses it for both directions, so this cannot hold a different outbound secret. When both are set to different non-empty values the channel logs the conflict and fails closed as unconfigured: inbound 401 and no outbound send. Prefer webhook_secret; this alias will be removed. Upgrade from a bot_token-only configuration: copy the installed bot secret to webhook_secret, verify replies still send, then delete bot_token.

Set it on any surface:

Gateway dashboard

Open /config/channels/nextcloud_talk and set the channels.nextcloud_talk.<alias>.bot_token field.

zerocode

In the Config pane, set the channels.nextcloud_talk.<alias>.bot_token field.

zeroclaw config

zeroclaw config set channels.nextcloud_talk.<alias>.bot_token    # masked input, stored encrypted

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__nextcloud_talk__<alias>__bot_token=
draft_update_interval_ms integer · default 1000

Retained for configuration compatibility. Currently inert while draft updates are disabled for this channel. Default: 1000 ms.

Set it on any surface:

Gateway dashboard

Open /config/channels/nextcloud_talk and set the channels.nextcloud_talk.<alias>.draft_update_interval_ms field.

zerocode

In the Config pane, set the channels.nextcloud_talk.<alias>.draft_update_interval_ms field.

zeroclaw config

zeroclaw config set channels.nextcloud_talk.<alias>.draft_update_interval_ms <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__nextcloud_talk__<alias>__draft_update_interval_ms=
excluded_tools string[] · default []

Tools excluded from this channel’s tool spec. When set, these tools are not exposed to the model when responding via this channel.

Set it on any surface:

Gateway dashboard

Open /config/channels/nextcloud_talk and set the channels.nextcloud_talk.<alias>.excluded_tools field.

zerocode

In the Config pane, set the channels.nextcloud_talk.<alias>.excluded_tools field.

zeroclaw config

zeroclaw config set channels.nextcloud_talk.<alias>.excluded_tools <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__nextcloud_talk__<alias>__excluded_tools=
proxy_url string? · default null

Per-channel proxy URL (http, https, socks5, socks5h). Overrides the global [proxy] setting for this channel only.

Set it on any surface:

Gateway dashboard

Open /config/channels/nextcloud_talk and set the channels.nextcloud_talk.<alias>.proxy_url field.

zerocode

In the Config pane, set the channels.nextcloud_talk.<alias>.proxy_url field.

zeroclaw config

zeroclaw config set channels.nextcloud_talk.<alias>.proxy_url <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__nextcloud_talk__<alias>__proxy_url=
stream_mode StreamMode · default "off"

Retained for configuration compatibility. Nextcloud Talk’s bot API does not provide message IDs or edit/delete operations, so draft updates are disabled and responses are currently sent as one final message for every value.

Set it on any surface:

Gateway dashboard

Open /config/channels/nextcloud_talk and set the channels.nextcloud_talk.<alias>.stream_mode field.

zerocode

In the Config pane, set the channels.nextcloud_talk.<alias>.stream_mode field.

zeroclaw config

zeroclaw config set channels.nextcloud_talk.<alias>.stream_mode <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__nextcloud_talk__<alias>__stream_mode=
webhook_secret 🔑 secret · default null

The bot secret Nextcloud installed for this bot. Canonical field. Used for BOTH directions: verifying inbound webhook signatures and signing outbound bot-API requests. When it is unset, inbound webhooks are rejected and no outbound request is sent (fail closed). Can also be set via ZEROCLAW_NEXTCLOUD_TALK_WEBHOOK_SECRET.

Set it on any surface:

Gateway dashboard

Open /config/channels/nextcloud_talk and set the channels.nextcloud_talk.<alias>.webhook_secret field.

zerocode

In the Config pane, set the channels.nextcloud_talk.<alias>.webhook_secret field.

zeroclaw config

zeroclaw config set channels.nextcloud_talk.<alias>.webhook_secret    # masked input, stored encrypted

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__nextcloud_talk__<alias>__webhook_secret=

The channel is read from the default alias. Set it through any config surface:

Gateway dashboard

Open /config/channels/nextcloud_talk in the web dashboard.

zerocode

In the Config pane, under Channels.

webhook_secret can also be supplied at runtime via the generic env override ZEROCLAW_channels__nextcloud_talk__default__webhook_secret, useful for rotating it without editing the config.

app_token is deprecated and unused (replies no longer go through OCS bearer auth); it’s only still accepted so old configs that set it don’t fail to parse.

Gateway endpoint

sh

zeroclaw daemon

Configure your Talk bot’s webhook URL to point at the alias of the [channels.nextcloud_talk.<alias>] instance that should receive it:

https://<your-public-url>/nextcloud-talk/<alias>

For example, [channels.nextcloud_talk.work] receives POST /nextcloud-talk/work. This per-alias routing (#6312) lets you run several Talk bots side by side and deliver each one’s webhooks to the right instance.

The bare https://<your-public-url>/nextcloud-talk path still works but is deprecated: it resolves to the lexicographically-first alias (deterministic across restarts) and returns an X-Zeroclaw-Deprecation response header. Single-instance deployments can keep using it unchanged. An unknown alias returns 404.

Local development? Configure [tunnel] in your config (ngrok, Cloudflare, or Tailscale) and the gateway exposes itself on startup: see Operations → Network deployment.

Signature verification

Inbound requests must carry:

  • X-Nextcloud-Talk-Random header
  • X-Nextcloud-Talk-Signature header

ZeroClaw verifies:

expected_sig = hex(hmac_sha256(secret, random + raw_request_body))
if X-Nextcloud-Talk-Signature != expected_sig:
    return 401

Without a resolved secret, ZeroClaw returns 401 before parsing or dispatching the webhook. There is no mode that accepts an unverified request.

Message routing

  • Bot-originated events (actorType = "bots") are ignored: prevents feedback loops
  • System events (joins, leaves, membership changes) are ignored
  • Non-message events are ignored
  • User messages are dispatched to the agent loop
  • Replies go back to the originating room via the token in the webhook payload

Quick validation

  1. Set external_peers = ["*"] in the peer group for first-time testing
  2. Send a test message in the configured Talk room
  3. Confirm ZeroClaw receives and replies in the same room
  4. Tighten the peer group to explicit actor IDs (e.g. ["alice", "bob"])

Troubleshooting

  • 404 Nextcloud Talk not configured: [channels.nextcloud_talk.default] section missing or enabled = false
  • 401 Invalid signature: secret mismatch, wrong random header, or body-signing bug. Check the raw body is being signed (not the parsed JSON)
  • No reply, webhook 200: event was filtered. Check logs for “actorType = bots” or a sender not in the peer set
  • Replies delivered but look wrong: check thread context; Talk replies are currently root-level only

Streaming

Nextcloud Talk does not support message edits via the Bot API, so streaming draft updates are disabled for this channel. Replies are sent on stream completion only.

Self-hosting notes

  • TLS: terminate at your reverse proxy; webhook signature verification works over HTTP-to-container loopback
  • Outbound replies authenticate via the Bot API’s HMAC signature (webhook_secret/bot_token), not a bearer token; there is no separate OCS bearer credential to manage
  • Rate limits are Nextcloud-server dependent; the default bot doesn’t run into them in normal conversation cadences
  • Per-channel proxy: set proxy_url to override the global [proxy] setting for Nextcloud Talk only (http://, https://, socks5://, socks5h://)

See also