Document Telegram gateway operations
This commit is contained in:
@@ -31,6 +31,22 @@
|
||||
|
||||
> If you find this work useful, consider [sponsoring @instructkr on GitHub](https://github.com/sponsors/instructkr) to support continued open-source harness engineering research.
|
||||
|
||||
## Telegram Gateway Docs
|
||||
|
||||
The current Rust Telegram gateway and Unraid deployment are documented in:
|
||||
|
||||
- [docs/telegram-gateway-operator-guide.md](docs/telegram-gateway-operator-guide.md)
|
||||
|
||||
That guide covers:
|
||||
|
||||
- gateway vs standalone mode
|
||||
- manifest-driven routing
|
||||
- worker lifecycle
|
||||
- full CLI command rundown
|
||||
- Unraid template behavior
|
||||
- first boot
|
||||
- troubleshooting and operator invariants
|
||||
|
||||
---
|
||||
|
||||
## Backstory
|
||||
|
||||
@@ -0,0 +1,495 @@
|
||||
# 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:
|
||||
|
||||
- [claw-telegram-gateway.xml](../rust/crates/claw-telegram/assets/unraid/claw-telegram-gateway.xml)
|
||||
|
||||
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`
|
||||
|
||||
Recommended auth:
|
||||
|
||||
- `ANTHROPIC_AUTH_TOKEN`
|
||||
|
||||
Optional legacy auth:
|
||||
|
||||
- `ANTHROPIC_API_KEY`
|
||||
|
||||
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:
|
||||
|
||||
```json
|
||||
{
|
||||
"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_AUTH_TOKEN"]
|
||||
},
|
||||
"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](../rust/crates/claw-telegram/src/main.rs) and [rust/crates/claw-profile-worker/src/main.rs](../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:
|
||||
|
||||
```bash
|
||||
claw-telegram gateway serve
|
||||
```
|
||||
|
||||
### Add the first Telegram user
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
docker exec -it claw-telegram-gateway \
|
||||
claw-telegram profiles channel add makar telegram 123456789
|
||||
```
|
||||
|
||||
### Remove a profile
|
||||
|
||||
```bash
|
||||
docker exec -it claw-telegram-gateway \
|
||||
claw-telegram profiles remove makar
|
||||
```
|
||||
|
||||
### Merge two mistaken profiles
|
||||
|
||||
```bash
|
||||
docker exec -it claw-telegram-gateway \
|
||||
claw-telegram profiles merge old-profile makar
|
||||
```
|
||||
|
||||
### Force a resync after manual manifest edits
|
||||
|
||||
```bash
|
||||
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
|
||||
Reference in New Issue
Block a user