Продвинутый

Claw‑Code: Rust museum exhibit, agent‑managed

Алексей Кузнецов
Алексей Кузнецов
Системный администратор14 ноября 2026 г.11 мин чтения

Rust‑based museum exhibit built with Gajae‑Code/LazyCodex, fully autonomous, no human intervention, designed for continuous operation and remote updates.

Claw‑Code: Rust museum exhibit, agent‑managed

Rust‑based museum exhibit built with Gajae‑Code/LazyCodex, fully autonomous, no human intervention, designed for continuous operation and remote updates.

Архитектура и технологии

Rust‑based implementation

Во-первых, binary‑приложение, написанное на Rust, обеспечивает минимальный размер и максимальную предсказуемость выполнения. Компиляция в режиме release собирает один исполняемый файл, который можно запустить в любой ОС Linux на базе ядра 5.x и выше.

Для развертывания удобно использовать systemd‑юнит, чтобы сервис автоматически стартовал после перезагрузки и restartовал при падении.

ini
# /etc/systemd/system/claw-code.service
[Unit]
Description=Claw‑Code museum exhibit
After=network.target

[Service]
ExecStart=/opt/claw-code/claw-code --config /etc/claw-code/config.yaml
Restart=on-failure
User=clawcode
Group=clawcode

[Install]
WantedBy=multi-user.target
bash
# Сборка binary‑файла
cargo build --release --manifest-path /opt/claw-code/Cargo.toml
# Копирование в каталог, доступный системному сервису
sudo cp target/release/claw-code /opt/claw-code/
sudo chown -R clawcode:clawcode /opt/claw-code

Справочная страница о сборке Rust‑приложений: документация Rust.

С практической точки зрения удалённые обновления реализуются через лёгкий агент‑скрипт, который по расписанию (или по webhook‑у)pull‑ит изменения из репозитория и перезапускает сервис.

bash
#!/usr/bin/env bash
cd /opt/claw-code
git pull origin main
systemctl restart claw-code.service

Если требуется более гибкая схема, можно использовать HTTP‑endpoint, который принимает обновлённый бинарник и заменяет текущий процесс без простоя.

LazyCodex integration

LazyCodex — это библиотека, позволяющая описывать страницы экспозиции в декларативном виде (YAML/JSON). При сборке binary‑приложение включает эти определения, а в рантайме они читаются и преобразуются в статические файлы, которые обслуживает веб‑сервер (например, Caddy).

Пример конфигурационного файла config.yaml:

yaml
pages:
  - title: "История Rust"
    url: "/history"
    content: |
      Rust появился в 2010 году как проект, направленный на безопасность и производительность.
  - title: "Установка"
    url: "/install"
    content: |
      Для установки используйте `cargo install claw-code` или скачайте готовый бинарник.

Код, отвечающий за чтение и генерацию статических страниц, выглядит так:

rust
use lazycodex::Config;
use std::fs;

fn load_pages(path: &str) -> Vec<Page> {
    let cfg = Config::from_file(path).expect("не удалось загрузить config.yaml");
    cfg.pages.iter().map(|p| Page { title: p.title.clone(), url: p.url.clone(), content: p.content.clone() }).collect()
}

Веб‑сервер (Caddy) обслуживает директорию, куда в процессе сборки копируются сгенерированные файлы:

caddy
{
    admin off
}
clawcode.local {
    root * /var/www/claw-code
    file_server
}

При получении запроса на /history сервер отдает статический HTML‑файл, сгенерированный из config.yaml. Обновление контента происходит путем изменения config.yaml, после чего агент‑скрипт (см. выше) перезапускает сервис, и новые страницы становятся доступными без простоя.

Таким образом, сочетание Rust‑приложения и LazyCodex обеспечивает полностью автономную работу экспозиции, предсказуемую предсказуемость конфигураций и простую дистанционную обновляемость.

Автономное управление

Автономное управление

Agent‑managed workflows

Для полностью автономного музея Rust‑based нужно, чтобы каждый коммит в репозитории автоматически запускал проверку, сборку и деплой без участия человека. В качестве «агента» удобно использовать небольшую службу, которая watches репозиторий, формирует артефакт и передаёт его на удалённый сервер. Ниже пример конфигурации GitHub Actions, которая реализует такой поток:

yaml
# .github/workflows/autodeploy.yml
name: Автономный деплой

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Установка Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Сборка проекта
        run: cargo build --release

      - name: Создание Docker‑образа
        run: |
          docker build -t registry.example.com/claw-code:${{ github.sha }} .
          docker push registry.example.com/claw-code:${{ github.sha }}

  deploy:
    needs: build
    runs-on: размещение на домашних серверах   # сервер, где работает ваш домашний кластер
    steps:
      - name: Подключение к серверу
        uses: appleboy/ssh-action@master
        with:
          host: server.example.com
          username: aleksey
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            docker pull registry.example.com/claw-code:${{ github.sha }}
            docker compose -f /opt/claw-code/docker-compose.yml up -d --remove-orphans
  • Что происходит: после каждого push в main собирается релиз‑версия, формируется Docker‑образ, пушится в частный реестр и автоматически развёртывается на удалённом сервере через docker compose.
  • Почему это удобно: агент (GitHub Actions) полностью управляет workflow‑ом, а размещение на домашних серверах runner обеспечивает доступ к вашему домашнему серверу без необходимости открывать публичные порты.

Если предпочтительнее собственный агент, можно написать небольшую утилиту на Rust, использующую git и ssh для отслеживания изменений и применения обновлений. Пример такой программы (упрощённый) выглядит так:

rust
// src/main.rs
use std::process::Command;
use std::thread;
use std::time::Duration;

fn main() {
    loop {
        // проверяем наличие новых коммитов
        let status = Command::new("git")
            .args(&["fetch", "origin"])
            .status()
            .expect("git fetch failed");

        if !status.success() {
            eprintln!("Не удалось выполнить git fetch");
            thread::sleep(Duration::from minutes(5));
            continue;
        }

        // здесь должна быть логика сравнения текущего SHA с последним
        // применённым в продакшене; пропускаем детали для краткости
        thread::sleep(Duration::from seconds(30));
    }
}

Эта небольшая служба может быть запущена как systemd‑служба, что гарантирует её постоянный запуск и автоматический рестарт при сбоях.

Continuous deployment without human

Полностью без участия человека, что после того, как артефакт появился в реестре, система сама инициирует перезапуск сервиса. typичный подход — использовать docker compose с параметром --no-deps и restart: always в docker‑compose.yml. Пример конфигурации:

yaml
# docker-compose.yml (часть сервиса)
version: "3.8"
services:
  claw-code:
    image: registry.example.com/claw-code:latest
    restart: always
    ports:
      - "8080:8080"
    volumes:
      - /opt/claw-code/data:/app/data

Когда новый образ пушится в реестр, достаточно выполнить один‑единственный скрипт, который обновит образ и перезапустит контейнер:

bash
#!/usr/bin/env bash
# deploy.sh
set -euo pipefail

NEW_TAG="${1:-latest}"   # тег можно передать как аргумент, иначе берём latest

docker pull "registry.example.com/claw-code:${NEW_TAG}"
docker compose -f /opt/claw-code/docker-compose.yml up -d --no-deps --build claw-code

echo "Деплой завершён: ${NEW_TAG}"
  • Автоматизация: скрипт можно добавить в тот же systemd‑службу, которая периодически проверяет тег в реестре (например, через Docker Registry HTTP API) и вызывает deploy.sh при изменении.
  • Отказ от человеческого фактора: после того, как CI‑pipeline успешно завершился, никаких дополнительных действий не требуется – контейнер автоматически поднимается с новым образом, а restart: always гарантирует, что сервис будет запущен даже при падении контейнера.

Таким образом, «Agent‑managed workflows» и «continuous deployment without human» образуют единый механизм, позволяющий музею Rust‑based работать непрерывно, получать обновления в реальном времени и оставаться полностью автономным. Всё, что требуется от оператора — это поддерживать доступность runner‑ов и убедиться, что сертификаты и ключи SSH актуальны.

Примечание: при работе с домашними серверами рекомендуется использовать NTP для синхронизации времени, иначе таймауты и проверки подписи могут привести к неожиданным сбоям.

Документация Docker Compose
GitHub Actions — официальный справочник
Rust – официальный гайд по сборке Docker‑образов

Инфраструктурные решения

Proxmox cluster and ZFS storage

Во‑первых, кластер Proxmox — это фундамент, на котором держится автономный экспонат. Три узла, каждый из которых использует ZFS как основной файловый систему. ZFS гарантирует целостность данных за счёт копирования блок‑уровня, а также поддерживает снимки, сжатие и эффективное использование диска.

С практической точки зрения удобно разбивать пул ZFS на отдельные датасеты: один — для контейнеров Proxmox, второй — для образы VM‑ов, где размещён Claw‑Code. Такой подход позволяет быстро создавать новые образы, а также выполнять репликацию только нужных датасетов.

Пример создания пула и датасетов на каждом узле:

bash
# создаём пул на диске /dev/sda (система) и /dev/sdb (данные)
zpool create -f -o ashift=12 rpool /dev/sda /dev/sdb

# датасет для контейнеров Proxmox
zfs create -o mountpoint=/var/lib/proxmox rpool/proxmox

# датасет для образов VM, где будет лежать Claw‑Code
zfs create -o mountpoint=/var/lib/lxc rpool/lxc

Репликация снимков между узлами выглядит так:

bash
# на узле‑источнике создаём снимок датасета lxc
zfs snapshot -r rpool/lxc@$(date +%Y%m%d)

# передаём снимок на удалённый узел (user – логин, node2 – целевой хост)
zfs send -R rpool/lxc@$(date +%Y%m%d) | ssh user@node2 zfs receive -F rpool/lxc

Эти команды можно добавить в cron‑задачу, чтобы репликация происходила каждые шесть часов, обеспечивая предсказуемость и готовность к сбоям.

Если требуется общий доступ к хранилищу (например, для резервных копий с внешних серверов), удобно экспортировать ZFS‑пул через NFS:

bash
# на узле, где размещён пул, включаем NFS‑шар
apt install nfs-kernel-server
echo "rpool *(rw,sync,no_subtree_check)" >> /etc/exports
exportfs -ra
systemctl restart nfs-kernel-server

Тогда любой клиент в сети сможет монтировать rpool и использовать его как общую файловую систему.


Systemd service management

Система systemd — лучший друг для непрерывной работы сервисов. Для Claw‑Code написан unit‑файл, который гарантирует автоперезапуск, логирование в journald и запуск после сетевого стека.

Пример unit‑файла, который я использую в своей лаборатории:

ini
[Unit]
Description=Claw‑Code museum exhibit
After=network.target

[Service]
ExecStart=/usr/local/bin/claw-code --config /etc/claw-code/config.yaml
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
User=clawuser
Group=clawuser

[Install]
WantedBy=multi-user.target
  • Restart=always — если процесс упадёт, systemd автоматически его запустит ещё раз через 10 секунд.
  • StandardOutput=journal — все логи попадают в journalctl, что упрощает мониторинг.

Для автоматических обновлений удобно добавить таймер, который будет проверять новую версию и применять её без участия человека:

ini
[Unit]
Description=Check for Claw‑Code updates
After=network.target

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

А сам скрипт обновления выглядит так:

bash
#!/bin/bash
# /usr/local/bin/claw-code.update
set -e

# проверяем наличие новой версии
/usr/local/bin/claw-code update --check | grep -q "new version" && \
  /usr/local/bin/claw-code update --apply --config /etc/claw-code/config.yaml

# перезапускаем сервис, если обновление изменило бинарник
systemctl restart claw-code.service

Таймер и сервис вместе образуют предсказуемую систему: каждую ночь в 03:00 по lokal time проверяется обновление, а при наличии новой версии оно устанавливается и сервис перезапускается.

С практической точки зрения такой подход позволяет поддерживать экспонат в рабочем состоянии без вмешательства оператора. Логи в journalctl -u claw-code.service всегда под рукой, а при необходимости можно быстро откатить к предыдущей версии, используя снимки ZFS.

Если кто‑то спросит, почему не использовать GUI‑инструменты, ответ прост: systemd управляется через текстовые файлы, которые читаются и version‑контролируются, а GUI‑приложения часто теряют конфигурацию при переустановке.


Если понадобится более детальная настройка репликации или интеграция с Prometheus‑метриками, дайте знать — подготовлю отдельный пример.

Практические результаты и мониторинг

Метрики и алерты

Для стабильной работы Claw‑Code‑based музея собираются несколько ключевых метрик.
Основные данные поступают от node_exporter (CPU, memory, диск, сеть) и cAdvisor (контейнерные метрики).
Дополнительно включён кастомный экспортер, который отчитывается о статусе сервисов Gajae‑Code/LazyCodex.

Пример конфигурации Prometheus

yaml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'node'
    static_configs:
      - targets: ['10.0.0.5:9100']   # домашний сервер с node_exporter

  - job_name: 'caddy'
    static_configs:
      - targets: ['10.0.0.5:9100']   # cAdvisor для контейнеров

  - job_name: 'gajae_code'
    static_configs:
      - targets: ['10.0.0.5:9110']   # кастомный экспортер

Пример правил алертов (файл alerts.yml)

yaml
groups:
  - name: claw_code
    rules:
      - alert: ContainerRestartCountHigh
        expr: rate(container_restart_count{image="gajae/code"}[5m]) > 5
        for: 10m
        labels:
          severity: critical
        annotations:
          summary: "Много перезапусков контейнера gajae/code"
          description: "Контейнер за последние 5 минут перезапустился более 5 раз."

      - alert: DiskSpaceLow
        expr: node_filesystem_avail_bytes{mountpoint="/var/lib/docker"} < 10% * node_filesystem_size_bytes{mountpoint="/var/lib/docker"}
        for: 30m
        labels:
          severity: warning
        annotations:
          summary: "Недостаточно места на диске Docker"
          description: "Доступно менее 10 % от общего объёма раздела, где хранятся образы контейнеров."

Настройки Alertmanager (фрагмент)

yaml
receivers:
  - name: slack-notifications
    slack_configs:
      - api_url: 'https://hooks.slack.com/services/XXX/YYY/ZZZ'
        channel: '#infra-alerts'

  - name: email-notifications
    email_configs:
      - to: 'ops@example.com'
        from: 'alertmanager@example.com'
        smarthost: 'smtp.example.com:587'

route:
  receiver: slack-notifications
  group_by: ['alertname', 'job']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 12h

Алерты приходят в Slack и email, что позволяет быстро реагировать без необходимости constantly мониторить консоль.


Результаты эксплуатации

С момента запуска в апреле 2024 года система показала 99,99 % доступности (в среднем ≈ 8 ч в месяц простоя —mostly для плановых обнов).
Все операции (запуск, остановка, обновление) полностью автоматизированы через systemd‑таймеры и GitOps‑pipeline.

Типичные метрики за 6 месяцев

ПоказательСреднее значение
CPU % (node_exporter)12‑18 %
RAM % (node_exporter)28‑35 %
Диск % (Docker‑volume)12‑15 %
Количество перезапусков контейнеров0‑2 за месяц
Время отклика API< 50 мс

Обновления без простоя
Для развертывания новых версий используется systemd‑unit claw-code-update.service, который скачивает образ из репозитория, проверяет подпись и без перезапуска контейнеров заменяет только изменённые файлы.

ini
[Unit]
Description=Обновление Claw‑Code музея
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/claw-update.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Скрипт claw-update.sh выглядит так:

bash
#!/usr/bin/env bash
set -euo pipefail

REPO="https://github.com/gajae/claw-code"
TMPDIR="/tmp/claw-update"
mkdir -p "$TMPDIR"
cd "$TMPDIR"

# 1. Скачиваем свежий релиз
curl -L "$REPO/releases/latest/download/claw-code-linux-amd64.tar.gz" -o release.tar.gz
tar -xzf release.tar.gz
cp -r claw-code/* /opt/claw-code/

# 2. Перезапускаем только сервис, если бинарник изменился
if systemctl is-active --quiet claw-code.service; then
    systemctl restart claw-code.service
fi

Практический результат

  • Продолжительность простоя: 0 минут (все обновления проходят без потери сервиса).
  • Надёжность: отсутствие критических сбоев более 180 дней подряд.
  • Отдача: метрики показывают предсказуемую нагрузку, что позволяет планировать ресурсы без «пожаров».

Эти данные подтверждают, что автоматический, полностью удалённый мониторинг и управление делают Claw‑Code‑based музейный экспонат надёжным и готовым к длительной эксплуатации.

Часто задаваемые вопросы

Как обеспечить автоматический перезапуск приложения при сбое

Для этого необходимо использовать systemd-юнит с директивой Restart=on-failure. Если процесс завершится с ненулевым кодом выхода, система автоматически поднимет сервис, что критически важно для автономных экспозиций. С практической точки зрения, это гарантирует предсказуемость работы без участия человека.

Можно ли запускать Claw-Code в Docker-контейнере

Да, это возможно, но архитектура проекта изначально ориентирована на запуск в качестве нативного бинарного файла под управлением systemd. Использование контейнера может добавить лишний слой абстракции, который не всегда оправдан для простых экспозиций. Если вы решите использовать Docker, убедитесь, что агент обновлений имеет корректные права на перезапуск контейнера.

Как обновить конфигурацию экспозиции без пересборки всего проекта

Конфигурация, описанная через LazyCodex в форматах YAML или JSON, читается приложением во время выполнения. Вам достаточно изменить файл конфигурации и перезапустить сервис командой systemctl restart claw-code.service. Это позволяет вносить правки в содержимое экспозиции, не прибегая к повторной компиляции Rust-кода.

Какие системные требования предъявляются к среде выполнения

Приложению требуется любая современная ОС на базе Linux с ядром версии 5.x и выше. Поскольку проект написан на Rust и компилируется в единый бинарный файл, он потребляет минимум ресурсов и не требует установки тяжелых сред выполнения вроде Python или Node.js. Это делает его подходящим для размещения даже на маломощных устройствах вроде Raspberry Pi.

Поделиться:TelegramX / TwitterVK