Skip to content
MattoYuzuruPublic

About

Kansoku is a local-first observability dashboard for tracking AI agents, skills, plugins, MCP tools, tokens, prompts, and usage trends.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Kansoku

Kansoku (観測, «наблюдение») — локальная платформа наблюдаемости для AI coding агентов (Codex, Claude Code и других). Она отвечает на простой вопрос: чем твои агенты реально пользуются — какие skills, plugins и MCP-инструменты активируются, как часто, с каким успехом и в каких сессиях, — не сохраняя ни одного prompt'а, ответа модели или фрагмента кода.

Всё работает на устройстве пользователя: один Docker Compose стек, один порт для дашборда, PostgreSQL для хранения. Ничего не уходит во внешние сервисы.

Зачем

Агенты вроде Codex и Claude Code уже умеют экспортировать телеметрию (OTel) и слать хуки, но у этих данных нет единого места, где видно фактическое использование агента — с учётом ненадёжных источников, обрывов данных и разной степени доказательности события. Kansoku собирает эти источники, честно помечает то, что не удалось подтвердить (unknown ≠ zero), и показывает результат в локальном дашборде.

Принципы

  1. Local-first. Runtime и данные — на устройстве пользователя; UI слушает только loopback.
  2. Metadata by default. Raw prompts, ответы моделей, tool input/output и код не сохраняются.
  3. No silent loss. Каждый источник данных измеряется на покрытие; разрывы помечаются явно, а не заполняются нулями.
  4. Adapter-first. Codex и Claude — обычные адаптеры, а не частные случаи в модели данных.
  5. Lineage everywhere. У каждой метрики есть источник, версия адаптера, schema fingerprint и время наблюдения.
  6. Unknown is not zero. Отсутствие данных, неподдерживаемый источник и настоящий ноль — три разных состояния, и они никогда не смешиваются.
  7. Useful over performative. Никакого сводного «productivity score» — только объяснимые метрики с явным числителем, знаменателем и ограничениями.
  8. Cheap to keep running. Один docker compose up, restart: always, ограниченное хранение и предсказуемое потребление ресурсов.

Быстрый старт

Нужны Docker (Desktop или Engine) и Docker Compose v2. Все команды ниже выполняются из каталога deploy/.

cd deploy

# 1. Собрать образ (контекст сборки — корень репозитория, поэтому Dockerfile лежит в deploy/,
#    а сама сборка идёт из deploy/ на уровень выше)
docker build -f Dockerfile -t kansoku:local ..

# 2. Сгенерировать 7 обязательных секретов (каждый — отдельный файл, ≥32 байт, без \n внутри)
mkdir -p secrets
for name in ingress_bearer read_bearer mutation_bearer csrf identity_hmac audit_hmac database_password; do
  openssl rand -base64 32 > "secrets/$name"
  chmod 600 "secrets/$name"
done

# 3. Зафиксировать тег образа в .env — без этого файла переменная не переживёт
#    следующий отдельный вызов docker compose (интерполируется заново при каждом запуске)
echo "KANSOKU_IMAGE=kansoku:local" > .env

# Необязательно: дать Kansoku read-only доступ к inventory Codex.
# Укажите только существующие абсолютные пути; Compose монтирует их read-only.
cat >> .env <<EOF
KANSOKU_CODEX_HOME_PERSONAL=/absolute/path/to/.codex-home
KANSOKU_CODEX_HOME_CORPORATE=/absolute/path/to/.codex-tbank
KANSOKU_CODEX_USER_SKILLS=/absolute/path/to/user-skills
KANSOKU_CODEX_SYSTEM_SKILLS=/absolute/path/to/system-skills
EOF

# Необязательно: дать Kansoku read-only доступ к inventory Claude Code.
# KANSOKU_CLAUDE_HOME — каталог с settings.json (обычно ~/.claude).
cat >> .env <<EOF
KANSOKU_CLAUDE_HOME=/absolute/path/to/.claude
KANSOKU_CLAUDE_USER_SKILLS=/absolute/path/to/.claude/skills
KANSOKU_CLAUDE_REPOSITORY_SKILLS=/absolute/path/to/repo/.claude/skills
KANSOKU_CLAUDE_SYSTEM_SKILLS=/absolute/path/to/system-skills
KANSOKU_CLAUDE_PLUGIN_CACHE=/absolute/path/to/.claude/plugins/cache
EOF

# Если записи в skills-каталоге — симлинки на отдельную библиотеку (частый случай:
# ~/.claude/skills/<name> -> ~/Projects/<library>/skills/<name>), укажите корень
# библиотеки. Он монтируется read-only по тому же абсолютному пути внутрь контейнера,
# иначе абсолютные симлинки внутри контейнера не разрешаются и такие скиллы не видны.
# Указывайте максимально узкий каталог, содержащий цели симлинков — никогда $HOME и не /.
cat >> .env <<EOF
KANSOKU_AGENT_LINK_ROOT_1_PATH=/absolute/path/to/skills-library
EOF

# 4. Поднять стек
docker compose -f compose.yaml up -d

# 5. Проверить процессы и UI
docker compose -f compose.yaml ps
curl -s http://127.0.0.1:43100/ -o /dev/null -w '%{http_code}\n'

# Capacity/ingestion health требует read bearer и может честно вернуть
# warning/degraded/critical даже при работающем PostgreSQL.
read_token=$(<secrets/read_bearer)
curl -s -H "Authorization: Bearer ${read_token}" \
  http://127.0.0.1:43100/api/v1/health

Дашборд — http://127.0.0.1:43100. OTLP-приём (для агентов) — http://127.0.0.1:4318 (HTTP) и 127.0.0.1:4317 (gRPC), оба требуют ingress_bearer из secrets/ingress_bearer.

secrets/ и .env — локальные и в git не попадают (см. .gitignore). PostgreSQL должен стать healthy. Kansoku становится healthy только при полном operational pass; недостаток Docker disk, durability/backpressure или stale source намеренно оставляет контейнер unhealthy, хотя UI и read API продолжают работать. source_state становится как минимум degraded при gap, inactivity, неизвестном runtime state или source clock более чем на пять минут в будущем; configured/not_observed и unsupported остаются отдельными exclusions. Первый healthcheck может занять до 30 секунд после старта.

По умолчанию база имеет advisory soft budget 5 GiB с порогами 70/85/95%, а не hard cap и не preallocation. Перед длительной эксплуатацией рекомендуется минимум 25–30 GiB свободного Docker disk space для БД, индексов, WAL/rollback, backup и emergency spool. Не запускайте 5 GiB soak, если свободное место уже ниже 20%; Kansoku не меняет allocation Docker Desktop автоматически.

Подключение реальных агентов

Возьмите ingress bearer:

cat deploy/secrets/ingress_bearer

Codex — добавьте в config.toml профиля ($CODEX_HOME/config.toml; если вы используете несколько профилей через разные CODEX_HOME, повторите для каждого):

[otel]
log_user_prompt = false

[otel.exporter.otlp-http]
endpoint = "http://127.0.0.1:4318/v1/logs"
protocol = "binary"
headers = { "Authorization" = "Bearer <ingress_bearer>" }

Claude Code — добавьте в блок "env" файла ~/.claude/settings.json:

{
  "env": {
    "CLAUDE_CODE_ENABLE_TELEMETRY": "1",
    "OTEL_METRICS_EXPORTER": "otlp",
    "OTEL_LOGS_EXPORTER": "otlp",
    "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
    "OTEL_EXPORTER_OTLP_ENDPOINT": "http://127.0.0.1:4318",
    "OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer <ingress_bearer>"
  }
}

OTEL_EXPORTER_OTLP_HEADERS здесь обязателен: loopback OTLP аутентифицирован, а заголовок — единственный механизм Claude Code передать bearer. Значение принадлежит оператору: Kansoku никогда его не пишет, не ротирует и не читает из чужого конфига; в installer-preview он показывается как disclosure, а не как ошибка. Запрещёнными для записи остаются OTEL_LOGS_EXPORTER_FILE и любой remote endpoint.

Конфиг агенты читают один раз при старте — после правки нужна новая сессия/процесс агента. Проверка: откройте дашборд → Activity/Agents, там должны появиться события за последние минуты.

Скиллы Claude Code. Активации приходят как OTel-событие skill_activated по тому же каналу — отдельная настройка не нужна. Но чтобы активация связалась с конкретным скиллом, он должен быть в inventory, поэтому скилл-каталоги выше нужно смонтировать, включая корень библиотеки для симлинков. Что не попадёт в inventory никогда:

  • встроенные скиллы Claude Code (dataviz, simplify, run, review, …) — они вкомпилированы в исполняемый файл, SKILL.md на диске нет; их вызовы остаются в unresolved_identity как явное исключение;
  • repository-скиллы более чем одного проекта с одинаковым именем — Claude не передаёт признак проекта, такие имена разрешаются как ambiguous.

У Claude Code нет источника exposure (списка скиллов, видимых модели), поэтому эта плоскость объявлена unsupported — в UI это отдельное состояние, а не ноль. Cold-состояние скиллов Claude считается по полноте inventory; Codex использует настоящие exposure-окна из App Server. Нечитаемая или битая запись в skills-каталоге понижает snapshot до partial, чтобы неполный inventory не выглядел уверенным нулём.

Codex App Server exact evidence — normal serve принимает явно направленный JSONL stream на POST http://127.0.0.1:4318/v1/evidence-bridges/codex-app-server. Нужны тот же ingress_bearer и заголовок X-Kansoku-Agent-Installation с opaque installation ID из inventory. Request ограничен 1 MiB, отдельный stream получает отдельный JSON-RPC demux. Kansoku не запускает и не перенастраивает Codex: настройка producer/tee остаётся явной внешней операцией. Этот endpoint не является доказательством для обычной CLI-сессии, чей stream через него не проходил. Raw frames разбираются только в памяти; сохраняются typed identity/mode/tier/lineage и redaction counters. Значение заголовка должно совпадать с installation_id Codex target в deploy/runtime-config.json: тот же ID используется read-only inventory и rollout watcher.

Если /api/v1/health показывает pending projection, оператор может выполнить только preview/approval-gated retry; автоматического discard нет:

mutation_token=$(<deploy/secrets/mutation_bearer)
csrf_token=$(<deploy/secrets/csrf)

curl -sS -X POST \
  -H "Authorization: Bearer ${mutation_token}" \
  -H "X-Kansoku-CSRF: ${csrf_token}" \
  -H "Origin: http://127.0.0.1:43100" \
  -H "Content-Type: application/json" \
  --data '{}' \
  http://127.0.0.1:43100/api/v1/admin/projection-repair/preview

Из ответа нужно вручную перенести request_id и parameters_sha256, сгенерировать новый одноразовый approval_nonce и отправить их на POST /api/v1/admin/projection-repair/apply с теми же auth/CSRF/origin headers. Один apply выполняет bounded PostgreSQL projection pass, один spool replay и reconciliation; он не возвращает сохранённый retry input и не удаляет неприменённые receipts.

Что именно измеряет component inventory

Kansoku раз в пять минут сканирует только явно смонтированные read-only roots из .env. Для Codex это профили CODEX_HOME, user/system skills, plugin-конфигурация и bounded plugins/cache/<marketplace>/<plugin>/<version>/skills catalog. Cache сам по себе не означает enabled: bundle ownership создаётся только для единственной однозначной версии настроенного plugin@marketplace, а несколько версий остаются cache-only. В Postgres попадают только нормализованные имена, scope, версия/её value state, enabled-state, HMAC-pseudonym пути, fingerprint и lineage snapshot; содержимое SKILL.md, команды MCP, env и credentials не сохраняются. Неполный scan отображается как coverage gap, а не как complete zero.

Overview и component observatories показывают независимые классы доказательств:

  • Installed/Enabled — текущее состояние из inventory snapshot;
  • Exposed/Invoked/Loaded — только реальные lifecycle-события поддерживаемого источника;
  • terminal outcome появляется только у компонента с зарегистрированным terminal contract;
  • not_observed означает, что текущий upstream такого события не прислал, а не нулевое использование.

На 2026-07-25 официальный Codex OTel не документирует стабильное skill/plugin activation событие. Поэтому Kansoku не выводит executed или opportunity detected из prompt-текста, tool name либо факта установки. Claude Code, напротив, документирует skill_activated, plugin_loaded и plugin_installed; они учитываются только в совместимых версиях и при прохождении privacy allowlist.

Plugin, найденный в конфигурации/каталоге, но не включённый, показывается как installed + disabled. Наличие plugin marketplace или cache само по себе не считается установкой. MCP показывается только после явной конфигурации; тестовый MCP или plugin не включается Kansoku автоматически.

MCP observatory дополнительно разделяет configuration/inventory, protocol connection и call lifecycle. Configured не означает connected, isError=false не означает успех пользовательской задачи, а ноль calls не называется unused без полного окна exposure. Аргументы, результаты, ошибки, URL, команды, environment и resource URI не сохраняются.

Подключение через hooks

Session 11 (ADR 0014, Gap B) добавила hook-installer таргеты codex.user_hook / claude.user_hook в contracts/privacy/installer.yaml и реальные builders (installer.BuildCodexHookPlan / installer.BuildClaudeHookPlan) в internal/installer, вместе с новым закрытым capability id configuration.hook_install (contracts/adapter-sdk/capabilities.yaml). PlanConfiguration в internal/codexadapter и internal/claudeadapter строит для этого capability реальный ChangePlan, проходящий через ту же Plan/Approval/SimulateApply/SimulateRollback/SimulateRemove/PlanSHA256 машину, что и OTel-таргеты — без второго apply-механизма.

Ownership-изоляция (обязательное требование ADR 0014, п. 4): Codex-hook и Codex-OTel живут в одном config.toml (разные таблицы), Claude-hook и Claude-OTel — в одном settings.json (разные ключи). Каждый план владеет только своим набором ключей — buildTargetPlan в internal/installer/protocol.go читает/пишет исключительно targetSpecs[id].required этого таргета и сканирует только его собственный forbidden-список, поэтому применение или откат hook-плана никогда не трогает уже применённые OTel-ключи (и наоборот) и не трогает никакой другой существующий пользовательский контент файла. Это проверено round-trip тестом (TestHookPlanOwnershipIsolationRoundTrip в internal/installer/protocol_test.go): применяем OTel-план, применяем hook-план, откатываем hook-план — OTel-ключи и весь несвязанный контент побайтово совпадают с состоянием до применения hook-плана.

Что именно пишет hook-план (только preview/simulate, см. ниже):

Codex (config.toml, таблица notify):

[notify]
command = "kansoku-codex-hook"
role = "collection_only"

Claude Code (settings.json, ключи hooks.* для всех 7 задокументированных событий):

{
  "hooks": {
    "SessionStart": "kansoku-claude-hook",
    "UserPromptSubmit": "kansoku-claude-hook",
    "PreToolUse": "kansoku-claude-hook",
    "PostToolUse": "kansoku-claude-hook",
    "SubagentStart": "kansoku-claude-hook",
    "SubagentStop": "kansoku-claude-hook",
    "Stop": "kansoku-claude-hook"
  }
}

Оба hook-помощника (kansoku-codex-hook / kansoku-claude-hook) по контракту (contracts/codex/hooks-and-otel.yaml / contracts/claude/hooks-and-otel.yaml) читают только bounded stdin JSON, вычисляют prompt-фичи в памяти, никогда не пишут и не пересылают сырой prompt/ответ/tool payload, и шлют уже санитизированное событие на тот же общий /v1/hooks/{adapter}/{event} route, что и OTel-приёмник — codexHookHandler / claudeHookHandler в internal/observability/routes.go (проверено TestRealAdapterHookStdinPayloadsReachCodexAndClaudeHookRoutes синтетическим stdin-пейлоадом, доходящим до реального handler'а).

Важно — это остаётся simulate-only, как и весь internal/installer (ADR 0002). В отличие от раздела «Подключение реальных агентов» выше, здесь нет ручных шагов «впишите это в файл» — ADR 0008's известный пробел (нет kansoku doctor/configure CLI) означает, что PlanConfiguration и SimulateApply/SimulateRollback для configuration.hook_install сегодня доступны только из Go-кода (тесты и будущий CLI), а не из README-инструкции для реального пользователя. AuthorizeRealWrite по-прежнему возвращает "not implemented" для любого таргета — ни один hook или OTel план не пишет в реальный файл пользователя без будущей CLI-обвязки и явного human-approval шага.

Если тебя как агента попросили поднять Kansoku

Если ты — AI-агент и тебе передали этот репозиторий с задачей «подними Kansoku и подключи себя к нему», порядок действий:

  1. Спроси явное подтверждение пользователя перед выполнением — дальнейшие шаги создают локальные секреты и меняют файлы конфигурации самого пользователя (config.toml / ~/.claude/settings.json).
  2. Выполни шаги из «Быстрый старт» выше (сборка образа, секреты, .env, docker compose up, проверка healthy).
  3. Прочитай deploy/secrets/ingress_bearer и пропиши его в конфиг своего собственного рантайма по разделу «Подключение реальных агентов» — определи, Codex ты или Claude Code, и какой у тебя CODEX_HOME / где твой settings.json.
  4. Сообщи пользователю, что нужно начать новую сессию/перезапустить процесс, чтобы конфиг применился, и укажи, где смотреть результат (http://127.0.0.1:43100).

Документы

  • ARCHITECTURE.md — карта модулей репозитория (internal/*, cmd/*, web/, contracts/*) для быстрой ориентации агента: что где лежит и какой документ ниже открывать дальше по конкретной области.
  • ROADMAP.md — история разработки и зависимости между этапами.
  • Engineering Proposal — цели, альтернативы, продуктовые решения.
  • Technical Design Document — контракты, схемы, алгоритмы, эксплуатационные требования.
  • SOURCES.md — официальные интерфейсы агентов и инфраструктуры, на которых основан дизайн.
  • adr/ — architecture decision records; reports/ — verification/reconciliation отчёты по каждому этапу разработки.
  • AGENTS.md — правила работы над этим репозиторием для будущих сессий разработки.

About

Kansoku is a local-first observability dashboard for tracking AI agents, skills, plugins, MCP tools, tokens, prompts, and usage trends.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages