Files
claw-code-parity/docs/telegram-gateway-operator-guide.md
T
Wylabb 6431bcac50
Build Claw Telegram / build (push) Successful in 4m45s
Build Claw Telegram / cleanup (push) Successful in 1s
Require API key in Unraid gateway templates
2026-04-04 23:56:53 +02:00

13 KiB

Telegram Gateway Operator Guide

This document is the operator and maintainer guide for the Rust Telegram integration in claw-code-parity.

It is written for:

  • humans deploying the bot on Unraid or Docker
  • future AI agents modifying the deployment
  • reviewers trying to understand how the gateway, workers, and manifest fit together

What Exists

There are three distinct runtime modes:

  • claw-telegram gateway serve

    • production gateway mode
    • owns the Telegram bot token
    • polls Telegram
    • reads the manifest
    • creates and reconciles worker containers through the Docker socket
  • claw-telegram standalone serve

    • legacy single-process mode
    • one process handles Telegram and the runtime directly
    • does not use the gateway/worker split
  • claw-profile-worker serve

    • internal worker process
    • one worker per profile
    • normally started by the gateway, not by end users

Architecture

The gateway/worker model is the intended deployment.

Topology:

  1. One Telegram bot token
  2. One gateway process
  3. One manifest file
  4. One worker container per profile

Important invariants:

  • one Telegram bot token must have one inbound owner
  • the gateway is the only process that should poll Telegram
  • workers do not talk to Telegram directly
  • workers are selected by profile mapping from the manifest
  • one profile owns one isolated worker container
  • one profile may have multiple channel identities in the future
  • today, only Telegram DM identities are implemented

Core Files And State

Gateway manifest:

  • default in-container path: /appdata/profiles.json
  • default Unraid host path: /mnt/user/appdata/claw-telegram-gateway/profiles.json

Gateway runtime state:

  • default in-container path: /appdata/state
  • used for Telegram update offsets and downloaded/generated files staged by the gateway

Generated Unraid worker templates:

  • live Unraid directory: /boot/config/plugins/dockerMan/templates-user/

Important Unraid fact:

  • the current value of each config field is stored in the text body of each <Config>...</Config> element
  • replacing the live XML with a blank repo copy can wipe stored values

Which Template To Use

For the gateway/worker deployment, use the gateway template:

Do not confuse it with the legacy standalone template:

  • standalone mode expects CLAW_TELEGRAM_BOT_TOKEN
  • gateway mode expects CLAW_GATEWAY_TELEGRAM_BOT_TOKEN

If the container logs mention CLAW_TELEGRAM_BOT_TOKEN, the container is almost certainly starting in standalone mode instead of gateway mode.

Required Gateway Settings

The gateway container needs:

  • Telegram bot token

    • CLAW_GATEWAY_TELEGRAM_BOT_TOKEN
  • worker API bearer token

    • CLAW_WORKER_AUTH_TOKEN
  • worker image

    • CLAW_GATEWAY_WORKER_IMAGE
  • appdata mount

    • host path usually /mnt/user/appdata/claw-telegram-gateway
    • mounted to /appdata
  • Docker socket mount

    • /var/run/docker.sock:/var/run/docker.sock
  • Unraid templates mount

    • /boot/config/plugins/dockerMan/templates-user:/unraid/templates-user
  • Docker network

    • claw_gateway
  • ANTHROPIC_API_KEY

Bearer-only auth is not currently supported by the Anthropic Messages API used by this runtime. If you pass only ANTHROPIC_AUTH_TOKEN, workers will fail with:

  • 401 Unauthorized (authentication_error): OAuth authentication is currently not supported.

The gateway copies inherited auth env vars into worker containers according to CLAW_GATEWAY_INHERITED_ENV.

First Boot

Expected first-boot behavior:

  1. The gateway starts with claw-telegram gateway serve
  2. If the manifest does not exist yet, the gateway creates an empty manifest automatically
  3. The gateway connects to Telegram
  4. Users are rejected until they are mapped in the manifest

The gateway does not auto-map Telegram users from chat messages.

Manifest Model

The manifest is the source of truth for routing.

Each profile contains:

  • profile_id
  • optional display_name
  • worker
    • container name
    • host state directory
    • host workspace directory
  • channels
    • today: Telegram DM user IDs

Example:

{
  "version": 1,
  "gateway": {
    "worker_image": "git.wylab.me/wylab/claw-telegram:latest",
    "worker_network": "claw_gateway",
    "template_dir": "/unraid/templates-user",
    "template_file_prefix": "claw-worker-",
    "template_archive_dir": "/appdata/template-archive",
    "inherited_env": ["ANTHROPIC_API_KEY"]
  },
  "worker_defaults": {
    "bind_port": 8080,
    "default_cwd": "/workspace",
    "permission_mode": "workspace-write",
    "model": "claude-opus-4-6",
    "host_state_root": "/mnt/user/appdata/claw-workers",
    "host_workspace_root": "/mnt/user/appdata/claw-workers"
  },
  "profiles": [
    {
      "profile_id": "makar",
      "display_name": "Makar",
      "worker": {
        "container_name": "claw-worker-makar",
        "host_state_dir": "/mnt/user/appdata/claw-workers/makar/state",
        "host_workspace_dir": "/mnt/user/appdata/claw-workers/makar/workspace"
      },
      "channels": [
        {
          "channel": "telegram",
          "kind": "dm",
          "user_id": 239824268
        }
      ]
    }
  ]
}

Validation rules:

  • profile IDs must be unique
  • worker container names must be unique
  • host state directories must be unique
  • host workspace directories must be unique
  • Telegram user IDs must be unique across all profiles

Command Reference

The command surface is implemented in rust/crates/claw-telegram/src/main.rs and rust/crates/claw-profile-worker/src/main.rs.

claw-telegram

Top-level commands:

  • claw-telegram serve

    • alias for legacy standalone mode
  • claw-telegram standalone serve

    • starts the old monolithic Telegram bot
  • claw-telegram gateway serve

    • starts the gateway mode
  • claw-telegram workers reconcile

    • loads the manifest
    • creates missing worker templates
    • creates missing worker containers
    • restarts stopped workers
    • archives removed templates
    • removes deleted managed containers
  • claw-telegram profiles add <profile-id> [--user-id <telegram-user-id>] [--display-name <name>]

    • creates a new profile
    • derives worker container and host paths
    • optionally attaches a Telegram user
    • saves manifest
    • reconciles workers
  • claw-telegram profiles remove <profile-id>

    • removes a profile from the manifest
    • reconciles workers
    • removes the container
    • keeps host state/workspace paths
  • claw-telegram profiles merge <source-profile-id> <target-profile-id>

    • moves the source profile channel identities into the target profile
    • removes the source profile
    • reconciles workers
    • keeps source state/workspace paths
  • claw-telegram profiles channel add <profile-id> telegram <user-id>

    • attaches a Telegram DM identity to an existing profile
    • saves manifest
    • reconciles workers
  • claw-telegram profiles channel remove telegram <user-id>

    • removes a Telegram DM identity from whatever profile currently owns it
    • saves manifest
    • reconciles workers
  • claw-telegram migrate-standalone-registry [registry-path]

    • imports old standalone registry data into the gateway manifest
    • then reconciles workers

claw-profile-worker

  • claw-profile-worker serve
    • starts one worker instance
    • expects worker env vars to already be present

Practical Operator Workflows

Start the gateway

If the container entrypoint is configured correctly, Unraid should start:

claw-telegram gateway serve

Add the first Telegram user

Example:

docker exec -it claw-telegram-gateway \
  claw-telegram profiles add makar --user-id 239824268 --display-name "Makar"

What this does:

  1. creates profile makar
  2. maps Telegram user 239824268
  3. writes the manifest
  4. reconciles worker state
  5. creates the worker template and worker container

Add another mapped user to an existing profile

docker exec -it claw-telegram-gateway \
  claw-telegram profiles channel add makar telegram 123456789

Remove a profile

docker exec -it claw-telegram-gateway \
  claw-telegram profiles remove makar

Merge two mistaken profiles

docker exec -it claw-telegram-gateway \
  claw-telegram profiles merge old-profile makar

Force a resync after manual manifest edits

docker exec -it claw-telegram-gateway \
  claw-telegram workers reconcile

Telegram Chat Commands

Once a Telegram user is mapped to a profile, the bot supports:

  • /start
  • /help
  • /status
  • /new
  • /cancel

These are user-facing chat commands, not gateway administration commands.

How Routing Works

When a Telegram DM arrives:

  1. the gateway polls Telegram
  2. the gateway reads from.id
  3. the gateway loads the manifest
  4. the gateway finds the profile whose channels include that Telegram user ID
  5. the gateway ensures that profile's worker exists
  6. the gateway sends the turn to that worker
  7. the worker streams events back
  8. the gateway renders those events back to Telegram

If no profile matches that Telegram user ID, the bot replies:

You are not mapped to a profile in the gateway manifest.

That message means the gateway is healthy enough to receive updates, but the sender is not registered yet.

Worker Lifecycle

Workers are managed by the gateway through the Docker socket.

The gateway:

  • creates host state/workspace directories
  • writes a worker template XML for each profile
  • creates the worker container from CLAW_GATEWAY_WORKER_IMAGE
  • starts it
  • health-checks it over HTTP

Current worker startup command:

  • entrypoint: claw-profile-worker
  • args: serve

Unraid-Specific Notes

Network mode

The gateway container must not run with --net=none.

Use:

  • claw_gateway for the real deployment

Why:

  • the gateway needs outbound HTTPS to Telegram
  • the gateway also needs network reachability to worker containers

Docker socket access

The gateway image currently runs as root by default.

Reason:

  • the gateway must open /var/run/docker.sock
  • the current worker bind-mount model also depends on writable host paths without an explicit PUID/PGID ownership flow

Live template values

Unraid stores actual values in the live template file under:

  • /boot/config/plugins/dockerMan/templates-user/

Do not blindly overwrite a configured live template with a blank repo copy after you have entered secrets.

Gateway template vs standalone template

If you are deploying the gateway/worker architecture:

  • use the gateway template
  • do not use the legacy standalone template

If you see logs complaining about CLAW_TELEGRAM_BOT_TOKEN, you are almost certainly starting the standalone path, not the gateway path.

Troubleshooting

You are not mapped to a profile in the gateway manifest.

Cause:

  • the sender's Telegram user ID is not attached to any profile

Fix:

  • add the user ID with profiles add or profiles channel add

missing required environment variable CLAW_TELEGRAM_BOT_TOKEN

Cause:

  • the container started in standalone mode
  • or the old standalone template is being used

Fix:

  • ensure the gateway container starts with gateway serve
  • ensure the template uses CLAW_GATEWAY_TELEGRAM_BOT_TOKEN

gateway manifest does not exist at /appdata/profiles.json

Cause:

  • older gateway build

Current behavior:

  • first boot now auto-creates the manifest if it does not exist

Permission denied (os error 13) when opening Docker socket

Cause:

  • gateway container was running as an unprivileged user

Current behavior:

  • published gateway image now runs as root by default so it can access /var/run/docker.sock

error sending request for url (.../getMe)

Meaning:

  • the Telegram token is loaded
  • the HTTPS request itself failed before Telegram returned an API payload

Likely causes:

  • container network mode is none
  • DNS failure
  • outbound 443 blocked
  • TLS or host time issue

Template values disappear after saving in Unraid

Likely causes:

  • using a stale live template
  • overwriting the live template file with a blank repo copy
  • using a malformed template where variable fields are self-closing instead of carrying body text

Operational rule:

  • the repo template is the seed
  • the live template in templates-user is where Unraid persists actual values

Notes For Future Maintainers And AI Agents

Do not change these assumptions casually:

  • one Telegram bot token must have one gateway owner
  • the manifest is the routing source of truth
  • workers are per-profile and isolated by host directories
  • the gateway template and worker templates are part of the operational surface, not just examples
  • Unraid stores live config values in the live XML files, not only in the UI
  • the published image must contain both claw-telegram and claw-profile-worker
  • the gateway image must be able to talk to the Docker socket

If you modify commands, env vars, or manifest behavior:

  • update this guide
  • update the gateway template
  • update the worker template generator
  • keep the CLI help output and docs aligned