🔗 Подключение к платформе
📌 Описание
Демон полностью автономен: всё работает через CLI и локальный API, без учётной записи в платформе. Подключение к AdminService.Cloud необязательно и добавляет сверху панель управления, AI-ассистента, команды и роли.
Связь устанавливается одноразовым токеном регистрации, который выдаётся в панели. Токен можно передать сразу установщику либо позже — командой asc connect.
🎯 Сценарии
- Оператор копирует команду установки из панели и выполняет её на чистом сервере: демон ставится и регистрирует ноду за один шаг.
- Платформа сама заходит по SSH, ставит демон и передаёт тот же токен.
- Ноду, установленную раньше без платформы, подключают позже:
sudo asc connect <токен>. - Регистрация не прошла, потому что платформа была недоступна: демон всё равно работает локально, а оператор повторяет попытку через
asc connect.
🏗️ Техническое решение
Команда установки
curl -fsSL https://raw.githubusercontent.com/AdminServiceCloud/asc-daemon/main/install.sh \
| sudo bash -s -- --silent --token <ТОКЕН> --url https://adminservice.cloud| Опция | Значение |
|---|---|
--silent | Принять настройки по умолчанию и ничего не спрашивать — режим, который использует платформа. Docker при этом ставится: нода, которая не умеет запускать контейнерные приложения, — не рабочая нода |
--no-docker | Не устанавливать Docker даже с --silent |
--direct | Открыть API в сеть по TLS, чтобы платформа обращалась к нему без SSH-туннеля |
--token <ТОКЕН> | Одноразовый токен регистрации; привязывает ноду к выдавшей его организации |
--url <URL> | Базовый URL платформы, по умолчанию https://adminservice.cloud |
Принимаются обе формы: --token <значение> и --token=<значение>. Токен проверяется до использования и может содержать только буквы, цифры, - и _. Отдельный --url отклоняется: без токена регистрировать нечего.
install.sh пробрасывает опции в asc-updater install, который сначала устанавливает демон, а регистрирует уже после.
asc connect
sudo asc connect <ТОКЕН> [--url https://adminservice.cloud]Требует root: команда пишет файлы, принадлежащие root. Она сохраняет токен, запоминает URL платформы и вызывает endpoint регистрации — тот же путь, что и у установщика, но без переустановки. asc status показывает платформу и id ноды либо сообщает, что нода не подключена.
Где что хранится
| Что | Где | Доступ |
|---|---|---|
| Токен регистрации | /etc/asc/platform.token | 0600, только root |
| URL платформы, id ноды, время регистрации | секция [platform] в /etc/asc/config.toml | 0644 |
Разделение следует правилу, которое уже записано в самом конфиге: секреты никогда не лежат в config.toml — он намеренно доступен на чтение всем, чтобы любой пользователь мог посмотреть настройки демона.
[platform]
url = "https://adminservice.cloud"
node_id = "0e2f…"
registered_at = "2026-08-20T09:31:04Z"Запрос регистрации
Демон отправляет один запрос на публичный bootstrap-endpoint платформы:
POST <url>/bootstrap/asc.node.v1.BootstrapService/RegisterNode
Content-Type: application/json
{"token":"…","hostname":"…","primaryIp":"…","os":"…","arch":"x86_64","daemonVersion":"0.10.0"}Тело уходит через stdin, а не аргументом командной строки, поэтому токен не виден в выводе ps. Платформа гасит токен — он работает ровно один раз — и отвечает идентификаторами ноды и организации.
URL с http:// разрешён для локальной разработки, но выводится предупреждение: токен пойдёт по сети незашифрованным.
Обработка ошибок
Регистрация никогда не роняет установку. Если платформа недоступна, установщик печатает предупреждение и завершается успешно: демон установлен и работает локально, а asc connect повторит попытку в удобный момент.
Живость ноды
Платформа спрашивает статус демона вызовом asc.daemon.v1.DaemonService/GetStatus. Нода, чей демон ответил, показывается как online; не ответил — offline. Дойти до демона вызов может двумя путями.
Через SSH (по умолчанию). API остаётся на loopback, платформа пробрасывает запрос по уже открытому SSH-подключению. Наружу ничего не выставляется; токен /etc/asc/api.token платформа читает при установке.
Напрямую (--direct). API переезжает на 0.0.0.0 и поднимает TLS. Эти два изменения включаются только вместе: bearer-токен даёт полный контроль над машиной, поэтому открывать порт без шифрования нельзя. Сертификат самоподписанный — у нод нет собственного домена — и платформа закрепляет его SHA-256-отпечаток при регистрации, ровно как закрепляет SSH host key. Токен API демон передаёт платформе внутри вызова регистрации: без SSH другого способа доставить его нет.
Нода, установленная обычной командой без --direct, сообщить свой статус не может вовсе: у платформы нет ни SSH-доступа, ни endpoint'а для запроса.
[api]
listen = "0.0.0.0:8420"
tls = "self_signed" # off | self_signed | acme | files
# Имя, по которому платформа обращается вместо адреса, — нода переживёт смену
# IP. В режиме self_signed оно вшивается в сертификат как SAN.
# domain = "node.example.com"
# tls = "files" — использовать сертификат оператора:
# tls_cert = "/etc/asc/fullchain.pem"
# tls_key = "/etc/asc/privkey.pem"Сертификат и ключ лежат рядом с config.toml: api.crt (0644) и api.key (0600). Команды asc api tls и asc api listen записывают эти настройки и проверяют их до записи, поэтому неработоспособная конфигурация отклоняется там, где её вводят, а не при следующем запуске.
🔐 Чем подтверждается сертификат (DMN-067)
tls | Что ручается за сертификат |
|---|---|
self_signed | Ничто, поэтому платформа закрепляет его SHA-256-отпечаток при регистрации — тот же контракт, что и у SSH host key. |
acme | Публичный CA — когда демон научится получать сертификат сам. Пока недоступно: в этом режиме демон не запускается; тот же результат даёт files с сертификатом от certbot. |
files | Тот, кто выпустил установленный вами сертификат. Платформа проверяет цепочку и имя хоста; если демон при этом сообщает отпечаток — сертификат от приватного CA, — платформа закрепляет его. |
Отпечаток сообщается для self_signed и files. У files сертификат может быть от приватного CA (например, Cloudflare Origin), и тогда за него может поручиться только отпечаток; acme проверяется по цепочке — закрепление сломало бы ноду при первом же продлении.
🪪 Сертификат от платформы (DMN-127)
TokenService.SetApiCertificate(certificate_pem, private_key_pem, domain) — только основным токеном. Демон проверяет пару (сертификат разбирается, ключ совпадает), пишет её рядом с config.toml — api-custom.crt (0644) и api-custom.key (0600) — и переводит [api] на tls = "files" с этими путями (и доменом, если передан). Если слушатель уже работает по TLS, сертификат подменяется на лету: новые подключения получают его сразу, уже открытые (включая сам вызов) — нет. Ответ — отпечаток нового сертификата и restart_required, если слушатель сейчас без TLS. Capability — api.certificate.
📡 Сообщение об изменении после регистрации (DMN-068)
Регистрация — одноразовое погашение токена, поэтому сообщить о том, что адрес или сертификат изменились позже, она не может. Для этого демон вызывает BootstrapService.ReportNodeEndpoint — при старте, если вычисленный endpoint отличается от сообщённого в прошлый раз, и после asc api tls. Вызов аутентифицируется основным API-токеном ноды, единственным общим секретом сторон, и может изменить только то, как до ноды дозваниваться: ни организацию, ни удалённую ноду он не трогает.
Что было сообщено в прошлый раз, кэшируется в config.toml ([platform] reported_endpoint / reported_fingerprint), чтобы демон, перезапустившийся без изменений, не дёргал платформу при каждой загрузке.
Что пока не реализовано
Постоянного канала с платформой нет. Зарегистрированная нода один раз сообщает о себе и на этом останавливается: heartbeat не отправляется, и панель не показывает её как online. Команды из панели, стрим логов и метрики появятся вместе с туннелем платформы (задача NODE-002 на стороне платформы).
🔗 Связанные задачи
DMN-058, DMN-127 (сертификат от платформы), DMN-129 (API через веб-сервер ноды) в ROADMAP; на стороне платформы — NODE-001 (реестр нод и токены регистрации) и NODE-002 (канал платформа ↔ демон).