feat(telegram): ADMIN_USER_ID may /impersonate another user
While on, the admin's updates are routed as the target user's (allow-list, chat, workspace, session, settings, jobs) and the target chat's output goes to the admin's chat. The target's own updates are dropped with a notice that an admin is working with their account. In memory; /impersonate 0 stops.
This commit is contained in:
@@ -68,6 +68,7 @@ The compose file maps them into the container.
|
||||
|---|---|---|---|
|
||||
| `TELEGRAM_BOT_TOKEN` | — | Required. The token from @BotFather | No, and redacted from tool output |
|
||||
| `TELEGRAM_ALLOWED_USER_IDS` | — | Required. Comma-separated Telegram user ids; the first one is the owner (the `🟢 Online.` message, Claude re-login prompts) | Yes |
|
||||
| `ADMIN_USER_ID` | — | Telegram user id that may `/impersonate` other users ([telegram-bot.md](telegram-bot.md#impersonation)) | Yes |
|
||||
| `TELEGRAM_MODEL` | `claude-code/claude-opus-5-5` | Model every chat uses; compose passes it as `Telegram__Model` | Yes, as `Telegram__Model` |
|
||||
| `TZ` | `UTC` | The container's time zone | The agent's `bash` gets the chat's zone as `TZ` instead |
|
||||
| `LOG_LEVEL` | `Information` | Passed as `Logging__LogLevel__Default` | Yes |
|
||||
|
||||
@@ -19,6 +19,7 @@ Chat with the agent through a Telegram bot. A new host project in this repo, run
|
||||
| Tools | All built-in tools; each chat works in its own directory `<WorkspaceRoot>/<chatId>` |
|
||||
| Access | Mandatory user-id allow-list; empty list = refuse to start; other users are ignored silently |
|
||||
| Chats | Private chats only |
|
||||
| Impersonation | Optional `AdminUserId` (`ADMIN_USER_ID`) may `/impersonate <user_id>` (`0` stops, in memory). The admin's updates are then routed as the target's (allow-list, chat, workspace, session, settings, jobs) and the target chat's output (replies, questions, files, scheduled deliveries) goes to the admin's chat. The target's own updates are dropped with a "an admin is working with your account" notice |
|
||||
|
||||
Out of scope: webhooks, Docker/Coolify (see `2026-09-26-docker-deployment-design.md`), voice and documents, group chats, several bot tokens, a custom polling loop.
|
||||
|
||||
@@ -30,6 +31,7 @@ Out of scope: webhooks, Docker/Coolify (see `2026-09-26-docker-deployment-design
|
||||
- `AllowedUserIds`, required, non-empty (`Telegram__AllowedUserIds__0=123`, or `TELEGRAM_ALLOWED_USER_IDS=1,2`).
|
||||
- `WorkspaceRoot`, default `~/.volcanic/telegram-workspaces`.
|
||||
- `Model`, default `claude-code/claude-opus-5-5`.
|
||||
- `AdminUserId` (also `ADMIN_USER_ID`), optional, positive: the user who may `/impersonate`.
|
||||
|
||||
At start the host logs whether the `claude` CLI was found; a missing CLI is a warning (the doctor command explains).
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ settings use the `GoogleCalendar` section, the scheduler the `Scheduler` section
|
||||
|---|---|---|
|
||||
| `TELEGRAM_BOT_TOKEN` / `Telegram:BotToken` | — | Required |
|
||||
| `TELEGRAM_ALLOWED_USER_IDS` / `Telegram:AllowedUserIds` | — | Required, comma separated. The bot refuses to start without it |
|
||||
| `ADMIN_USER_ID` / `Telegram:AdminUserId` | — (off) | The Telegram user id that may use `/impersonate` (see [Impersonation](#impersonation)). Need not be on the allow-list. Must be positive |
|
||||
| `Telegram:WorkspaceRoot` | `~/.volcanic/telegram-workspaces` | Chat `<id>` works in `<root>/<id>`; put `AGENTS.md`, `PERSONA.md`, `MEMORY.md` there |
|
||||
| `Telegram:Model` | `claude-code/claude-opus-5-5` | Any `provider/model` the Claude Code provider serves |
|
||||
| `Telegram:EditInterval` | `00:00:01.5` | Minimum time between edits of the "Working..." status message |
|
||||
@@ -205,6 +206,22 @@ the current format, and keeps the original next to it as `jobs.json.secretary-<t
|
||||
Copy the workspace's `scripts/` into the owner's workspace too: watcher commands such as `python scripts/check.py` run
|
||||
from there. A one-off that is already due fires on the first start.
|
||||
|
||||
### Impersonation
|
||||
|
||||
The admin (`ADMIN_USER_ID`) can act as another user with `/impersonate <user_id>`; `/impersonate 0` (or the admin's
|
||||
own id) stops. The command shows up in the admin's command menu only, and from anyone else it is an ordinary unknown
|
||||
command passed to the agent.
|
||||
|
||||
While it is on, the bot treats the admin's messages, commands, button presses and answers exactly as the target user's:
|
||||
the allow-list check (impersonating someone outside it means the bot ignores the admin until they stop), the chat, its
|
||||
workspace, session, time zone, jobs and calendar. Everything the target's chat sends goes to the admin's chat instead:
|
||||
replies, questions, files, notices and scheduled deliveries, including runs started by the target user's own messages.
|
||||
Meanwhile the target user is blocked: whatever they send (messages, commands, button presses) is dropped, and the bot
|
||||
answers "🔒 An admin is working with your account right now. Please try again later." The admin's own chat (their own jobs, for
|
||||
example) keeps working as before.
|
||||
|
||||
Impersonation is kept in memory only: a restart ends it.
|
||||
|
||||
### Google Calendar
|
||||
|
||||
Each chat connects its own Google account with `/calendar_connect`, and the agent's `calendar` tool only uses that
|
||||
@@ -215,7 +232,8 @@ storage and time zones are in [google-calendar.md](google-calendar.md).
|
||||
## Security
|
||||
|
||||
The agent has the built-in tools, including `bash`, and runs them as the bot's OS user. Only allow-listed user ids
|
||||
are served (everyone else is ignored and logged), and only in private chats. Run it in the container image
|
||||
are served (everyone else is ignored and logged), and only in private chats. `ADMIN_USER_ID` can act as any
|
||||
allow-listed user with `/impersonate`, so it gets that user's workspace, sessions, jobs and connected calendar. Run it in the container image
|
||||
([docker.md](docker.md)), which is the recommended setup, or at least as a dedicated user, and treat the workspace root
|
||||
as writable by anyone on the allow-list.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user