363 lines
27 KiB
Markdown
363 lines
27 KiB
Markdown
---
|
||
title: Hermes Docker on Kraken
|
||
created: '2026-05-23'
|
||
updated: '2026-09-23'
|
||
type: tech
|
||
namespace: personal
|
||
tags: [hermes, docker, kraken, infra, how-to]
|
||
status: active — SSH контейнер→хост РАБОТАЕТ (фикс uid 1000 применён 2026-09-23)
|
||
related:
|
||
- "[[tech/kraken-network]]"
|
||
- "[[concepts/hermes-deployment-patterns]]"
|
||
- "[[tech/hermes-kraken-api]]"
|
||
- "[[family/how-to/kraken-access]]"
|
||
- "[[personal/tech/docker-data-root-mount-race]]"
|
||
---
|
||
|
||
# Hermes Docker on Kraken
|
||
|
||
## Key Rule
|
||
|
||
Hermes runs via **`docker compose`** from the `hermes-build/` directory.
|
||
|
||
## Directory Layout (on Kraken)
|
||
|
||
```
|
||
/srv/.../docker/
|
||
├── hermes-build/ ← build + run from here
|
||
│ ├── Dockerfile ← clones upstream NousResearch/hermes-agent
|
||
│ ├── docker-compose.yml ← canonical compose file
|
||
│ ├── config.yaml ← unused at build time (runtime config is in hermes/config/)
|
||
│ └── .env ← all secrets/API keys (sourced by compose)
|
||
└── hermes/
|
||
└── config/ ← mounted as /opt/data (HERMES_HOME) inside container
|
||
```
|
||
|
||
## Compose File (`hermes-build/docker-compose.yml`)
|
||
|
||
```yaml
|
||
services:
|
||
kraken:
|
||
image: ghcr.io/mallexxx/hermes-kraken:latest # ← ГОТОВЫЙ образ, не локальная сборка
|
||
container_name: hermes-kraken
|
||
stdin_open: true
|
||
tty: true
|
||
restart: unless-stopped
|
||
user: "1000:1000" # ← добавлено 2026-09-23 (было неявно из USER образа)
|
||
volumes:
|
||
- /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker/hermes/config:/opt/data
|
||
- /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker/hermes/config/.ssh:/opt/data/.ssh # ← было /root/.ssh
|
||
- /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker/hermes/config/etc-passwd:/etc/passwd:ro
|
||
- /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker/hermes/config/etc-group:/etc/group:ro
|
||
- /home/kraken/obsidian:/vault:rw
|
||
environment:
|
||
- HERMES_ALLOW_ROOT_GATEWAY=1
|
||
env_file:
|
||
- .env
|
||
command: gateway
|
||
network_mode: host
|
||
```
|
||
|
||
> ✅ **Обновлено 2026-09-23 — фикс применён, SSH работает.** Полный разбор — в разделе
|
||
> «✅ SSH контейнер → хост починен 2026-09-23» ниже. Бэкап исходных файлов:
|
||
> `/home/kraken/backups/hermes-uid1000-20260923-123803/`.
|
||
>
|
||
> ⚠️ Ранее в доке был описан вариант с `build:` — **это устарело**. Сейчас compose тянет **готовый образ**
|
||
> `ghcr.io/mallexxx/hermes-kraken:latest` (собранный из `mallexxx/hermes-agent`, не upstream NousResearch).
|
||
> uid **1000** приходит из `USER` в образе; с 2026-09-23 он продублирован в compose явно.
|
||
|
||
## Managing the Container
|
||
|
||
```bash
|
||
cd /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker/hermes-build
|
||
|
||
docker compose up -d # start
|
||
docker compose down # stop
|
||
docker compose pull # pull new image (if from registry)
|
||
docker compose build # rebuild image from Dockerfile
|
||
docker compose restart # restart
|
||
docker compose logs -f # follow logs
|
||
```
|
||
|
||
## Image
|
||
|
||
⚠️ **ОБНОВЛЕНО 2026-09-23 — прежнее описание было неверным.**
|
||
|
||
Фактически compose использует готовый образ **`ghcr.io/mallexxx/hermes-kraken:latest`**
|
||
(`image:`, без `build:`). Локальный `hermes-build/Dockerfile` в репозитории **есть** и в нём прописан
|
||
`git clone --depth=1 https://github.com/mallexxx/hermes-agent.git` — это **форк Alex**, а НЕ upstream
|
||
`NousResearch/hermes-agent`, как утверждалось ранее. То есть «not a custom fork» — неверно.
|
||
|
||
**Локальный `Dockerfile` (актуальное содержимое, ключевое):**
|
||
```dockerfile
|
||
FROM ghcr.io/astral-sh/uv:0.11.6-python3.13-trixie AS uv_source
|
||
FROM tianon/gosu:1.19-trixie AS gosu_source
|
||
FROM debian:13.4 # Trixie, не Bookworm
|
||
ENV HERMES_HOME=/opt/data
|
||
RUN useradd -u 10000 -m -d /opt/data hermes # ← единственный пользователь в образе = uid 10000
|
||
...
|
||
ENTRYPOINT [ "/usr/bin/tini", "-g", "--", "/opt/hermes/docker/entrypoint.sh" ]
|
||
```
|
||
В этом Dockerfile **нет** `USER` — значит uid 1000 приходит из **ghcr-сборки**, а файл в
|
||
`hermes-build/` её не воспроизводит (расхождение образ ↔ Dockerfile). **Это надо развести перед
|
||
правкой** — иначе правка Dockerfile не попадёт в работающий контейнер.
|
||
|
||
🔴 **Открытый вопрос (блокирует шаг 2 плана):** где собирается `ghcr.io/mallexxx/hermes-kraken:latest`
|
||
— CI в репозитории `mallexxx/hermes-agent`, или локально на Kraken с `docker push`? Пока не выяснено,
|
||
`docker compose pull` может перезаписать любой локальный фикс.
|
||
|
||
Обновление Hermes: либо `docker compose pull && up -d` (если образ из GHCR), либо
|
||
`docker compose build --no-cache && up -d` (если собирается локально).
|
||
|
||
## Volumes
|
||
|
||
| Host path | Container path | Purpose |
|
||
|-----------|----------------|---------|
|
||
| `hermes/config/` | `/opt/data` | `HERMES_HOME` — config, sessions, skills, memory |
|
||
| `hermes/config/.ssh/` | `/opt/data/.ssh` | ✅ SSH-ключи — **перевешено 2026-09-23** (было `/root/.ssh`, при `HOME=/opt/data` не читалось) |
|
||
| `hermes/config/etc-passwd` | `/etc/passwd` (ro) | ✅ создан 2026-09-23 — запись `kraken:x:1000` для uid контейнера |
|
||
| `hermes/config/etc-group` | `/etc/group` (ro) | ✅ создан 2026-09-23 — `kraken:x:1000:` |
|
||
| `/home/kraken/obsidian` | `/vault` | Obsidian vault read/write access |
|
||
|
||
> ℹ️ `etc-passwd`/`etc-group` — **статичные снимки** файлов образа + одна добавленная строка.
|
||
> При смене базового образа (новые uid) их надо пересоздать:
|
||
> ```bash
|
||
> cd /srv/dev-disk-by-uuid-49e8f586-.../docker/hermes/config
|
||
> sudo docker exec hermes-kraken cat /etc/passwd > /tmp/p && printf 'kraken:x:1000:1000::/opt/data:/bin/sh\n' >> /tmp/p && sudo cp /tmp/p etc-passwd
|
||
> ```
|
||
|
||
## SSH из контейнера → хост (диагностика и фиксы)
|
||
|
||
> Перенесено из удалённого скилла `kraken-omv-maintenance` / `kraken-docker-ssh`. Актуальные пути и фиксы для случая, когда `ssh kraken-host` изнутри контейнера `hermes-kraken` не работает.
|
||
|
||
**Архитектура (УТОЧНЕНО 2026-09-23 живой проверкой)**
|
||
|
||
```
|
||
Container (hermes-kraken) ← состояние ПОСЛЕ фикса 2026-09-23
|
||
uid=1000 gid=1000 ← задан явно в compose (user: "1000:1000")
|
||
HOME=/opt/data
|
||
/etc/passwd — bind-mount из config/etc-passwd:
|
||
hermes:x:10000:10000::/opt/data:/bin/sh
|
||
kraken:x:1000:1000::/opt/data:/bin/sh ← ✅ добавлено 2026-09-23
|
||
config/.ssh → /opt/data/.ssh ← ✅ перевешено с /root/.ssh
|
||
/opt/data/.ssh/ — authorized_keys, id_ed25519(600), config, known_hosts, kraken-backup-push
|
||
|
||
Host (Kraken RPi5)
|
||
User kraken (uid=1000) owns /home/kraken/.ssh/
|
||
/home/kraken/.ssh/id_ed25519 — закрытый ключ (обязательно 600)
|
||
/home/kraken/.ssh/authorized_keys — публичный ключ
|
||
…/docker/hermes/config/etc-passwd + etc-group — root:root, 644 (bind-mount ro)
|
||
```
|
||
|
||
**🔴 Симптом: `No user exists for uid 1000`**
|
||
|
||
```
|
||
$ sudo docker exec hermes-kraken ssh kraken-host "echo HELLO"
|
||
No user exists for uid 1000
|
||
```
|
||
|
||
Это **НЕ про ключ**. Это `getpwuid(euid)` вернул NULL внутри `ssh`. Порядок работы `ssh`:
|
||
`getpwuid(euid)` → `$HOME` → `$HOME/.ssh/config` → `$HOME/.ssh/id_*` → коннект → аутентификация.
|
||
Падает на **первом** шаге — до чтения ключа. **Пересоздание/копирование ключа не помогает.**
|
||
|
||
Почему: в образе создан только `hermes` с uid **10000**, а контейнер запущен под uid **1000**
|
||
(из `USER` в образе) → для 1000 в passwd нет записи.
|
||
|
||
Как это выглядит в диагностике:
|
||
```bash
|
||
sudo docker exec hermes-kraken id # uid=1000 gid=1000
|
||
sudo docker exec hermes-kraken getent passwd 1000 # пусто, exit=2
|
||
sudo docker exec hermes-kraken getent passwd 10000 # hermes:x:10000:10000::/opt/data:/bin/sh
|
||
```
|
||
⚠️ **Питфолл диагностики:** `sudo docker exec` по умолчанию даёт **uid 0**, и `getpwuid(0)` тоже
|
||
находит `hermes` (по home=/opt/data). Поэтому проверка «изнутри exec» выглядит частично рабочей,
|
||
а реальный процесс под 1000 — падает. Проверять именно `getent passwd 1000`.
|
||
|
||
**История (не повторять ошибок)**
|
||
|
||
- **2026-06-17:** та же ошибка. Обошли **копированием ключа** в `/opt/data/home/.ssh/id_ed25519`
|
||
(владелец 1000, mode 600) и вызовом с явным `-i`. Это костыль: `$HOME` и место ключа разъезжаются,
|
||
любой новый `ssh` без `-i` ломается снова. **Правильного фикса тогда не сделали.**
|
||
- **2026-09-20:** `chown 1000:1000` на **хосте** для `docker/hermes/config` (был владелец `10000:10000`,
|
||
mode 700 → crash-loop `mkdir /opt/data: Permission denied`). Это правка владельца **файлов**, а **не**
|
||
uid контейнера. uid контейнера остался 1000.
|
||
- **2026-09-23:** разбор повторного симптома → найден корень: **отсутствие записи uid 1000 в passwd образа**.
|
||
|
||
**Почему `entrypoint.sh` не спасает**
|
||
|
||
Вся логика сброса прав (`gosu hermes`, `usermod -u`, `chown -R hermes:hermes /opt/data`) обёрнута в
|
||
```bash
|
||
if [ "$(id -u)" = "0" ]; then ... exec gosu hermes "$0" "$@" ; fi
|
||
```
|
||
Контейнер стартует под **uid 1000**, а не под root → **вся ветка пропускается целиком**.
|
||
`usermod -u "$HERMES_UID" hermes` внутри неё недостижим без запуска контейнера от root.
|
||
В `hermes-build/.env` `HERMES_UID`/`HERMES_GID` не заданы.
|
||
|
||
**Fixed points (НЕ менять)**
|
||
|
||
1. Закрытый ключ на хосте **обязан быть 600** — OpenSSH 10.x (Bookworm/Trixie) отвергает 644.
|
||
2. Публичный ключ на хост один раз: `ssh-copy-id kraken@<host>`.
|
||
3. SSH config обязан лежать в `$HOME/.ssh/config` (`$HOME=/opt/data` → `/opt/data/.ssh/config`).
|
||
4. **В `/etc/passwd` должна быть запись для uid, под которым стартует контейнер.** Сейчас uid=1000 без
|
||
записи. См. план ниже — добавлять в **Dockerfile образа**, не в живой контейнер (иначе сбросится).
|
||
5. Не ставить 644 на закрытый ключ.
|
||
6. НЕ использовать `Include ~/.ssh/config` в системном `/etc/ssh/ssh_config` — OpenSSH не резолвит `~`.
|
||
7. Может быть несколько пар ключей: bind mount приносит хост-ключ, а агент может иметь свой в другом месте.
|
||
8. Полный mount `/etc/ssh/` перезаписывает и host keys тоже.
|
||
|
||
**Диагностика по порядку (актуальна и ПОСЛЕ фикса — для регрессий)**
|
||
|
||
```bash
|
||
ssh kraken@<host> "echo OK && hostname" # доступен ли хост
|
||
ssh kraken@<host> "sudo docker exec hermes-kraken id" # реальный uid
|
||
ssh kraken@<host> "sudo docker exec hermes-kraken getent passwd 1000" # ← КЛЮЧЕВАЯ проверка; exit=2 = регрессия (mount отвалился)
|
||
ssh kraken@<host> "sudo docker exec hermes-kraken ssh -o ConnectTimeout=5 -o BatchMode=yes kraken-host 'id; echo OK'" # приёмка
|
||
ssh kraken@<host> "sudo docker exec hermes-kraken ls -lan /opt/data/.ssh" # ключ и права
|
||
ssh kraken@<host> "ps -o uid,cmd -p \$(sudo docker inspect -f '{{.State.Pid}}' hermes-kraken)" # uid РЕАЛЬНОГО процесса (не exec!)
|
||
ssh kraken@<host> "cd <compose dir> && sudo docker inspect hermes-kraken --format 'User=[{{.Config.User}}] Mounts={{range .Mounts}}{{.Destination}} {{end}}'"
|
||
```
|
||
|
||
**🔁 Регрессия: что делать, если симптом вернулся**
|
||
|
||
Порядок проверки — по вероятности:
|
||
|
||
1. **Mount пропал** (чаще всего). `etc-passwd` не примонтирован → uid 1000 без записи.
|
||
Проверить: `docker inspect hermes-kraken --format '{{range .Mounts}}{{.Destination}} {{end}}'` —
|
||
должен быть `/etc/passwd`. Если нет — правку compose потеряли, вернуть строки и `compose up -d`.
|
||
2. **Mount есть, но файл пустой/битый** — `config/etc-passwd` перезаписан или обрезан.
|
||
Проверить хвост: `tail -3 .../docker/hermes/config/etc-passwd` → должна быть строка `kraken:x:1000…`.
|
||
Лечится повторным созданием (команда в разделе Volumes).
|
||
3. **Сменился uid контейнера** (например после `docker compose pull` нового образа с другим `USER`).
|
||
Тогда строка в `etc-passwd` не совпадает с фактическим uid. Сверить `id` и `getent passwd`.
|
||
4. **Сменился базовый образ** → `etc-passwd` устарел (в нём старый набор uid). Пересоздать из
|
||
`docker exec … cat /etc/passwd` + добавить свою строку.
|
||
|
||
⚠️ **Порядок восстановления:** НЕ трогать ключи и НЕ пересоздавать образ — симптом `No user exists for
|
||
uid N` всегда про passwd, никогда про ключ.
|
||
|
||
**Pitfalls**
|
||
|
||
- 🔴 **Главный:** uid, под которым стартует контейнер, ОБЯЗАН иметь запись в `/etc/passwd`. Иначе
|
||
любой `ssh` внутри контейнера падает с `No user exists for uid <N>` ещё до чтения ключа.
|
||
- ❌ **ОПРОВЕРГНУТО (было в доке как «не chown'ить на 1000:1000»):** формулировка неверна. Правка
|
||
владельца `/opt/data` — это про **права файлов**, не про uid. Реальный инвариант: **uid процесса ==
|
||
владелец `/opt/data`**. При фиксе 2026-09-20 оба и привели к 1000. Держать их согласованными.
|
||
- ❌ **ОПРОВЕРГНУТО (было «в compose не задавать `user:`; entrypoint сам сбросит uid»):** entrypoint
|
||
сбрасывает права **только если стартовал от root** (`if [ "$(id -u)" = "0" ]`). Контейнер стартует
|
||
под 1000 → ветка пропускается. Утверждение верно лишь для root-старта.
|
||
- ❌ **ОПРОВЕРГНУТО (было «passwd сбрасывается при пересоздании контейнера»):** passwd из **образа**,
|
||
пересоздание контейнера его не трогает. Он сбрасывается при **пересборке образа** — поэтому и надо
|
||
править **Dockerfile**, а не живой контейнер.
|
||
- Не ставить 644 на закрытый ключ.
|
||
- НЕ использовать `Include ~/.ssh/config` в системном `/etc/ssh/ssh_config` — OpenSSH не резолвит `~`.
|
||
- Полный mount `/etc/ssh/` перезаписывает и host keys тоже.
|
||
- `chown -R` по большому дереву на медленном USB-HDD **не влезает в SSH-таймаут** → запускать через
|
||
`sudo systemd-run --unit=<name> --collect --property=Type=oneshot chown -R ...`, потом проверять
|
||
`systemctl is-active <name>`.
|
||
|
||
## ПЛАН: фикс uid 1000 — ⛔ ОТМЕНЁН 2026-09-23 (заменён рабочим решением)
|
||
|
||
> **Статус: НЕ ПРИМЕНЁН И НЕ БУДЕТ ПРИМЕНЁН.** Оставлено для истории — почему отказались.
|
||
|
||
**Что планировалось (вариант A):** завести `kraken` с uid 1000 **в образе** — правка `Dockerfile`
|
||
(`RUN useradd -u 1000 … && USER kraken`), затем `docker compose build --no-cache` + пересоздание.
|
||
|
||
**Почему отказались — три блокера, каждый проверен живьём:**
|
||
|
||
1. 🔴 **`docker compose build` некуда применить.** Compose тянет готовый
|
||
`ghcr.io/mallexxx/hermes-kraken:latest` (`image:`, без `build:`). Локальный `Dockerfile` в
|
||
`hermes-build/` её **не воспроизводит** (в нём нет `USER`, а uid 1000 приходит именно оттуда).
|
||
Переключение на `build:` = подмена версии Hermes на другую ревизию → риск регрессии.
|
||
2. 🔴 **В ветке про root: `entrypoint.sh` не спасает.** Вся логика `usermod`/`gosu`/`chown` обёрнута в
|
||
`if [ "$(id -u)" = "0" ]`. Контейнер стартует под 1000 → ветка пропускается целиком.
|
||
3. 🔴 **Запись в `/etc/passwd` изнутри невозможна.** Файл `root:root`, процесс uid 1000 →
|
||
`touch /etc/passwd` = `Permission denied`. Ни `useradd`, ни `printf >> /etc/passwd` из
|
||
`entrypoint.sh` не сработают (блок молча пропускался на `[ -w /etc/passwd ]`).
|
||
|
||
**Что сделали вместо — bind-mount passwd/group (без пересборки, версия Hermes не менялась).**
|
||
Полный разбор и команды — в следующем разделе.
|
||
|
||
**Урок:** при выборе «править Dockerfile vs обойти» сначала проверять, из чего реально запущен
|
||
контейнер (`docker inspect --format '{{.Config.Image}}'` + есть ли `build:` в compose). Правка
|
||
Dockerfile, который не участвует в сборке, — нулевой эффект.
|
||
|
||
## ✅ SSH контейнер → хост починен 2026-09-23 (passwd через bind-mount)
|
||
|
||
> **Симптом:** `docker exec hermes-kraken ssh kraken-host "…"` → `No user exists for uid 1000`. Старые доки называли это «passwd сбрасывается» — на деле причина глубже, см. ниже.
|
||
>
|
||
> **Проверенный диагноз (сырьё):**
|
||
> - Контейнер стартует под **uid 1000**, заданным `USER` в образе (`ps -o uid,pid,cmd` → PID 1 = `tini` под uid 1000). В compose строки `user:` НЕ было.
|
||
> - `/etc/passwd` образа создаёт только `hermes:x:10000` (`RUN useradd -u 10000 -m -d /opt/data hermes` в Dockerfile). Записи для uid 1000 **нет**.
|
||
> - `ssh` падает на `getpwuid(euid)` **до** чтения `~/.ssh/config` и ключа → пересоздание ключа не помогает.
|
||
> - `/etc/passwd` принадлежит `root:root`, процесс uid 1000 → `touch /etc/passwd` = `Permission denied`. **Записать запись изнутри контейнера невозможно.** Оригинальный `entrypoint.sh` поэтому и оборачивал всё в `if [ "$(id -u)" = "0" ]`, а `gosu hermes` / `usermod` / `chown` для uid 1000 не выполняются вообще.
|
||
> - `.ssh` монтировался в `/root/.ssh` (недоступен под uid 1000, `HOME=/opt/data`) — ключи до SSH не доезжали.
|
||
>
|
||
> **Фикс (2 файла, пересборки образа НЕ требует → версия Hermes не меняется):**
|
||
>
|
||
> 1. Создать копию passwd/group с записью для uid 1000:
|
||
> ```bash
|
||
> cd /srv/dev-disk-by-uuid-49e8f586-.../docker/hermes/config
|
||
> sudo docker exec hermes-kraken cat /etc/passwd > /tmp/passwd.new
|
||
> printf 'kraken:x:1000:1000::/opt/data:/bin/sh\n' >> /tmp/passwd.new
|
||
> sudo docker exec hermes-kraken cat /etc/group > /tmp/group.new
|
||
> printf 'kraken:x:1000:\n' >> /tmp/group.new
|
||
> sudo cp /tmp/passwd.new etc-passwd && sudo cp /tmp/group.new etc-group
|
||
> sudo chown root:root etc-passwd etc-group && sudo chmod 644 etc-passwd etc-group
|
||
> ```
|
||
> 2. В `hermes-build/docker-compose.yml`:
|
||
> ```yaml
|
||
> user: "1000:1000" # явно, раньше приходило из USER образа
|
||
> volumes:
|
||
> - …/docker/hermes/config/etc-passwd:/etc/passwd:ro
|
||
> - …/docker/hermes/config/etc-group:/etc/group:ro
|
||
> - …/docker/hermes/config/.ssh:/opt/data/.ssh # было /root/.ssh — мимо $HOME
|
||
> ```
|
||
> 3. Пересоздать:
|
||
> ```bash
|
||
> cd /srv/dev-disk-by-uuid-49e8f586-.../docker/hermes-build
|
||
> sudo docker rm -f hermes-kraken && sudo docker compose up -d
|
||
> # Порядок подтверждён рабочим 2026-09-23.
|
||
> ```
|
||
> ⚠️ `docker compose up -d --force-recreate` **падает** с
|
||
> `Conflict. The container name "/hermes-kraken" is already in use` — контейнер создан вне
|
||
> compose-проекта, поэтому compose не находит его как «свой». Поэтому и нужен явный `docker rm -f`.
|
||
> ⚠️ `docker compose up -d` без `rm -f` в этой ситуации просто ничего не делает (контейнер уже есть).
|
||
> 4. **Дополнительные монтирования** (проверено по факту):
|
||
> `config/.ssh` при этом указывать на `/opt/data/.ssh`, а не `/root/.ssh`.
|
||
>
|
||
> **Приёмка (проверено 2026-09-23, Alex подтвердил: «ssh работает»):**
|
||
> ```bash
|
||
> ssh kraken 'sudo docker exec hermes-kraken sh -c "getent passwd 1000; ssh kraken-host \"id; echo OK\""'
|
||
> # → kraken:x:1000:1000::/opt/data:/bin/sh
|
||
> # → uid=1000(kraken) gid=1000(kraken) ... kraken ... HELLO_OK
|
||
> ```
|
||
>
|
||
> **Результат:** SSH из контейнера `hermes-kraken` → хост Kraken работает без `-i` и без явных опций.
|
||
> Ключи, права и конфиг трогать не потребовалось — только passwd/group и точка монтирования `.ssh`.
|
||
>
|
||
> **Что НЕ потребовалось (важно для будущих сессий):**
|
||
> - Пересборка образа (`docker compose build`) — НЕ делалась, версия Hermes **не менялась**.
|
||
> - Правка `Dockerfile` — НЕ делалась (см. отменённый план выше).
|
||
> - Пересоздание/копирование ключей — НЕ делалось и не поможет (симптом не про ключ).
|
||
>
|
||
> **Питфоллы:**
|
||
> - `chown`/`cp` в `hermes-build/` и `docker/hermes/config/` требуют `sudo` — каталог принадлежит `root`, обычный `cp` даёт `Permission denied`. Это ловилось живьём при заливке compose.
|
||
> - Правка `entrypoint.sh` (добавить `printf … >> /etc/passwd`) **не работает** — файл root-owned, запись молча пропускается на `[ -w /etc/passwd ]`. Блок остался в файле внутри контейнера как безвредный no-op; при пересоздании контейнера он и так исчезнет (файл берётся из образа). Копия исходника: `backups/hermes-uid1000-20260923-123803/entrypoint.sh`.
|
||
> - `useradd` в контейнере тоже невозможен по той же причине (root-owned `/etc/passwd`).
|
||
> - `etc-passwd` — **статичный снимок** passwd образа. При смене базового образа (новые uid) файл надо пересоздать.
|
||
> - `/etc/group` тоже понадобился — без него `id` показывает `gid=1000` без имени группы.
|
||
> - Не путать «контейнер поднялся, но внутри своя ошибка» с гонкой docker↔mount (см. `personal/tech/docker-data-root-mount-race.md`).
|
||
> - 🔴 **Логи читать только из ТЕКУЩЕГО контейнера.** После `docker rm -f` старые ошибки (напр. Telegram `httpx.ConnectError`) остаются в буфере прошлого контейнера. Я процитировал их как актуальные — Alex опроверг: Telegram работал. Проверять `docker logs hermes-kraken 2>&1 | grep -ic <что>` на **новом** контейнере.
|
||
|
||
## Notes
|
||
|
||
- `network_mode: host` — port 8642 is directly on the Kraken host, no port mapping needed
|
||
- `HERMES_ALLOW_ROOT_GATEWAY=1` — ⚠️ исправлено 2026-09-23: контейнер **НЕ работает под root**, он
|
||
стартует под **uid 1000** (`tini` под 1000, проверено). Переменная остаётся в окружении как
|
||
историческая настройка; фактический root-старт возможен только если явно запустить под uid 0.
|
||
- All secrets in `hermes-build/.env` (not committed)
|
||
- Образ: `ghcr.io/mallexxx/hermes-kraken:latest` (собран из **`mallexxx/hermes-agent`** — форк, а НЕ
|
||
upstream `NousResearch/hermes-agent`, как утверждалось ранее).
|
||
- ⚠️ **Неизвестно (уточнить):** собирается ли образ локально из `hermes-build/Dockerfile` и пушится в
|
||
GHCR, или GHCR-образ собирается CI в другом репозитории. От этого зависит, где вносить правку uid 1000.
|