Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ DADATA_SECRET=change-me-dadata-secret
ENRICH_PY_PATH=./service-agent/skills/compliance/enrich_placeholder.py

# ── Опционально ──────────────────────────────────────────────────────────
# OPENAI_API_KEY=sk-... # для транскрибации голосовых (voice.stt.provider: openai в Hermes)
# GITLAB_TOKEN=glpat-...
# GITLAB_URL=https://your-gitlab.example.com
2 changes: 1 addition & 1 deletion .github/scripts/ai_security_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ def looks_text(path: pathlib.Path) -> bool:
return False

def should_skip_secret_path(path: pathlib.Path) -> bool:
skip_parts = {".git", ".venv", "__pycache__", "node_modules", "openclaw_data", ".openclaw"}
skip_parts = {".git", ".venv", "__pycache__", "node_modules", "hermes_data", ".hermes"}
return bool(skip_parts.intersection(path.parts))

def check_hardcoded_tokens():
Expand Down
20 changes: 10 additions & 10 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,8 @@ docker-compose.override.yml

# Секреты — никогда в git
.env
.anthropic_tokens.json
.cli-proxy-api/
Codex-*.json
instances/*/.env
instances/*/openclaw.json
!instances/admin/openclaw.json
instances/*/openclaw_data/
**/*.env
instances/*/hermes_data/
**/*.key
**/*.token
**/*.db
Expand Down Expand Up @@ -40,21 +34,27 @@ backups/
.DS_Store
Thumbs.db

# Рантайм-директории платформы (локальные, не для git)
.arkenos/
.optimus/

# ralphex progress logs
.ralphex/progress/
/docs/plans/*

# Greeting counter state
shared/skills/corp-greeting/.greeting_counter

# Migration state per instance (local)
# Migration state per instance (local; template & admin baseline tracked)
instances/*/.migrations_applied
instances/*/openclaw_data/.migrations_applied
instances/*/workspace/.migrations_applied
!instances/_template/workspace/.migrations_applied
!instances/admin/workspace/.migrations_applied

# Instance workspace runtime data (not tracked)
instances/*/workspace/memory/
instances/*/workspace/MEMORY.md
instances/*/workspace/.openclaw/
instances/*/workspace/.hermes/
instances/*/workspace/.git/
instances/*/workspace/.gitignore
instances/*/workspace/notes-vault/
Expand Down
40 changes: 23 additions & 17 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project Overview

**CortexForge** — корпоративная AI-инфраструктура на базе [OpenClaw](https://github.com/openclaw/openclaw): каждому сотруднику выдаётся изолированный Telegram-бот с собственной памятью, квотой токенов и персонажем. Разворачивается одной командой на одном сервере через Docker Compose.
**CortexForge** — корпоративная AI-инфраструктура на базе [Hermes Agent](https://github.com/NousResearch/hermes-agent): каждому сотруднику выдаётся изолированный Telegram-бот с собственной памятью, квотой токенов и персонажем. Разворачивается одной командой на одном сервере через Docker Compose.

## Commands

Expand Down Expand Up @@ -90,7 +90,7 @@ python3 -m pytest tests/test_broker_persistence.py::TestBrokerPersistence::test_
### Security checks (required before PRs)

```bash
python3 .github/scripts/ai_security_check.py # 8 AI-specific security rules (Semgrep-like)
python3 .github/scripts/ai_security_check.py # 9 AI-specific security rules (Semgrep-like)
shellcheck scripts/*.sh # shell script linting
make security-check # .env-права (600), ключи, gitignore
```
Expand Down Expand Up @@ -160,13 +160,13 @@ python3 scripts/migrate-instances.py --dry-run # посмотреть что

Инстансы **никогда** не видят настоящий API-ключ. Цепочка:
1. `add-user.sh` генерирует `QUOTA_KEY_<NAME>=quota-<name>-<random>` в корневом `.env`
2. В `docker-compose.override.yml` инстанс получает `ANTHROPIC_API_KEY=${QUOTA_KEY_<NAME>}` — OpenClaw думает, что это обычный API-ключ
3. `openclaw.json.template` указывает `baseUrl: "http://quota-proxy:9090"` — все запросы идут через прокси
2. В `docker-compose.override.yml` инстанс получает `ANTHROPIC_API_KEY=${QUOTA_KEY_<NAME>}` — Hermes думает, что это обычный API-ключ
3. `hermes_config.yaml.template` указывает `base_url: http://quota-proxy:9090` — все запросы идут через прокси
4. `quota-proxy` хэширует полученный ключ (SHA256), ищет в `_quota_keys`, подставляет настоящий upstream-ключ

### User instance image

Пользовательские инстансы собираются из `instances/Dockerfile.user` (не напрямую из `ghcr.io/openclaw/openclaw:latest`). Поверх базового образа добавлены: `ffmpeg` (транскрибация аудио), `tesseract-ocr` (русский + английский), `mcporter` (MCP CLI). Admin использует базовый образ напрямую.
Пользовательские инстансы собираются из `instances/Dockerfile.user` (не напрямую из `nousresearch/hermes-agent:stable`). Поверх базового образа добавлены: `ffmpeg` (транскрибация аудио), `tesseract-ocr` (русский + английский), `mcporter` (MCP CLI). Admin собирается из `instances/admin/Dockerfile` (тот же базовый образ + docker CLI).

### Service-agent skills

Expand Down Expand Up @@ -218,7 +218,7 @@ Naming convention: сервис `assistant-<name>`, контейнер `corp-<na

### Instance workspace structure

Каждый инстанс — OpenClaw-контейнер с workspace:
Каждый инстанс — Hermes-контейнер с workspace:
```
instances/<name>/workspace/
SOUL.md # персонаж и стиль общения
Expand All @@ -230,7 +230,10 @@ instances/<name>/workspace/
HEARTBEAT.md # задачи для периодических проверок (пустой = skip)
memory/ # долгосрочная память
.migrations_applied # трекинг миграций (gitignored)
openclaw.json # конфиг: Telegram-канал + модель

instances/<name>/hermes_data/ # маунт → /opt/data (~/.hermes)
config.yaml # конфиг Hermes: модель (quota-proxy), terminal.cwd
.env # секреты: Telegram, gateway-токен, quota-key
```

### Admin instance (Admin)
Expand Down Expand Up @@ -273,7 +276,7 @@ openclaw.json # конфиг: Telegram-канал + модель

## CI/CD

- **`security.yml`** — Semgrep, CodeQL, Trivy, Gitleaks, Hadolint, ShellCheck + `ai_security_check.py` (8 кастомных правил)
- **`security.yml`** — Semgrep, CodeQL, Trivy, Gitleaks, Hadolint, ShellCheck + `ai_security_check.py` (9 кастомных правил)
- **`lint-commits.yml`** — проверка Conventional Commits
- **`release-please.yml`** — автоматически открывает PR с version bump и обновлением `CHANGELOG.md`
- Правило: PR нельзя мёрджить с красными security-checks
Expand All @@ -290,10 +293,13 @@ openclaw.json # конфиг: Telegram-канал + модель
- `BROKER_KEY_MONITOR` — ключ resource-monitor для отправки алертов в broker
- `SERVICE_API_KEY` — ключ для авторизации запросов к service-agent API

Переменные инстанса в `instances/<name>/.env`:
- `TELEGRAM_BOT_TOKEN`, `TELEGRAM_ALLOW_FROM`
Переменные инстанса в `instances/<name>/.env` (compose env_file, для скиллов):
- `BROKER_KEY` — берётся из корневого `.env`
- `GATEWAY_TOKEN` — auth-токен OpenClaw web UI (генерируется `add-user.sh`)

Секреты Hermes в `instances/<name>/hermes_data/.env`:
- `TELEGRAM_BOT_TOKEN`, `TELEGRAM_ALLOWED_USERS`
- `HERMES_GATEWAY_TOKEN` — auth-токен gateway (генерируется `add-user.sh`)
- `ANTHROPIC_API_KEY` — quota-key для quota-proxy

Пороги алертов resource-monitor (env vars): `ALERT_CPU_PCT` (80%), `ALERT_RAM_PCT` (85%), `ALERT_DISK_PCT` (90%), `ALERT_QUOTA_PCT` (80%), `MONITOR_INTERVAL` (60s).

Expand All @@ -308,17 +314,17 @@ openclaw.json # конфиг: Telegram-канал + модель
| 8317 | cliproxyapi | internal only |
| 9090 | quota-proxy | `127.0.0.1:9090` |
| 9091 | resource-monitor | `127.0.0.1:9091` |
| 18789 | admin OpenClaw UI | `127.0.0.1:18789` |
| 18790+N | user instance UIs | `127.0.0.1:18790+N` |
| 18789 | admin Hermes Dashboard (контейнер: 9119) | `127.0.0.1:18789` |
| 18790+N | user Hermes Dashboards (контейнер: 9119) | `127.0.0.1:18790+N` |

### Key operational scripts

| Скрипт | Назначение |
|--------|------------|
| `scripts/autoupdate.sh` | Cron-скрипт (каждые 5 мин): git pull + rebuild changed |
| `scripts/sync-instances.sh` | Ежечасный maintenance от admin: миграции, healthcheck |
| `scripts/fix-permissions.sh` | Восстановить права 600 на .env после git-операций |
| `scripts/refresh-anthropic-token.sh` | Обновить OAuth-токен из `ANTHROPIC_REFRESH_TOKEN` |
| `scripts/fix-permissions.sh` | Восстановить права 600 на .env и 1000:1000 на hermes_data/workspace |
| `scripts/provision-hermes-data.sh` | Подготовить `hermes_data/` (каталоги, `.env`, `config.yaml`, `SOUL.md`, права) — вызывается из `make bootstrap` / `make deploy` |
| `scripts/migrate-instances.py` | Применить миграции ко всем инстансам |

## Gotchas
Expand All @@ -330,9 +336,9 @@ openclaw.json # конфиг: Telegram-канал + модель
- **Broker sender auth**: отправитель определяется по API-ключу, не по полю body — подменить sender невозможно
- **Streaming не трекается квотой**: `quota-proxy` считает токены только из non-streaming ответов (`{"usage": {...}}`). SSE-стримы (`text/event-stream`) пропускаются — использование не учитывается
- **Broker per-operation connections**: `broker.py` открывает/закрывает SQLite-соединение на каждую операцию (для WAL mode). `quota-proxy` наоборот — держит один `_conn` на всё время жизни
- **Node.js heap**: admin = `--max-old-space-size=1536` (1.5 GB), user instances = `768` (768 MB)
- **Memory limits**: admin = 2G, user instances = 1G (`deploy.resources.limits`)
- **Test import order**: env-переменные **ОБЯЗАТЕЛЬНО** ставить до `import` тестируемого модуля — модули читают env при импорте, не при вызове
- **docker-compose.override.yml**: gitignored, содержит все пользовательские инстансы. `add-user.sh` дописывает туда. Базовый `docker-compose.yml` — только инфраструктура + admin
- **GATEWAY_TOKEN**: генерируется `add-user.sh` для каждого инстанса, используется как auth-токен OpenClaw web UI
- **HERMES_GATEWAY_TOKEN**: генерируется `add-user.sh` для каждого инстанса, используется как auth-токен Hermes gateway
- **Migration overrides**: `../overrides/migrations/` — внешние миграции с приоритетом над `migrations/` (совпадение по имени файла)
- **ai_security_check.py exit codes**: CRITICAL/HIGH = exit 1 (блокирует CI), MEDIUM/LOW = warnings (не блокируют)
14 changes: 8 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,19 @@ Describe the problem you're solving, not just the solution.

```bash
git clone https://github.com/example-org/cortex-forge.git
cd CortexForge
cd cortex-forge

# Global config
cp .env.example .env
chmod 600 .env
# Edit .env: set ANTHROPIC_API_KEY and other tokens

# Admin instance (required)
mkdir -p instances/admin
cp instances/.env.example instances/admin/.env
# Edit instances/admin/.env: set TELEGRAM_BOT_TOKEN, TELEGRAM_ALLOW_FROM
chmod 600 instances/admin/.env
# Edit instances/admin/.env: set TELEGRAM_BOT_TOKEN, TELEGRAM_ALLOWED_USERS, HERMES_GATEWAY_TOKEN
make bootstrap

# Launch
make deploy
Expand All @@ -80,8 +83,7 @@ make security-check
### Running tests

```bash
# Tests are being added — see open issues tagged 'tests'
# pytest tests/ -v
python3 -m unittest discover tests/ -v
```

### Adding a skill to service-agent
Expand All @@ -94,7 +96,7 @@ make security-check
# ... do work ...
print(json.dumps({"result": "done"}))
```
3. Add `skills.json` manifest (see `compliance/skills.json` as reference)
3. Add `skills.json` manifest (see `service-agent/skills/compliance/skills.json` as reference)
4. Rebuild: `docker compose build assistant-service`

---
Expand Down Expand Up @@ -141,4 +143,4 @@ PRs are merged via **squash merge** to keep `master` history clean.

**Do not open a public issue for security vulnerabilities.**

See [SECURITY.md](SECURITY.md) for responsible disclosure instructions.
See [docs/SECURITY.md](docs/SECURITY.md) for responsible disclosure instructions.
17 changes: 11 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: add-user remove-user deploy restart logs backup status quota-report quota-reset set-limit security-check monitor monitor-alerts add-service service-health service-skills install-hooks admin-overview
.PHONY: add-user remove-user bootstrap deploy restart logs backup status quota-report quota-reset set-limit security-check monitor monitor-alerts add-service service-health service-skills install-hooks admin-overview

# ── Управление инстансами ──────────────────────────────────────────────────

Expand All @@ -13,9 +13,14 @@ remove-user:
@[ -n "$(NAME)" ] || (echo "❌ NAME= обязателен"; exit 1)
@bash scripts/remove-user.sh "$(NAME)"

# make bootstrap — подготовить hermes_data инстансов (каталоги, .env, SOUL.md, права)
bootstrap:
@bash scripts/provision-hermes-data.sh
@echo "✅ hermes_data готов"

# ── Деплой ────────────────────────────────────────────────────────────────

deploy:
deploy: bootstrap
docker compose up -d --build
@echo "✅ Все инстансы запущены"

Expand All @@ -41,15 +46,15 @@ backup:

# ── Безопасность ───────────────────────────────────────────────────────────

# Проверка базовых требований безопасности
# Проверка базовых требований безопасности (поддержка GNU stat и BSD stat на macOS)
security-check:
@echo "🔒 Проверка безопасности..."
@[ -f .env ] && stat -c "%a" .env | grep -qE "^6[04]0$$" \
@[ -f .env ] && (stat -c "%a" .env 2>/dev/null || stat -f "%Lp" .env 2>/dev/null) | grep -qE "^6[04]0$$" \
&& echo " ✅ .env: права 600/640" \
|| echo " ❌ .env: исправь права: chmod 600 .env"
@find instances -name ".env" | while read f; do \
mode=$$(stat -c "%a" $$f); \
echo $$mode | grep -qE "^6[04]0$$" \
mode=$$(stat -c "%a" "$$f" 2>/dev/null || stat -f "%Lp" "$$f" 2>/dev/null); \
echo "$$mode" | grep -qE "^6[04]0$$" \
&& echo " ✅ $$f: $${mode}" \
|| echo " ❌ $$f: chmod 600 $$f"; \
done
Expand Down
Loading
Loading