[2026-09-26] eagle: personal/tech/balda-go-fx-pitfalls.md personal/tech/hermes-secret-redaction-bypass.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-26 13:10:58 +06:00
1 parent a69ed4663b
commit 4687f2f85c
2 files changed
+153 -2

No files matched your search

+87 -2
View File
@@ -1,7 +1,8 @@
# Balda — Go/fx pitfalls и метод правок (зеркало скилла)
> Создано 2026-09-26. Обновлено 2026-09-26 (ночь, позже) — добавлен **Дефект 4**
> (advertisement ↔ actor-хендлер), найденный при первом реальном запуске на Kraken.
> Создано 2026-09-26. Обновлено 2026-09-26 (ночь, root-cause сессия) — добавлены:
> **WS-авторизация MM** (§«авторизация — ОДИН механизм»), **`Token:`-заголовок мёртв в MM 11.x**,
> **формат JSON дельт** (плоский массив + 3 аргумента у обёртки).
> Зеркало секции скилла `devops/docker-balda-service`, добавленной 2026-09-26.
> Источник: работа над Mattermost-адаптером Балды (Фаза 2) + деплой на Kraken (Фаза 3).
> Проектные доки: [[personal/projects/balda/balda-mattermost-connector]],
@@ -379,6 +380,90 @@ bash tools/run-project-deltas.sh apply tools/deltas-mattermost.json tools/.delta
⚠️ Дельты с длинным `replace` писать в JSON через `write_file`, **не** heredoc — кавычки и `\n`
ломаются молча. Это тот же питфолл, что у SSH-heredoc с вложенными кавычками.
🔴 **JSON дельт — ПЛОСКИЙ МАССИВ, не объект.** Формат-обёртка
`{"deltas": [...]}` (или любой объект с ключами) роняет апплаер:
```
TypeError: string indices must be integers
File "apply-project-deltas.py", line 91, in main
path = root / d["file"]
```
Причина: `main()` итерирует `for i, d in enumerate(deltas)` — если `deltas` объект, `d`
становится строкой-ключом. Проверено 2026-09-26 на `deltas-mattermost-14.json`.
**Правильно:** файл начинается с `[`, а не с `{`.
⚠️ Обёртка `run-project-deltas.sh` требует **ТРИ** аргумента, а не два:
`run-project-deltas.sh <check|apply|list> <deltas.json> <state.json>`.
Без третьего — `line 9: 3: нужен файл состояния`.
И `check` не поддерживает `--list`: `list` — отдельный глагол (`$1 = list`).
---
## 🔴 Mattermost WebSocket: авторизация — ОДИН механизм, не два
Найдено живым запуском (Фаза 3, root-cause). Симптом: в логе
`mattermost websocket connected`, но **ни одного** события `posted` в инбаунде, даже
на `logger.level: debug`. При этом пробник (pure Go stdlib) ловил `posted` на том же сокете.
**Две причины, наложившиеся друг на друга:**
### 1. `Origin` в handshake → 403 CORS
Если клиент шлёт `Origin: http://mattermost:8065`, а `SiteURL=https://mm.qentra.top` —
MM отвечает:
```
HTTP/1.1 403 Forbidden
{"id":"api.web_socket.connect.upgrade.app_error","message":"URL Blocked because of CORS. Url: …"}
```
📌 **`Origin` не слать вообще.** Балда его не шлёт — с ней этой проблемы нет.
Это ловушка для пробников и внешних клиентов.
### 2. `authentication_challenge` поверх `Authorization: Bearer` → подписка умирает
`channel/mattermost/ingress.go`, `streamOnce`:
```go
conn, _, err := dialer.DialContext(ctx, endpoint, header) // header: Authorization: Bearer ***
// …
s.logger.Info().Msg("mattermost websocket connected") // ← лог УЖЕ «здоровый»
conn.WriteJSON(map[string]any{ // ← 🔴 а это ломает подписку
"seq": 1, "action": "authentication_challenge",
"data": map[string]string{"token": token},
})
```
**Эмпирика (пробник, `-authheader`):**
- с челленджем: `hello` → `status_change` → **тишина 40 с**, хотя пост в канал создаётся;
- без челленджа: `hello` → `posted` штатно.
То есть избыточный челлендж заставляет MM переинициализировать сессию сокета, и `posted`
на него больше не пушится. **Диагностически коварно:** `websocket connected` логируется
**до** челленджа, поэтому лог выглядит здоровым.
**Правило:** сокет авторизуется **либо** заголовком на dial, **либо** `authentication_challenge`.
Оба сразу — тишина. Если заголовок есть, челлендж слать **нельзя**.
**Фикс** — дельта `tools/deltas-mattermost-14.json` (2 дельты: убрать `WriteJSON`-блок
и unused-константу `websocketActionAuthChallenge`). Образ `balda-mm:0.1.2`.
📌 **Общий урок для транспортов:** лог «connection established» ничего не говорит о том,
что подписка жива. Проверять **фактом получения события**, а не фактом коннекта.
Для MM — пробник из `~/Developer/balda-wsprobe/` (pure stdlib, RFC6455, собирается нативно
на Mac, на хост кладётся статический бинарь).
### 🔴 MM 11.x: заголовок `Token:` для API мёртв
`POST /users/login` отдаёт сессию в **заголовке ответа** `Token:`. Но использовать её нужно
**только** как `Authorization: Bearer *** — заголовок `Token: <session>` в запросах даёт
`401 api.context.session_expired.app_error` при живой сессии (в БД `expiresat` = +180 дней).
Старый стиль MM 5.x больше не принимается. Проверено 2026-09-26.
📌 Отсюда же: если скрипт падает с `session_expired` — сначала проверить, каким **именем
заголовка** шлётся токен, а не искать проблему в пароле/лимите сессий.
---
## Связанные заметки