Сессии Claude Code на ваших машинах видны в одной закрытой Telegram-группе с темами. Каждая папка проекта получает свою тему: там видно, что делает Claude, приходят его ответы и кнопки разрешений, а ваше сообщение в теме уходит в сессию.
ноутбук, ПК, сервер сервер с Docker Telegram
claude-cctg в папке ── TLS ──> cctg hub ── Bot API ──> группа: тема на папку
Hub это один процесс с ботом. Он живёт на сервере (или на одной из ваших машин). На каждой машине с Claude Code стоит клиент cctg, его ставит одна команда.
- Telegram-бот и группа с темами (ниже, 5 минут).
- Сервер с Docker для hub. Можно и без сервера: hub на своей машине,
docs/poc.md. - На каждой машине с сессиями: Linux, macOS (Apple Silicon) или Windows. Claude Code установщик поставит сам, если его нет.
- Windows: Git for Windows (в нём Git Bash, через него работают установщик и хуки cctg). Если его нет:
winget install Git.Git, потом откройте Git Bash.
- В Telegram у @BotFather:
/newbot, получить токен бота. - Создать группу, в настройках включить «Темы» (Topics).
- Добавить бота в группу и сделать админом с правами «Управление темами», «Удаление сообщений», «Закрепление сообщений».
- Написать в группу любое сообщение и открыть
https://api.telegram.org/bot<токен>/getUpdates:chat.id(начинается с-100) это id группы,from.idэто ваш id. - Необязательно, личка с темами: в @BotFather откройте Mini App, My bots → бот → Bot Settings → Threads Settings, включите Threaded Mode, а создание тем пользователями там же лучше выключить. Потом откройте личный чат с ботом и нажмите Start. Hub при старте видит режим сам (
getMe): новые сессии получают тему только в вашей личке, а сессии, у которых уже есть тема в группе, идут в обе. - Необязательно, ещё группы (с личкой из шага 5): добавьте бота в другую группу с темами администратором с правами «Управление темами» и «Удаление сообщений» от своего имени, не анонимно (иначе Telegram не скажет боту, кто его добавил, и бот из группы выйдет). Hub подключает группу сам, без перезапуска и без правки
.env, и пишет в её General, что она подключена или чего не хватает. Если бот уже был в группе или права поменяли позже, напишите там/connect@<имя_бота>(без админских прав бот видит только команды со своим именем). Группа изCCTG_CHAT_IDостаётся основной.
На сервере с Docker:
curl -fsSL https://raw.githubusercontent.com/pockerhead/cctg/main/install.sh | sh -s -- --hubУстановщик спросит токен бота (не показывается при вводе), id группы, ваш id, адрес сервера и, если Telegram с сервера доступен только через прокси, адрес прокси. Он сам сделает секрет и сертификат, запустит hub в Docker и проверит бота и группу. В конце он печатает строку для установки клиента. В ней одноразовый код: он работает один раз и 10 минут, секрета hub в строке нет. Код для следующей машины: cd ~/cctg-hub && docker compose exec hub cctg hub code.
Подробности (прокси, обновление, порты): docs/remote-hub.md.
curl -fsSL https://raw.githubusercontent.com/pockerhead/cctg/main/install.sh | sh -s -- --hub --localТе же вопросы; прокси остаётся только в hub.env. Hub запускается при входе в систему без окон: в Windows запись автозагрузки (HKCU\...\Run, Windows Script Host запускает cctg supervise скрыто), в Linux пользовательский юнит systemd cctg-hub.service, в macOS LaunchAgent. Всё лежит в ~/.cctg/hub (hub.env, состояние, лог hub.log). Без --public-host ХОСТ hub пускает только эту машину; с ним слушает все адреса по TLS, и строку для других машин даёт /join. В конце установщик печатает строку клиента для этой машины. Linux: hub живёт, пока вы в системе; чтобы работал и без входа, loginctl enable-linger.
Выполните строку, которую напечатал hub (в Windows это Git Bash). Она выглядит так:
curl -fsSL https://raw.githubusercontent.com/pockerhead/cctg/main/install.sh | sh -s -- --hub-host <сервер> --pin <отпечаток> --join XXXX-XXXX-XXXX-XXXXУстановщик обменяет код на собственный секрет этой машины и запишет его в ~/.cctg/device.env, на экран секрет не попадает. Для каждой машины нужен свой код: /join в теме General отвечает готовой строкой с новым кодом (только людям из allowlist); сообщение потом говорит, что код использован (и каким устройством) или истёк. Список машин и кнопка «Отозвать»: /devices в теме General.
Без строки можно так, установщик всё спросит:
curl -fsSL https://raw.githubusercontent.com/pockerhead/cctg/main/install.sh | shУстановщик ставит cctg в ~/.cctg, команду claude-cctg в ~/.local/bin и проверяет связь с hub. Ваши ~/.claude/settings.json и ~/.claude.json он не трогает. Если ~/.local/bin нет в PATH, он предлагает дописать его одной строкой в ~/.zshrc, ~/.bashrc или ~/.profile (по вашему shell; с --yes без вопроса, --uninstall строку убирает). В Docker-контейнере имя машины это id контейнера, поэтому установщик спрашивает имя для тем (или --host NAME).
Проверить связь потом: ~/.cctg/bin/cctg doctor.
В папке проекта вместо claude:
claude-cctgВсе аргументы claude работают так же (claude-cctg --resume, claude-cctg "почини тест"). Claude Code при старте спрашивает про development channels: выберите пункт 1. В Windows в обычной консоли (PowerShell, cmd, Windows Terminal), на Linux и macOS в терминале cctg нажимает за вас; в окне Git Bash (mintty) нет, поэтому сессии в Windows лучше запускать из PowerShell или cmd.
~/.cctg/bin/cctg sandbox on в папке проекта: сессии claude-cctg в ней и во вложенных папках не читают и не пишут ничего вне папки (команды, скрипты, которые пишет модель, файловые инструменты, send_file, ! и опасные слеш-команды из Telegram). Выключить: cctg sandbox off, состояние: cctg sandbox status. На Windows нужен один раз cctg sandbox-install (один UAC создаёт скрытые учётки сэндбокса). Что закрыто, что нет и что для этого нужно на машине: docs/sandbox.md.
- Тема
[машина] папка · название сессии. Иконка показывает, жива сессия, ждёт ли разрешения. - Ваши промпты, текст Claude, по строке на каждый вызов инструмента и финальный ответ хода.
- Запрос разрешения приходит с кнопками Allow и Deny.
- Закреплённое сообщение со статусом: модель, контекст, лимиты.
- Сообщение в теме уходит в сессию. Файлы и фото тоже.
- Голосовое hub в Docker распознаёт офлайн: в теме ответ «Голосовое сообщение распознано. Текст: …», сессия получает текст и файл (
docs/remote-hub.md, «Голосовые»). - В общей теме группы голосовое обращается к агенту подписью с
@имя_ботаили ответом на сообщение бота (не на его пост с текстом голосового); «бот, …», сказанное голосом, обращением не считается. /briefи/fullв теме присылают транскрипт сессии.- Сессия закрыта: сообщения ждут в теме (до 50) до следующего запуска в этой папке.
- Команда в одной группе: в
CCTG_ALLOWED_USER_IDSнесколько id через запятую, все равны. Тогда Claude видит, кто пишет (Имя: текст, username или имя из Telegram), а ответ на запрос разрешения подписан именем нажавшего. - Люди из меню (Threaded Mode бота: меню живёт в личке): id из
CCTG_ALLOWED_USER_IDSэто владельцы. Остальных владелец добавляет и удаляет во вкладке меню «👥 Люди», без рестарта hub и без правки.env: переслать в General лички с ботом любое сообщение человека (бот спросит подтверждение кнопками) или, если Telegram скрывает его аккаунт в пересылках, дать одноразовую ссылку-приглашение (10 минут): человек открывает её и нажимает Start, владелец подтверждает. Участник пишет сессиям и отвечает на вопросы и разрешения, как владелец, но не подключает и не отзывает устройства (/join,/devices) и не меняет список. Удаление (🗑, второе нажатие подтверждает) сразу закрывает доступ, удаляет его меню, отключает его устройства и сообщает ему об этом, но не убирает его из групп Telegram. Владельцев из меню удалить нельзя. Список хранится вregistry.jsonhub. - Личка с темами (Threaded Mode бота, шаг 5 выше): новая сессия получает тему только в личке владельца устройства; слот, у которого уже была тема в группе, остаётся и там, и группа повторяет личку. Показать сессию группе:
/shareв её теме в личке или кнопка «👥 В группу» на статусе там; в группе появляется тема, первая строка в ней── общий доступ: <имя> ──(имя и фамилия из Telegram того, кто нажал; username, только если имени нет); сообщения до share в тему не переносятся, но/briefи/fullв этой теме показывают всю сессию, включая ходы до share. Убрать:/unshareили «🙈 Убрать из группы» (кнопка просит второго нажатия); бот удаляет тему группы со всеми сообщениями, а без права удалять сообщения тема остаётся с пометкой и иконкой завершённой сессии и больше не обновляется. Делать это может только владелец, из своей лички:/shareв группе получает отказ. Общий доступ принадлежит слоту и сохраняется для следующих сессий в нём. Если личка недоступна (нет Start, бот заблокирован), новая сессия идёт в группу, как раньше. Владелец: первый id вCCTG_ALLOWED_USER_IDS; в команде тот, кто сделал/joinэтого устройства, а устройства на общем секрете и вступившие до этой версии принадлежат первому id. Писать и нажимать кнопки можно в любом из двух мест: сессия получает сообщение один раз, в другом месте оно появляется с подписью автора (✉ имя: текст); первое нажатие решает, оба сообщения правятся. У лички и группы свои лимиты: 429 в одном месте не тормозит другое. Если группа отстаёт (у неё около 20 запросов в минуту на всё), часть сообщений в ней пропускается, в личке есть всё. Без Start в личке бот пишет об этом в General группы, сессии идут только в группу, пока вы не нажмёте Start; после Start (или разблокировки) слот, попавший в группу только поэтому, снова живёт в одной личке, а его тема в группе получает прощальное сообщение и больше не обновляется. Запрос разрешения или вопрос, ждавший ответа в этой теме группы, приходит заново в личку (копия в группе теряет кнопки; запрос, на который уже ответили в терминале, не повторяется), а на сообщение, написанное в старую тему группы, бот отвечает тем же прощальным текстом (до перезапуска hub, потом такие сообщения молча не доходят). Откат на v0.1.15:docs/remote-hub.md, раздел «Обновление». - Общая тема в группе отвечает только на обращения:
@имя_ботав сообщении или подписи (регистр не важен) или ответ на сообщение бота. Остальные сообщения группы в сессию сразу не уходят и в личке не повторяются; группа один раз получает подсказку, как обратиться. С обращением сессия получает историю темы с прошлого обращения с авторами, затем само обращение. История длиннее лимита (2000, 4000 или 8000 символов, «👁 Показ» → «👥 Группа», по умолчанию 4000) сжимается на устройстве сессии запускомclaude -p --model haikuбез инструментов, хуков и MCP (он тратит лимит вашего аккаунта Claude и не создаёт темы); если сжать не вышло за минуту, остаются самые новые сообщения. Файлы в истории видны как[фото],[файл имя]и не скачиваются; альбом, в подписи которого есть обращение, уходит в сессию целиком (файлы альбома, попавшие в историю чужого обращения, второй раз не идут; после перезапуска hub посреди альбома его оставшиеся файлы остаются в истории). Если файл обращения не скачался, история и подпись всё равно доходят текстом, и бот просит прислать ещё раз только файл. Пока идёт сжатие, следующие сообщения этой сессии ждут. Кнопка 📣 в строке сессии меню переключает тему на «все сообщения», 💬 возвращает режим обращений. Сообщения из лички идут как раньше. - Меню в личке (Threaded Mode): с первой темой сессии в личке бот присылает в General лички меню и закрепляет его;
/startили/menuтам присылают его заново (старое и сама команда удаляются). Три вкладки. «Сессии»: живые и до 12 завершённых сессий ваших устройств с состоянием, кнопки ↗ (тема поднимается наверх списка), 👥/🙈 (в группу и из неё, как на статусе), ⏹ (прервать ход, второе нажатие подтверждает), 🔒/🔓 (сэндбокс папки сессии на её устройстве, только у владельца; 🔓 подтверждается вторым нажатием, сессия перезапускается сама,docs/sandbox.md) и «Обновить все клиенты». «Показ»: всё, кратко (без строк инструментов) или только ответы (и заметка о прерванном ходе), отдельно 💭 размышления, и ход полный или сжатый (строки инструментов и 💭 сообщения хода в свёрнутой цитате, она раскрывается нажатием); у сессии в группе свой показ: переключатель «👤 Личка / 👥 Группа» во вкладке, по умолчанию в группе всё. «Звук» сообщений в личке: ответы и запросы (как раньше), всё (каждое сообщение сессии; служебные уведомления тихие) или ничего («ничего» глушит и запросы разрешений); тихие часы по вашему поясу, в них звук только у запросов разрешений и вопросов. Пояс задаётся фиксированным смещением: летнее время не учитывается, при переходе укажите пояс заново. Звук в группе не меняется. Настройки у каждого свои и хранятся вregistry.jsonhub. Меню само не обновляется: ↻ показывает текущее состояние. У владельцев (CCTG_ALLOWED_USER_IDS) вторая строка вкладок: «👥 Люди» (ниже), «💻 Устройства» и «🛠 Hub». «Устройства»: все машины hub с владельцем и последним входом, 🗑 отзывает машину (второе нажатие подтверждает), «➕ Добавить устройство» присылает ниже строку установки с кодом, как/join, машина будет вашей. «Hub»: версия, время работы, число сессий и тем, 429 за сутки и уборка тем: выбрать возраст («сейчас» или 1, 7, 30, 90 дней), бот считает слоты и темы, 🧹 и второе нажатие удаляют темы слотов, завершённых раньше. Уборка общая на весь hub (темы всех людей, в личках и в группах), её делают только владельцы; слоты с сообщениями, ждущими Resume, и с открытым запросом или вопросом не трогаются; темы в группе без права удалять сообщения остаются как есть. Следующая сессия той же папки получает новую тему, а показ в группе у убранного слота пропадает: добавить сессию в группу нужно заново. Сообщение в личную тему убранного слота, пока она ждёт удаления и в папке ещё не было новой сессии, возвращает эту тему слоту; в остальных случаях (тема группы, папку уже заняла новая сессия) сообщение не передаётся, бот отвечает, что тему убрала уборка, и тема удаляется. Пока есть убранные слоты или темы в очереди на удаление, откат hub на v0.1.27 и старше нужен с правкойregistry.json:docs/remote-hub.md, раздел «Обновление». - Несколько групп (шаг 6): сессию из лички можно показать в любой подключённой группе и в нескольких сразу. Когда групп больше одной, кнопка «👥 Группы…» на статусе,
/shareи/unshareв теме сессии и кнопка👥 N…в меню присылают в тему сессии выбор: 👥 показать в группе, 🙈 убрать (с подтверждением, тема в этой группе удаляется). Режим упоминаний (📣/💬 в меню) один на сессию и действует во всех её группах; настройка показа «👥 Группа» тоже одна на все группы. Если сессия шла в основной группе только потому, что личка была недоступна, то после выбора основной группы её прежняя тема становится общей; пока выбор открыт (до 5 минут), в эту тему ничего не пишется, а если выбрана другая группа или время вышло, тема получает прощальное сообщение. Если hub перезапустился, пока выбор был открыт, прежняя тема остаётся как есть, без прощания, а выбор основной группы создаёт новую тему. У каждой группы, кроме основной, свой лимит сообщений Telegram и своя очередь сообщений в hub (ответы, поток хода, файлы, Resume): пауза после 429 в такой группе не задерживает ни личку, ни другие группы. Основная группа делит очередь hub с личками, как раньше. Если бота удалили из группы (в том числе из основной), сессии туда больше не пишут, пока его не вернут админом; это пауза, а не отмена показа: вернувшийся бот снова пишет в прежние темы. В других группах, кроме основной,/briefи/fullработают только в теме сессии, показанной в этой группе, и без id сессии: в General и по id чужие сессии там не показываются.
Команды выше берут установщик с main, он ставит последний релиз. Чтобы поставить конкретный релиз, замените main в ссылке на его тег (например v0.1.18).
- Клиент: после обновления hub работающие сессии получают версию hub по кнопке «⬆️ Обновить» в теме: клиент сам скачивает сборку того же релиза из GitHub Releases, сверяет
SHA256SUMSи ставит её на место старой. Конфиги и обёртку обновляет команда установки нового тега. - Hub: команда
--hubнового тега (hub стоит на образе своего релиза, поэтому одногоdocker compose pullмало).
curl -fsSL https://raw.githubusercontent.com/pockerhead/cctg/main/install.sh | sh -s -- --uninstallУдаляет только то, что поставил установщик. Hub: та же строка с --hub --uninstall вместо --uninstall (останавливает контейнер, hub.env, ключ и состояние остаются), hub на этой машине: --hub --local --uninstall (останавливает hub и убирает автозапуск, ~/.cctg/hub остаётся). Пока стоит локальный hub, --uninstall клиента оставляет бинарник: hub работает из него.