Переход на NixOS

Полгода я сидел на Арче, и в конце июня одно обновление остановило моё сердце на вечер. Что вообще случилось? Сидел я, значит, вечером, решил апдейтнуть систему, и после обновления перестала подниматься Амнезия. Это произошло из-за того, что обновилось ядро, несовместимое с версией amneziawg.

Решил поставить чистый WireGuard, но не смог его нормально настроить, вследствие чего сломал инет. Пришлось откатиться. У меня был настроенный snapper, но уже не помню, по какой причине не получилось откатиться. Решил восстановиться с бэкапа (на флешке у меня были zstd-архивы корня и хомяка). Получилось распаковать только корень. У хомяка повредилась структура при записи на флешку, поэтому через утилиту распаковать не удалось. Забросил.

Начал думать, какой выбрать надёжный дистрибутив. Хотел Федору, но не захотел с Арча переходить на что-то простое. Стал выбирать из сложных… т.к. я девопс (сторонник декларативного подхода), мне приглянулся Никс. Запал тот факт, что можно управлять системой одним конфигом. Поставил. Решил разобраться с тем хомяком. Вытащил оттуда ~9,5 ГБ, но все конфиги Hyprland были написаны только наполовину (вторая половина содержимого стёрлась). Удалил хомяка, чтобы не занимал место. Конфиги Hyprland решил написать сам, не брать с инета.

В общем, получилось по красоте настроить весь визуал:

Амнезию настроил с первого раза за несколько минут. На Арче я настраивал Амнезию далеко не с первого раза + у меня ещё тогда не было опыта. В Никсе это просто строчек 10-15 в один конфиг и всё:

  systemd.services.amneziawg = {
    description = "AmneziaWG VPN Tunnel";
    after = ["network.target"];
    wantedBy = ["multi-user.target"];
    path = with pkgs; [amneziawg-tools amneziawg-go iproute2 bash iptables];
    environment = {
      WG_QUICK_USERSPACE_IMPLEMENTATION = "amneziawg-go";
    };
    serviceConfig = {
      Type = "oneshot";
      RemainAfterExit = "yes";
      ExecStart = "${pkgs.amneziawg-tools}/bin/awg-quick up awg0";
      ExecStop = "${pkgs.amneziawg-tools}/bin/awg-quick down awg0";
    };
  };

Настройка OpenClaw

Сначала решил попробовать локально через ollama. У меня Ryzen 5 1400, RX560 (4 ГБ) и ОЗУ (16 ГБ). Начал с лёгкой модели llama3.2:1b - отвечает почти без задержки, но несёт всякую чушь (просто поздоровался с ней на русском языке, а она мне прислала какой-то огромный китайский текст), потом протестировал qwen2.5:7b - но там уже комп просто завис. Так что от локального решения пришлось отказаться.

Начал искать бесплатную апишку. Нашёл через одного ютубера и вышел на сайт Z.ai. Там увидел две бесплатные модельки: GLM-4.7-Flash и вроде GLM-4.5-Flash. Выбрал которую посовременнее. Контекста 200к токенов, для коротких диалогов вполне достаточно. Минус только в очереди, в пиковые часы иногда приходится ждать ответа минут 10-15.

На Арче настроить OpenClaw было проще простого. У меня тогда это занимало примерно полчаса, а здесь… подольше. Ну, на самом деле я на Никсе хотел сделать не просто рабочего ассистента, а модернизированного. Начал искать в инете, как можно улучшить OpenClaw. И нашел, что можно внедрить в него долгосрочную память, чем я и занялся.

На Никсе через nix-openclaw всё описывается одним конфигом:

{ openclaw-workspace, ... }: {
  programs.openclaw = {
    enable = true;
    workspace.bootstrapFiles = {
      agents = "${openclaw-workspace}/AGENTS.md";
      soul = "${openclaw-workspace}/SOUL.md";
      tools = "${openclaw-workspace}/TOOLS.md";
      identity = "${openclaw-workspace}/IDENTITY.md";
      user = "${openclaw-workspace}/USER.md";
    };

    environment = {
      ZHIPU_API_KEY = "/run/agenix/openclaw-zhipu-key";
      OPENCLAW_GATEWAY_TOKEN = "/run/agenix/openclaw-gateway-token";
    };

    config = {
      gateway = { mode = "local"; };
      tools.elevated = {
        enabled = true;
        allowFrom = { telegram = [5748618304]; };
      };
      channels.telegram = {
        tokenFile = "/run/agenix/openclaw-telegram-token";
        allowFrom = [5748618304];
      };
      models.providers.openai = {
        baseUrl = "https://open.bigmodel.cn/api/paas/v4";
        apiKey = { source = "env"; provider = "default"; id = "ZHIPU_API_KEY"; };
        models = [{
          name = "glm-4.7-flash";
          id = "glm-4.7-flash";
          api = "openai-completions";
          contextWindow = 200000;
        }];
      };

А память и настройки самого агента отдельно:

      memory.backend = "qmd";
      agents.defaults = {
        model.primary = "openai/glm-4.7-flash";
        thinkingDefault = "low";
        compaction.reserveTokensFloor = 20000;
      };
    };
  };
}

На Арче это собиралось руками: ставил пакет, создавал конфиги, руками писал systemd-юнит для gateway, настраивал переменные окружения. Тут просто включил модуль и готово. Когда первый раз увидел конфиг модуля - не понял, куда смотреть. Оказалось, он состоит из трёх частей: overlay, home-manager module и systemd user service. Из полезных плагинов: summarize (сжимает историю) и peekaboo (показывает, что агент видит в браузере).

Самое первое, что я проверил после настройки - телеграм-бота. Гейтвей подключился к Bot API. Написал задачу, агент выполнил на сервере, прислал результат. Elevated tools - файловая система и команды.

Но перед тем, как телеграм-бот заработал, надо было решить, где хранить токен (не в гите же). Наткнулся на agenix. Теперь ключи у меня зашифрованы прямо в гите и никаких .env, которые можно было случайно запушить (проходил это на Арче). agenix шифрует age-ключом, расшифровывается только при сборке. Выглядит как список файлов, привязанных к системе:

"secrets/openclaw-telegram-token.age".publicKeys = [ system ];
"secrets/openclaw-zhipu-key.age".publicKeys = [ system ];
"secrets/openclaw-gateway-token.age".publicKeys = [ system ];
"secrets/amneziawg-awg0.age".publicKeys = [ system ];

В users.nix для каждого прописывается owner, group, mode. На системе файлы лежат в /run/agenix/. Read-only, только владелец, подправить на лету нельзя - только пересобрать. Дальше я задумался: ассистент должен знать, кто он и что может. Для этого придумали bootstrap-файлы. Пять файлов в ~/.config/openclaw/, загружаются в контекст при старте gateway. Вот как это выглядит по файлам:

  • AGENTS.md (инструкции) - общайся со мной на русском языке, перемещайся по файлам и запускай команды. PolKit у меня настроен для управления питанием;

  • SOUL.md (характер) - неформальный и технический;

  • TOOLS.md (список доступного) - bash, read/write, glob/grep. Модель сама решает, что из этого применить;

  • IDENTITY.md (кто он) - персональный ассистент;

  • USER.md (мои данные) - имя, страна, город…

В итоге получилось настроить OpenClaw на Никсе лучше, чем на Арче. На Арче не хватало рук обновить конфиги. А тут всё в репозитории, сделал nixos-rebuild switch и готово. Теперь вся моя система крутится вокруг gateway. Режим local означает, что всё на локальной машине. Гейтвей запускается через systemd user service (сообщество советует, хоть и не хотел сначала, т.к. не люблю, когда в фоне запущено то, что тебе не нужно сейчас):

Память - qmd (маркдаун-файлы с метаданными: дата, модель, токены). При старте gateway подгружает историю, ну и контекст не теряется:

В общем, сравнивать Арч и Никс не совсем честно, т.к. на Арче я всё делал впервые, а на Никсе уже был опыт. Но факт есть факт. Амнезия убила вечер из-за ядра, тут 15 строк. OpenClaw настраивал полчаса руками, а тут programs.openclaw.enable.

Мой конфиг