Compare commits

..
13 changed files with 1085 additions and 303 deletions
+6
View File
@@ -23,6 +23,12 @@ jobs:
- name: Run production inventory placeholder guard self-test
run: bash scripts/check_production_inventory_placeholders.sh --self-test
- name: Run private-config guard
run: bash scripts/check_private_config_guard.sh
- name: Run portal contract sync guard
run: node scripts/check_portal_contract_sync.mjs
rust-runtime-guard:
runs-on: ubuntu-latest
steps:
+31
View File
@@ -0,0 +1,31 @@
name: rust-workspace
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
rust-workspace:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust 1.85
uses: dtolnay/rust-toolchain@1.85.0
with:
components: rustfmt, clippy
- name: Format
run: cargo fmt --manifest-path adk-rust/Cargo.toml --all -- --check
- name: Test
run: cargo test --manifest-path adk-rust/Cargo.toml --workspace
- name: Clippy
run: cargo clippy --manifest-path adk-rust/Cargo.toml --workspace --all-targets -- -D warnings
- name: Release build
run: cargo build --manifest-path adk-rust/Cargo.toml --workspace --release
+6 -2
View File
@@ -1,7 +1,11 @@
# Local secrets
/secrets/
/private-config/*.env
/private-config/*.local
/private-config/*
!/private-config/
!/private-config/README.md
!/private-config/.gitkeep
!/private-config/*.example
!/private-config/*.template
/ansible/inventory.ini
/codex_history.txt
+542 -297
View File
@@ -1,422 +1,667 @@
# Полная инструкция по развёртыванию и поддержке ActivityWatch-Russian
Полная инструкция по развёртыванию и поддержке AWatch-rus
Документ описывает полный цикл: Proxmox/LXC сервер, установка ActivityWatch Server, RU Web UI patch, развёртывание Windows-клиентов в другом AD-домене, валидация, сопровождение и rollback.
Статус документа
---
Этот документ описывает актуальный **Rust-fiWindows/PowerShell deployment flow больше не считается основным способом развёртывания, патчинга или эксплуатации. Если в репозитории остаются старые ".ps1"-файлы, они рассматриваются как legacy/history или как будущий provider-слой, но не как production runtime.
## 0) Структура проекта (полные пути)
0. Назначение
- `<PROJECT_ROOT>/private-config/deploy.env`
- `<PROJECT_ROOT>/proxmox/create-ct.sh`
- `<PROJECT_ROOT>/proxmox/push-aw-artifacts.sh`
- `<PROJECT_ROOT>/aw-server/install_aw_server.sh`
- `<PROJECT_ROOT>/aw-server/apply_webui_ru_patch.sh`
- `<PROJECT_ROOT>/windows/deploy-single-user.ps1`
- `<PROJECT_ROOT>/windows/deploy-domain-users.ps1`
- `<PROJECT_ROOT>/windows/deploy-ensemble.ps1`
- `<PROJECT_ROOT>/windows/hardening-recovery.ps1`
- `<PROJECT_ROOT>/windows/validate-deployment.ps1`
- `<PROJECT_ROOT>/windows/browser-domains-native-collector.ps1`
- `<PROJECT_ROOT>/windows/dlp-endpoint-signals-collector.ps1`
- `<PROJECT_ROOT>/ansible/deploy_aw_server.yml`
- `<PROJECT_ROOT>/ansible/provision_proxmox_ct_and_deploy_aw.yml`
- `<PROJECT_ROOT>/ansible/provision_proxmox_ct_matrix_and_deploy_aw.yml`
- `<PROJECT_ROOT>/ansible/deploy_aw_windows.yml`
AWatch-rus — программный комплекс операционного контроля, технического аудита, оценки трудоотдачи сотрудников и мониторинга корпоративной ИТ-инфраструктуры на базе:
---
- Rust backend/runtime;
- Rust Agent;
- Rust server-rendered HTML + HTMX-compatible JSON API;
- Grafana/Prometheus-витрин;
- модулей Workforce, Security и Forensics;
- evidence/reporting tooling;
- ActivityWatch-compatible источников данных, где это применимо.
## 1) Подготовка
Проект не позиционируется как сертифицированная DLP/SIEM/EDR/XDR/СЗИ. DLP, evidence, UEBA и расследовательские функции используются как внутренние аналитические и операционные модули.
### 1.1 Требования
1. Актуальная архитектура
- Proxmox VE 8/9, доступ root (или sudo с правами на `pct`).
- Шаблон Debian 12 LXC на хосте Proxmox.
- Windows хост(ы) с PowerShell 5.1+ и правами локального администратора.
- Сетевой доступ Windows-клиентов до ActivityWatch Server (`5600/tcp`).
1.1 Основной runtime
### 1.2 Подготовка единого файла секретов
Основной production runtime AWatch-rus — Rust-first:
Скопируйте шаблон:
- backend/runtime — Rust;
- agent — Rust;
- portal — Rust server-rendered HTML + HTMX-compatible JSON API;
- operational status/check — Rust;
- DLP server-side helpers — Rust;
- worktime helpers/exporters/prewarm — Rust;
- SLO/health/readiness helpers — Rust;
- evidence/install-kit tooling — Rust;
- auto-heal helpers — Rust, только в безопасном режиме.
```bash
cp <PROJECT_ROOT>/private-config/deploy.env.example \
<PROJECT_ROOT>/private-config/deploy.env
```
1.2 Что не является основным runtime
Заполните в файле `<PROJECT_ROOT>/private-config/deploy.env`:
Не считать основным production deployment flow:
- все `CT_*` параметры контейнера;
- все `AW_SERVER_*` параметры сервера;
- `CT_PASSWORD` (реальный пароль).
- PowerShell deployment;
- старые Windows ".ps1" rollout scripts;
- ручное исправление production-файлов без release/backup;
- прямое редактирование Web UI в "/opt" без воспроизводимого патча;
- Python/shell как основной operational runtime, если для компонента уже есть Rust-аналог.
Важно: этот файл подхватывается автоматически скриптами Proxmox.
Python, shell, Ansible или PowerShell могут оставаться в проекте только как:
---
- legacy compatibility;
- вспомогательные dev/test tools;
- миграционные сценарии;
- будущие provider-слои;
- Telegram/OCR/AI/ETL/MCP helpers, если они явно не входят в Rust-first core.
## 2) Развёртывание сервера в Proxmox
2. Типовые роли узлов
### 2.0 Ansible full-stack (создание CT + установка AW)
2.1 Server node
Подготовьте:
Серверный узел содержит:
- `<PROJECT_ROOT>/ansible/inventory.ini`
- `<PROJECT_ROOT>/ansible/group_vars/all.yml`
- `<PROJECT_ROOT>/ansible/group_vars/proxmox.yml`
- AWatch-rus backend/runtime;
- portal;
- API;
- exporters;
- health/readiness/status tooling;
- systemd units/timers;
- Grafana/Prometheus integration;
- evidence/reporting storage.
Запуск:
2.2 Agent node
```bash
cd <PROJECT_ROOT>/ansible
ansible-playbook -i inventory.ini provision_proxmox_ct_and_deploy_aw.yml
```
Agent node содержит:
Этот сценарий полностью закрывает:
- Rust Agent;
- локальную конфигурацию агента;
- systemd service или другой штатный supervisor;
- локальные логи;
- буфер/очередь, если предусмотрено конфигурацией;
- сетевой доступ до backend/API.
- создание CT в Proxmox;
- bootstrap пакетов в CT;
- установку ActivityWatch Server;
- применение RU Web UI patch;
- проверку API.
2.3 Monitoring node
Для массового режима (несколько CT):
Monitoring node может содержать:
```bash
cd <PROJECT_ROOT>/ansible
ansible-playbook -i inventory.ini provision_proxmox_ct_matrix_and_deploy_aw.yml
```
- Prometheus;
- Grafana;
- dashboards;
- alerting rules;
- external logs/metrics storage.
### 2.1 Создать LXC контейнер
Monitoring node может совпадать с server node в пилотной установке.
На узле Proxmox:
3. Требования
```bash
cd <PROJECT_ROOT>
<PROJECT_ROOT>/proxmox/create-ct.sh
```
3.1 Базовые требования
По умолчанию читается:
- Linux-сервер или LXC/VM.
- Доступ администратора к systemd.
- Rust toolchain для сборочного узла.
- Сетевой доступ между agent node и server node.
- Закрытый доступ к API и порталу через VPN, reverse proxy или внутренний контур.
- Backup/snapshot перед любым production patch.
- `<PROJECT_ROOT>/private-config/deploy.env`
3.2 Рекомендуемый production-подход
При необходимости можно передать другой путь:
Для production не собирать проект прямо на боевом сервере, если есть отдельный build host.
```bash
<PROJECT_ROOT>/proxmox/create-ct.sh /absolute/path/to/deploy.env
```
Рекомендуемый поток:
### 2.2 Загрузить bootstrap-артефакты и env внутрь CT
git checkout нужного commit/tag
→ cargo fmt / clippy / test / build
→ упаковка release artifacts
→ перенос artifacts на сервер
→ backup/snapshot
→ остановка/перезапуск нужных services
→ smoke tests
→ фиксация версии
```bash
cd <PROJECT_ROOT>
<PROJECT_ROOT>/proxmox/push-aw-artifacts.sh
```
4. Основные пути
Скрипт загружает в CT:
Рекомендуемая структура на сервере:
- `<CT_BOOTSTRAP_DIR>/install_aw_server.sh`
- `<CT_BOOTSTRAP_DIR>/apply_webui_ru_patch.sh`
- `<CT_BOOTSTRAP_DIR>/activitywatch-server.service`
- `<CT_BOOTSTRAP_DIR>/aw-ru-patch.js`
- `<CT_BOOTSTRAP_DIR>/aw-sw-cleanup.js`
- `/etc/activitywatch/aw-server.env` (из `AW_SERVER_*`)
/opt/awatch-rus/
bin/
etc/
portal/
releases/
evidence/
reports/
logs/
### 2.3 Установить ActivityWatch Server внутри CT
/etc/awatch-rus/
awatch-rus.env
agent.env
portal.env
```bash
pct enter <CT_ID>
bash <CT_BOOTSTRAP_DIR>/install_aw_server.sh
```
/var/lib/awatch-rus/
data/
state/
cache/
evidence/
reports/
### 2.4 Применить RU patch Web UI
/var/log/awatch-rus/
backend.log
agent.log
portal.log
exporter.log
```bash
bash <CT_BOOTSTRAP_DIR>/apply_webui_ru_patch.sh
systemctl restart activitywatch-server.service
```
Рекомендуемые runtime binaries:
После применения патча доступны:
/usr/local/bin/detmir-status
/usr/local/bin/detmir-check
/usr/local/bin/detmir-dlp
/usr/local/bin/detmir-auto
/usr/local/bin/detmir-heal-safe
/usr/local/bin/aw-rus-healthd
- верхнее меню `DLP` в Web UI;
- DLP-страница bucket `aw-dlp-endpoint-signals_<HOST>`;
- встроенный центр `DLP review и правила`;
- служебные buckets `aw-dlp-review_<HOST>` и `aw-dlp-rules_<HOST>`.
Имена конкретных бинарников должны соответствовать текущему "Cargo.toml" и фактически собранным artifacts. Если имя binary изменено, документация и systemd unit должны обновляться в том же commit.
### 2.5 Проверка сервера
5. Конфигурация
В CT:
5.1 Общие правила
```bash
systemctl status activitywatch-server.service --no-pager
curl -fsS http://127.0.0.1:5600/api/0/info
ss -ltnp | grep 5600
grep -n 'aw-ru-patch\|aw-sw-cleanup' /opt/activitywatch/webui-ru/index.html
```
- Не хранить production secrets в публичном репозитории.
- Не коммитить реальные hostnames, IP, логины, ФИО, токены, пароли.
- Для production использовать "/etc/awatch-rus/*.env".
- Для demo использовать только обезличенные fixtures.
- Все параметры, влияющие на runtime, должны быть описаны в документации.
Ожидается:
5.2 Пример server env
- сервис `active (running)`;
- API отвечает JSON;
- порт 5600 слушается;
- в `index.html` присутствуют оба скрипта.
AWATCH_ENV=production
AWATCH_BIND_ADDR=127.0.0.1
AWATCH_PORT=5600
AWATCH_DATA_DIR=/var/lib/awatch-rus/data
AWATCH_LOG_DIR=/var/log/awatch-rus
AWATCH_EVIDENCE_DIR=/var/lib/awatch-rus/evidence
AWATCH_REPORTS_DIR=/var/lib/awatch-rus/reports
RUST_LOG=info
Дополнительно после первого входа в Web UI:
5.3 Пример agent env
- `#/home` должен показывать один корректный пункт `DLP`;
- `#/buckets/aw-dlp-endpoint-signals_<HOST>` должен открываться без ошибок;
- сохранение review/rule должно создавать buckets `aw-dlp-review_<HOST>` и `aw-dlp-rules_<HOST>`.
AWATCH_AGENT_ENV=production
AWATCH_SERVER_URL=https://awatch.example.local
AWATCH_AGENT_HOST_ID=HOSTNAME_OR_NODE_ID
AWATCH_AGENT_DATA_DIR=/var/lib/awatch-rus/agent
AWATCH_AGENT_LOG_DIR=/var/log/awatch-rus
RUST_LOG=info
---
6. Сборка
## 3) Развёртывание Windows-клиентов (другой AD-домен)
6.1 Проверки перед сборкой
### 3.1 Подготовка на Windows-хосте
На build host:
Скопируйте каталог:
cd /path/to/AWatch-rus
- `<PROJECT_ROOT>/windows`
git status --short
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
например в:
Если в репозитории есть проектные quality gates, выполнить их обязательно:
- `C:\Program Files\AWatch-rus\windows`
bash scripts/check_private_config_guard.sh
bash scripts/quality-gate.sh
Откройте **elevated PowerShell**:
Если какой-то скрипт отсутствует в текущей ветке, не создавать фиктивную замену. Зафиксировать это в release notes.
```powershell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process
```
6.2 Release build
### 3.2 Массовое доменное развёртывание (рекомендуется)
cargo build --release --workspace
Если текущий production ещё работает в старых каталогах
`C:\Program Files\ActivityWatch-Phase2` и `C:\ProgramData\ActivityWatch-Phase2`,
сначала выполните безопасную миграцию:
Проверить artifacts:
```powershell
C:\Program Files\AWatch-rus\windows\migrate-awatch-rus-paths.ps1 -WhatIf
C:\Program Files\AWatch-rus\windows\migrate-awatch-rus-paths.ps1
```
find target/release -maxdepth 1 -type f -executable -print
Скрипт остановит `ActivityWatch Recovery`/`ActivityWatch Launch *`, создаст backup в
`C:\ProgramData\AWatch-rus\migration-backups\...`, перенесёт файлы в единые пути,
пересоздаст `deployment-config.json`/scheduled tasks и запустит validation.
6.3 Упаковка artifacts
Пример со списком пользователей:
Рекомендуемый вариант:
```powershell
C:\Program Files\AWatch-rus\windows\deploy-domain-users.ps1 `
-ServerHost aw.example.local `
-ServerPort 5600 `
-Domain CONTOSO `
-UserListPath C:\Deploy\aw-users.txt `
-CustomRulesPath C:\Program Files\AWatch-rus\windows\web-category-rules.example.json
```
mkdir -p dist/awatch-rus-release/bin
cp target/release/detmir-status dist/awatch-rus-release/bin/ 2>/dev/null || true
cp target/release/detmir-check dist/awatch-rus-release/bin/ 2>/dev/null || true
cp target/release/detmir-dlp dist/awatch-rus-release/bin/ 2>/dev/null || true
cp target/release/detmir-auto dist/awatch-rus-release/bin/ 2>/dev/null || true
cp target/release/detmir-heal-safe dist/awatch-rus-release/bin/ 2>/dev/null || true
cp target/release/aw-rus-healthd dist/awatch-rus-release/bin/ 2>/dev/null || true
Поддерживаемые варианты:
tar -C dist -czf awatch-rus-release.tar.gz awatch-rus-release
sha256sum awatch-rus-release.tar.gz > awatch-rus-release.tar.gz.sha256
- `-Users user01,user02`
- `-Users 'CONTOSO\user01','CONTOSO\user02'`
- `-UserListPath <txt|csv>`
Не использовать "cp ... || true" в CI без последующей проверки обязательных binaries. Для ручного production release список обязательных binaries должен быть проверен явно.
### 3.2.1 Ensemble orchestration (рекомендуется для production)
7. Первичное развёртывание server node
```powershell
C:\Program Files\AWatch-rus\windows\deploy-ensemble.ps1 `
-ServerHost aw.example.local `
-ServerPort 5600 `
-Domain CONTOSO `
-Users user1,user2,user3,user4,user5 `
-ValidateAfterDeploy
```
7.1 Создание каталогов
Отчёт сохраняется в:
sudo mkdir -p /opt/awatch-rus/bin
sudo mkdir -p /opt/awatch-rus/releases
sudo mkdir -p /etc/awatch-rus
sudo mkdir -p /var/lib/awatch-rus/data
sudo mkdir -p /var/lib/awatch-rus/state
sudo mkdir -p /var/lib/awatch-rus/evidence
sudo mkdir -p /var/lib/awatch-rus/reports
sudo mkdir -p /var/log/awatch-rus
- `C:\ProgramData\AWatch-rus\ensemble-report-YYYYMMDD-HHMMSS.json`
7.2 Установка binaries
### 3.3 Single-user развёртывание
sudo install -m 0755 dist/awatch-rus-release/bin/* /opt/awatch-rus/bin/
```powershell
C:\Program Files\AWatch-rus\windows\deploy-single-user.ps1 `
-ServerHost aw.example.local `
-ServerPort 5600 `
-TargetUser 'CONTOSO\user01' `
-CustomRulesPath C:\Program Files\AWatch-rus\windows\web-category-rules.example.json
```
Создать symlink для удобства:
### 3.4 Recovery / hardening
sudo ln -sf /opt/awatch-rus/bin/detmir-status /usr/local/bin/detmir-status
sudo ln -sf /opt/awatch-rus/bin/detmir-check /usr/local/bin/detmir-check
sudo ln -sf /opt/awatch-rus/bin/detmir-dlp /usr/local/bin/detmir-dlp
```powershell
C:\Program Files\AWatch-rus\windows\hardening-recovery.ps1 `
-ConfigPath C:\ProgramData\AWatch-rus\deployment-config.json
```
Если binary отсутствует, не создавать пустой symlink. Сначала проверить фактический состав release artifact.
### 3.5 Валидация deployment-а (PowerShell report)
7.3 Конфигурация
```powershell
$report = C:\Program Files\AWatch-rus\windows\validate-deployment.ps1 `
-ConfigPath C:\ProgramData\AWatch-rus\deployment-config.json
$report | ConvertTo-Json -Depth 12
```
sudo install -m 0640 awatch-rus.env /etc/awatch-rus/awatch-rus.env
---
Проверить права:
## 4) Что должно появиться на Windows после установки
sudo chown root:root /etc/awatch-rus/awatch-rus.env
sudo chmod 0640 /etc/awatch-rus/awatch-rus.env
- `C:\Program Files\AWatch-rus\bin`
- `C:\ProgramData\AWatch-rus\deployment-config.json`
- `C:\ProgramData\AWatch-rus\launch-watchers.ps1`
- `C:\ProgramData\AWatch-rus\recovery-loop.ps1`
- `C:\ProgramData\AWatch-rus\browser-domains-native-collector.ps1`
- `C:\ProgramData\AWatch-rus\web-category-rules.json`
- `C:\ProgramData\AWatch-rus\logs\`
8. systemd units
Задачи планировщика:
8.1 Пример backend service
- `ActivityWatch Launch [<user>]` (per-user, при логоне)
- `ActivityWatch Recovery` (system-level recovery)
[Unit]
Description=AWatch-rus backend/runtime
After=network-online.target
Wants=network-online.target
---
[Service]
Type=simple
EnvironmentFile=/etc/awatch-rus/awatch-rus.env
ExecStart=/opt/awatch-rus/bin/awatch-rus-backend
Restart=on-failure
RestartSec=5
WorkingDirectory=/opt/awatch-rus
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=/var/lib/awatch-rus /var/log/awatch-rus
## 5) Полная валидация потока данных
[Install]
WantedBy=multi-user.target
### 5.1 На Windows-хосте
Если фактическое имя backend binary отличается, заменить "awatch-rus-backend" на актуальное имя из release artifact.
Проверить процессы:
8.2 Пример health service
```powershell
Get-Process aw-watcher-afk,aw-watcher-window -ErrorAction SilentlyContinue
Get-CimInstance Win32_Process | ? { $_.CommandLine -like '*browser-domains-native-collector.ps1*' } | select ProcessId,SessionId,CommandLine
```
[Unit]
Description=AWatch-rus health daemon
After=network-online.target
Wants=network-online.target
Проверить задачи:
[Service]
Type=simple
EnvironmentFile=/etc/awatch-rus/awatch-rus.env
ExecStart=/opt/awatch-rus/bin/aw-rus-healthd
Restart=on-failure
RestartSec=5
WorkingDirectory=/opt/awatch-rus
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=/var/lib/awatch-rus /var/log/awatch-rus
```powershell
Get-ScheduledTask | ? { $_.TaskName -like 'ActivityWatch*' } | select TaskName,State
```
[Install]
WantedBy=multi-user.target
### 5.2 На сервере ActivityWatch API
8.3 Применение unit files
```bash
curl -sS http://127.0.0.1:5600/api/0/buckets | jq 'keys'
```
sudo systemctl daemon-reload
sudo systemctl enable --now awatch-rus-backend.service
sudo systemctl enable --now aw-rus-healthd.service
Ожидаемые bucket'ы:
Если конкретный unit не используется в текущей инсталляции, не создавать фиктивный сервис. Документировать фактический набор services.
- `aw-watcher-afk_<HOST>`
- `aw-watcher-window_<HOST>`
- `aw-watcher-web-<browser>_<HOST>`
- `aw-detmir-web-category_<HOST>` (категоризованный поток)
- `aw-dlp-endpoint-signals_<HOST>` (endpoint сигналы)
- `aw-dlp-review_<HOST>` (ручная классификация через UI)
- `aw-dlp-rules_<HOST>` (suppress/rule записи через UI)
9. Развёртывание Rust Agent
Проверка событий браузера:
9.1 Установка agent binary
```bash
curl -sS "http://127.0.0.1:5600/api/0/buckets/aw-watcher-web-edge_<HOST>/events?limit=5" | jq
```
sudo mkdir -p /opt/awatch-rus/bin
sudo mkdir -p /etc/awatch-rus
sudo mkdir -p /var/lib/awatch-rus/agent
sudo mkdir -p /var/log/awatch-rus
Проверка категоризации:
sudo install -m 0755 awatch-rus-agent /opt/awatch-rus/bin/awatch-rus-agent
sudo install -m 0640 agent.env /etc/awatch-rus/agent.env
```bash
curl -sS "http://127.0.0.1:5600/api/0/buckets/aw-detmir-web-category_<HOST>/events?limit=5" | jq
```
9.2 Пример agent service
Проверка DLP review/rules:
[Unit]
Description=AWatch-rus Rust Agent
After=network-online.target
Wants=network-online.target
```bash
curl -sS "http://127.0.0.1:5600/api/0/buckets/aw-dlp-review_<HOST>/events?limit=20" | jq
curl -sS "http://127.0.0.1:5600/api/0/buckets/aw-dlp-rules_<HOST>/events?limit=20" | jq
```
[Service]
Type=simple
EnvironmentFile=/etc/awatch-rus/agent.env
ExecStart=/opt/awatch-rus/bin/awatch-rus-agent
Restart=on-failure
RestartSec=5
WorkingDirectory=/opt/awatch-rus
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=/var/lib/awatch-rus /var/log/awatch-rus
Ожидаемые поля review:
[Install]
WantedBy=multi-user.target
- `reviewId`
- `signalType`
- `verdict`
- `category`
- `comment`
- `archived`
9.3 Запуск agent
Ожидаемые поля rules:
sudo systemctl daemon-reload
sudo systemctl enable --now awatch-rus-agent.service
sudo systemctl status awatch-rus-agent.service --no-pager
- `ruleId`
- `signalType`
- `match`
- `category`
- `comment`
- `enabled`
10. Развёртывание портала
---
Портальный слой AWatch-rus зафиксирован как Rust server-rendered HTML + HTMX-compatible JSON API.
## 6) Сопровождение (обязательно)
10.1 Общий порядок
### 6.1 Backup перед любыми изменениями
build portal/backend binary
→ install binary
→ install templates/static assets, если они выделены отдельно
→ update portal env
→ restart portal service
→ smoke check HTTP/API routes
На Proxmox:
10.2 Проверка портала
curl -fsS http://127.0.0.1:5600/healthz
curl -fsS http://127.0.0.1:5600/readyz
curl -fsS http://127.0.0.1:5600/version
Если конкретные endpoints в текущей версии отличаются, использовать фактически реализованные health/readiness/version endpoints и обновить этот документ в том же commit.
11. Патчи в развернутой среде
11.1 Правило
Любой production patch применяется только через контролируемый цикл:
определить commit/tag
→ собрать release artifact
→ выполнить локальные проверки
→ сделать backup/snapshot
→ установить новые binaries/configs
→ restart/reload services
→ smoke tests
→ зафиксировать результат
→ сохранить rollback path
11.2 Перед патчем
git rev-parse HEAD
git status --short
Сохранить:
дата/время
commit/tag
кто применяет
какие services затрагиваются
какой rollback path
11.3 Backup перед патчем
Если используется Proxmox/LXC:
```bash
vzdump <CT_ID> --mode snapshot --compress zstd --storage <BACKUP_STORAGE>
```
Конфиги внутри CT:
Внутри сервера:
```bash
pct exec <CT_ID> -- tar -C / -czf <PRIVATE_BACKUP_DIR>/activitywatch-config-backup.tgz \
etc/activitywatch \
etc/systemd/system/activitywatch-server.service \
opt/activitywatch/webui-ru \
opt/activitywatch/releases
```
sudo tar -C / -czf /root/awatch-rus-backup-$(date +%Y%m%d-%H%M%S).tgz \
etc/awatch-rus \
opt/awatch-rus \
var/lib/awatch-rus \
var/log/awatch-rus
### 6.2 Обновление сервера
Если данные большие, backup "/var/lib/awatch-rus" выполнять отдельной процедурой согласно backup policy.
1. Обновить `AW_SERVER_VERSION` и `AW_SERVER_DOWNLOAD_URL` в
`<PROJECT_ROOT>/private-config/deploy.env`
2. Выполнить:
11.4 Установка нового binary
```bash
<PROJECT_ROOT>/proxmox/push-aw-artifacts.sh
pct enter <CT_ID>
bash <CT_BOOTSTRAP_DIR>/install_aw_server.sh
bash <CT_BOOTSTRAP_DIR>/apply_webui_ru_patch.sh
systemctl restart activitywatch-server.service
```
Сохранить предыдущую версию:
3. Повторить валидацию API/UI.
sudo mkdir -p /opt/awatch-rus/releases/previous
sudo cp -a /opt/awatch-rus/bin /opt/awatch-rus/releases/previous/bin-$(date +%Y%m%d-%H%M%S)
### 6.3 Rollback
Установить новый artifact:
RU patch rollback:
sudo install -m 0755 dist/awatch-rus-release/bin/* /opt/awatch-rus/bin/
```bash
cp /opt/activitywatch/webui-ru/index.html.bak.<timestamp> /opt/activitywatch/webui-ru/index.html
systemctl restart activitywatch-server.service
```
11.5 Restart services
Полный rollback:
sudo systemctl daemon-reload
sudo systemctl restart awatch-rus-backend.service
sudo systemctl restart aw-rus-healthd.service
- восстановить CT из snapshot/backup;
- проверить API и Web UI;
- проверить доступность для Windows-клиентов.
Если патч касается только agent:
---
sudo systemctl restart awatch-rus-agent.service
## 7) Безопасность
Если сервис в текущем контуре называется иначе, использовать фактическое имя systemd unit.
- Не хранить реальные приватные параметры вне `<PROJECT_ROOT>/private-config/deploy.env`.
- Не открывать `5600/tcp` в интернет напрямую.
- Публиковать через VPN или reverse proxy с ограничением доступа.
- Перед изменениями всегда делать backup.
12. Smoke-тесты после патча
---
12.1 Systemd
## 8) Короткий чек-лист ввода в эксплуатацию
systemctl --failed --no-pager
systemctl status awatch-rus-backend.service --no-pager
systemctl status aw-rus-healthd.service --no-pager
1. Заполнен `<PROJECT_ROOT>/private-config/deploy.env`.
2. Выполнен `<PROJECT_ROOT>/proxmox/create-ct.sh`.
3. Выполнен `<PROJECT_ROOT>/proxmox/push-aw-artifacts.sh`.
4. В CT выполнены `<CT_BOOTSTRAP_DIR>/install_aw_server.sh` и `<CT_BOOTSTRAP_DIR>/apply_webui_ru_patch.sh`.
5. Сервер API/порт/UI проверены.
6. На Windows выполнен `deploy-domain-users.ps1`.
7. Проверены процессы, задачи и bucket'ы.
8. Зафиксированы параметры и дата ввода.
12.2 Rust operational checks
detmir-status --json
detmir-check --json
detmir-dlp --json
Если отдельная команда не установлена в данном контуре, это не считается ошибкой только при наличии документированного исключения.
12.3 HTTP/API
curl -fsS http://127.0.0.1:5600/healthz
curl -fsS http://127.0.0.1:5600/readyz
curl -fsS http://127.0.0.1:5600/version
12.4 Portal smoke
Проверить в браузере:
/portal
/portal/reports
/portal/architecture
Для Pilot v1 проверить роли:
executive
manager
security
forensics
admin
12.5 Data freshness
Проверить, что витрины и отчёты не пустые из-за сбоя сбора:
последние события поступают
worktime reports обновляются
DLP/security events отображаются, если включены
evidence/reporting не падает
Grafana dashboards открываются
13. Rollback
13.1 Быстрый rollback binary
Найти предыдущий backup:
ls -lah /opt/awatch-rus/releases/previous/
Восстановить:
sudo rsync -a --delete /opt/awatch-rus/releases/previous/bin-YYYYMMDD-HHMMSS/ /opt/awatch-rus/bin/
sudo systemctl restart awatch-rus-backend.service
sudo systemctl restart aw-rus-healthd.service
13.2 Rollback конфигурации
sudo cp /etc/awatch-rus/awatch-rus.env.bak /etc/awatch-rus/awatch-rus.env
sudo systemctl restart awatch-rus-backend.service
13.3 Rollback CT/VM
Если повреждение затрагивает runtime, данные или systemd-конфигурацию:
остановить сервисы
восстановить snapshot/backup
проверить health/readiness/version
проверить портал
проверить поступление данных
зафиксировать incident note
14. Monitoring
14.1 Что должно контролироваться
- service status;
- process uptime;
- API health/readiness;
- latency;
- error rate;
- freshness данных;
- заполненность диска;
- размер логов;
- успешность exporters;
- SLO status;
- agent coverage;
- отсутствие failed systemd units.
14.2 Grafana
В Grafana должны быть разделены витрины:
- executive dashboard;
- security dashboard;
- operations dashboard;
- RDP/user activity dashboard;
- data quality/freshness dashboard;
- DLP/evidence dashboard, если модуль включён.
14.3 Prometheus
Prometheus scrape должен быть доступен только из внутреннего контура мониторинга. Не открывать metrics endpoints наружу.
15. Security hardening
Обязательные правила:
- не публиковать API напрямую в интернет;
- использовать VPN/reverse proxy/access control;
- закрыть лишние порты;
- хранить secrets вне git;
- ограничить права systemd services;
- использовать отдельного service user, если это поддерживается текущей установкой;
- включить backup;
- проверять логи после каждого патча;
- не использовать demo fixtures как production data;
- не смешивать реальные ФИО/IP/hostname с публичными demo screenshots.
16. Проверка перед вводом в эксплуатацию
Минимальный checklist:
[ ] выбран commit/tag release
[ ] cargo fmt прошёл
[ ] cargo clippy прошёл
[ ] cargo test прошёл
[ ] cargo build --release прошёл
[ ] private config guard прошёл
[ ] backup/snapshot создан
[ ] binaries установлены
[ ] systemd services запущены
[ ] health/readiness/version отвечают
[ ] detmir-status/check/dlp работают
[ ] portal открывается
[ ] роли Pilot v1 проверены
[ ] Grafana dashboards открываются
[ ] данные поступают
[ ] rollback path известен
[ ] дата/commit/оператор зафиксированы
17. Что больше не использовать как основной путь
Не использовать как основной production flow:
windows/deploy-single-user.ps1
windows/deploy-domain-users.ps1
windows/deploy-ensemble.ps1
windows/validate-deployment.ps1
windows/hardening-recovery.ps1
windows/browser-domains-native-collector.ps1
windows/dlp-endpoint-signals-collector.ps1
Если эти файлы физически остаются в репозитории, они должны быть явно помечены как:
legacy
planned provider
migration-only
dev/test helper
Они не должны описываться в основном deployment manual как обязательный production-путь.
18. Короткий production runbook
18.1 Развернуть
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
cargo build --release --workspace
sudo install -m 0755 target/release/<binary> /opt/awatch-rus/bin/<binary>
sudo systemctl daemon-reload
sudo systemctl restart <service>.service
18.2 Проверить
systemctl --failed --no-pager
detmir-status --json
detmir-check --json
curl -fsS http://127.0.0.1:5600/healthz
curl -fsS http://127.0.0.1:5600/readyz
curl -fsS http://127.0.0.1:5600/version
18.3 Откатить
sudo rsync -a --delete /opt/awatch-rus/releases/previous/bin-YYYYMMDD-HHMMSS/ /opt/awatch-rus/bin/
sudo systemctl restart <service>.service
19. Правило актуализации этого документа
Если меняется:
- имя binary;
- имя systemd unit;
- порт;
- endpoint;
- путь хранения данных;
- способ сборки;
- способ доставки artifacts;
- smoke-test;
- rollback procedure;
то этот файл должен обновляться в том же commit, что и изменение кода или deployment-конфигурации.
+111
View File
@@ -0,0 +1,111 @@
# RC Evidence Pack: Pilot v1
Документ фиксирует доказательства финальной проверки release candidate процесса для ветки `hardening/pilot-v1-defects-cleanup`.
## Идентификаторы проверки
- Branch/ref: `origin/hardening/pilot-v1-defects-cleanup`
- Commit: `a8c0482e760cc17b53182999355f65c17457d7f2`
- Commit short: `a8c0482`
- Дата проверки: `2026-06-12`
- Clean worktree: `<LOCAL_VALIDATION_WORKTREE>/AWatch-rus-rc-validation-a8c0482`
- RC name: `v1.0.2-rc-validation`
- RC output: `dist/release-candidate/v1.0.2-rc-validation/`
- `CARGO_TARGET_DIR`: `$HOME/.cache/aw-rus-hardening-target`
Абсолютный путь локального операторского home-каталога намеренно не фиксируется в tracked-документации. Это не влияет на воспроизводимость: команда использует стандартный `$HOME`.
## Команды проверки
Preflight без вынесенного target dir:
```bash
bash scripts/build_release_candidate.sh --preflight
```
Preflight с вынесенным cargo target dir:
```bash
CARGO_TARGET_DIR=$HOME/.cache/aw-rus-hardening-target \
bash scripts/build_release_candidate.sh --preflight
```
Полная RC-сборка:
```bash
CARGO_TARGET_DIR=$HOME/.cache/aw-rus-hardening-target \
bash scripts/build_release_candidate.sh v1.0.2-rc-validation
```
Команда полной сборки без имени RC проверена отдельно и корректно завершается с `exit=2`, потому что первый аргумент обязателен.
## Созданные RC artifacts
В каталоге `dist/release-candidate/v1.0.2-rc-validation/` созданы:
- `FILES.txt`
- `SHA256SUMS.txt`
- `SHA256SUMS-v0.2.txt`
- `git-commit.txt`
- `RELEASE_ASSETS_MANIFEST-v0.2.json`
- `sbom/cargo-metadata-v0.2.json`
- `sbom/cargo-tree-v0.2.txt`
- `sbom/cyclonedx-rust-v0.2.json`
- `sbom/python-inputs-v0.2.txt`
- `sbom/spdx-rust-v0.2.json`
`git-commit.txt` содержит `a8c0482e760cc17b53182999355f65c17457d7f2`.
## Artifact verification
Подтверждено:
- `sha256sum -c SHA256SUMS.txt`: OK
- `sha256sum -c SHA256SUMS-v0.2.txt`: OK
- JSON parse для `RELEASE_ASSETS_MANIFEST-v0.2.json`: OK
- JSON parse для `sbom/cargo-metadata-v0.2.json`: OK
- JSON parse для `sbom/cyclonedx-rust-v0.2.json`: OK
- JSON parse для `sbom/spdx-rust-v0.2.json`: OK
- `FILES.txt` соответствует фактическому набору checksum-covered файлов: OK
Повторный запуск с тем же `RC_NAME` блокируется сообщением `release candidate output already exists`; существующий `SHA256SUMS.txt` не изменяется.
## Dirty-tree guard
Clean-tree requirement сохранен и проверен двумя сценариями:
- non-ignored untracked file блокирует настоящую RC-сборку;
- tracked modification блокирует настоящую RC-сборку.
В обоих случаях скрипт завершается до создания RC-каталога. Ignored files намеренно не блокируют сборку, иначе `dist/` ломал бы повторные проверки и локальную валидацию артефактов.
## dist/ и git
Подтверждено:
- `git ls-files dist` возвращает `0` tracked files;
- `git status --ignored dist` показывает `!! dist/`;
- `dist/` не добавляется в git и остается локальным output-каталогом.
## Обязательные проверки
В clean validation worktree выполнены:
- `bash -n scripts/build_release_candidate.sh`: OK
- `bash scripts/build_release_candidate.sh --preflight`: OK
- `CARGO_TARGET_DIR=$HOME/.cache/aw-rus-hardening-target bash scripts/build_release_candidate.sh --preflight`: OK
- `git diff --check`: OK
- `bash scripts/check_private_config_guard.sh`: OK
- `node scripts/check_portal_contract_sync.mjs`: OK
- `bash scripts/quality-gate.sh`: OK
Внутри полной RC-сборки также прошли:
- `cargo fmt --manifest-path adk-rust/Cargo.toml --all -- --check`
- `cargo test --manifest-path adk-rust/Cargo.toml --workspace`
- `cargo clippy --manifest-path adk-rust/Cargo.toml --workspace --all-targets -- -D warnings`
- `cargo build --manifest-path adk-rust/Cargo.toml --workspace --release`
## Вывод
Release candidate процесс подтвержден как воспроизводимый в чистом рабочем дереве. Clean-tree requirement сохранен. Сборка не требует ослабления защитных проверок. Ветка готова к review и merge в main.
+108
View File
@@ -0,0 +1,108 @@
# Release Candidate Runbook
Этот документ описывает техническую сборку Release Candidate для AWatch-rus. RC-сборка нужна, чтобы одной воспроизводимой командой собрать проверенные артефакты, зафиксировать git commit, сформировать SBOM/manifest/checksums и сложить результат в отдельный каталог под конкретное имя кандидата.
Release Candidate не равен юридической готовности к подаче в реестр и не заменяет финальную процедуру релиза.
## Evidence pack
Финальная проверка RC-процесса для ветки `hardening/pilot-v1-defects-cleanup` зафиксирована в `docs/RC_EVIDENCE_PACK_PILOT_V1_RU.md`.
## Запуск
Команда выполняется из корня репозитория:
```bash
bash scripts/build_release_candidate.sh v1.0.2-rc1
```
Первый аргумент обязателен. Имя кандидата используется как имя каталога в `dist/release-candidate/`, поэтому скрипт требует начало с буквы или цифры и дальше принимает только буквы, цифры, точку, подчеркивание и дефис.
Перед сборкой рабочее дерево git должно быть чистым. Если есть незакоммиченные, staged или untracked файлы, скрипт завершится с ошибкой. Это защищает RC от незафиксированного состояния.
## Preflight
Перед полной RC-сборкой можно проверить локальные предпосылки без создания каталога release candidate и без запуска cargo build/test:
```bash
bash scripts/build_release_candidate.sh --preflight
```
Preflight проверяет наличие команд `git`, `cargo`, `bash`, `node`, `sha256sum`, наличие обязательных внутренних скриптов, а также то, что `dist/` игнорируется git. Этот режим не требует чистого git tree, не создает артефакты и не заменяет полную RC-сборку.
## Если проект лежит на USB/HDD mount
На локальном контуре проект может лежать под `/mnt/` или `/media/`. В таком случае cargo build artifacts в стандартном `adk-rust/target` могут падать на filesystem-ограничениях mount, например на `libsqlite3-sys` с `Operation not permitted`.
Рекомендуемый запуск для такого контура:
```bash
CARGO_TARGET_DIR=$HOME/.cache/aw-rus-hardening-target bash scripts/build_release_candidate.sh v1.0.2-rc1
```
Это не обход проверок. Все `cargo fmt`, `cargo test`, `cargo clippy`, `cargo build`, `quality-gate`, private-config guard, OpenAPI contract guard и SBOM generation продолжают выполняться. Меняется только место, куда cargo складывает build artifacts.
`dist/` по-прежнему не коммитится. Требование чистого git tree для настоящей RC-сборки также остается обязательным.
## Проверки
Скрипт выполняет обязательные проверки и сборку Rust workspace:
```bash
cargo fmt --manifest-path adk-rust/Cargo.toml --all -- --check
cargo test --manifest-path adk-rust/Cargo.toml --workspace
cargo clippy --manifest-path adk-rust/Cargo.toml --workspace --all-targets -- -D warnings
cargo build --manifest-path adk-rust/Cargo.toml --workspace --release
bash scripts/quality-gate.sh
bash scripts/check_private_config_guard.sh
node scripts/check_portal_contract_sync.mjs
```
Если любая проверка падает, RC-сборка считается несостоявшейся.
Неполный output-каталог при ошибке удаляется, чтобы не смешивать частичные артефакты с валидной сборкой.
## Артефакты
Результат складывается в:
```text
dist/release-candidate/<RC_NAME>/
```
Для примера выше итоговый каталог будет:
```text
dist/release-candidate/v1.0.2-rc1/
```
В каталоге создаются:
- `git-commit.txt` - commit, из которого собран кандидат;
- `FILES.txt` - список файлов, покрытых итоговыми checksum, кроме самого `SHA256SUMS.txt`;
- `SHA256SUMS.txt` - SHA-256 для всех файлов каталога, кроме самого `SHA256SUMS.txt`;
- `sbom/` - SBOM-файлы, созданные существующим генератором `scripts/generate_release_sbom_v0_2.sh`;
- `RELEASE_ASSETS_MANIFEST-v0.2.json` и `SHA256SUMS-v0.2.txt` - manifest/checksums, которые формирует существующий SBOM generator.
Каталог `dist/` не предназначен для коммита в git.
## Проверка checksum
Для проверки итоговых checksum:
```bash
cd dist/release-candidate/v1.0.2-rc1
sha256sum -c SHA256SUMS.txt
```
Ожидаемый результат - `OK` для всех записей. Любая ошибка означает, что набор артефактов изменился после сборки или поврежден.
## Перед реальной подачей
Release Candidate подтверждает техническую воспроизводимость сборки, но перед реальной подачей все еще нужны:
- release tag;
- release-specific SBOM;
- license review;
- signed/checksummed artifacts;
- проверка отсутствия live/private data;
- финальные install/user/admin guide под конкретную версию.
-4
View File
@@ -1,4 +0,0 @@
*.env
*.local
!.gitignore
!*.example
+1
View File
@@ -0,0 +1 @@
+13
View File
@@ -0,0 +1,13 @@
# private-config
This directory is reserved for local, host-specific, or secret configuration.
Do not commit real runtime values here. Git only allows:
- `private-config/README.md`
- `private-config/.gitkeep`
- `private-config/*.example`
- `private-config/*.template`
Use `scripts/check_private_config_guard.sh` before commits and in CI to verify
that no private config file has entered the git index.
+159
View File
@@ -0,0 +1,159 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"
print_cargo_target_dir() {
if [[ -n "${CARGO_TARGET_DIR:-}" ]]; then
echo "CARGO_TARGET_DIR=$CARGO_TARGET_DIR"
else
echo "CARGO_TARGET_DIR is not set; cargo default target dir will be used"
fi
}
preflight_ok() {
echo "[OK] $1"
}
preflight_fail() {
echo "[FAIL] $1" >&2
PREFLIGHT_FAILED=1
}
check_command() {
local command_name="$1"
if command -v "$command_name" >/dev/null 2>&1; then
preflight_ok "command available: $command_name"
else
preflight_fail "missing command: $command_name"
fi
}
check_file() {
local path="$1"
if [[ -f "$path" ]]; then
preflight_ok "required file exists: $path"
else
preflight_fail "required file is missing: $path"
fi
}
run_preflight() {
PREFLIGHT_FAILED=0
echo "release candidate preflight"
print_cargo_target_dir
check_command git
check_command cargo
check_command bash
check_command node
check_command sha256sum
check_file scripts/generate_release_sbom_v0_2.sh
check_file scripts/check_private_config_guard.sh
check_file scripts/check_portal_contract_sync.mjs
if command -v git >/dev/null 2>&1; then
if git check-ignore -q dist/release-candidate/.preflight-probe; then
preflight_ok "dist/ is ignored by git"
else
preflight_fail "dist/ is not ignored by git"
fi
else
preflight_fail "cannot verify git ignore rules without git"
fi
case "$ROOT_DIR" in
/mnt/*|/media/*)
cat <<'EOF'
[HINT] Project is under /mnt or /media. If cargo fails on the mount with Operation not permitted, run the full RC build with a writable target dir:
CARGO_TARGET_DIR=/home/igor/.cache/aw-rus-hardening-target bash scripts/build_release_candidate.sh v1.0.2-rc1
EOF
;;
esac
if [[ "$PREFLIGHT_FAILED" -ne 0 ]]; then
echo "release candidate preflight: FAIL" >&2
return 1
fi
echo "release candidate preflight: OK"
}
if [[ "${1:-}" == "--preflight" ]]; then
run_preflight
exit $?
fi
print_cargo_target_dir
RC_NAME="${1:-}"
if [[ -z "$RC_NAME" ]]; then
cat >&2 <<'EOF'
usage: bash scripts/build_release_candidate.sh <rc-name>
example: bash scripts/build_release_candidate.sh v1.0.2-rc1
preflight: bash scripts/build_release_candidate.sh --preflight
EOF
exit 2
fi
if [[ ! "$RC_NAME" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]]; then
echo "invalid release candidate name: start with a letter or number; use only letters, numbers, dot, underscore, and hyphen" >&2
exit 2
fi
if [[ -n "$(git status --porcelain --untracked-files=normal)" ]]; then
echo "git working tree is not clean; commit, stash, or remove changes before building release candidate" >&2
git status --short >&2
exit 1
fi
OUT_DIR="$ROOT_DIR/dist/release-candidate/$RC_NAME"
if [[ -e "$OUT_DIR" ]]; then
echo "release candidate output already exists: $OUT_DIR" >&2
exit 1
fi
BUILD_SUCCESS=0
cleanup_on_failure() {
status=$?
if [[ $status -ne 0 && $BUILD_SUCCESS -ne 1 && -d "$OUT_DIR" ]]; then
rm -rf "$OUT_DIR"
fi
exit "$status"
}
trap cleanup_on_failure EXIT
mkdir -p "$OUT_DIR"
git rev-parse HEAD > "$OUT_DIR/git-commit.txt"
cargo fmt --manifest-path adk-rust/Cargo.toml --all -- --check
cargo test --manifest-path adk-rust/Cargo.toml --workspace
cargo clippy --manifest-path adk-rust/Cargo.toml --workspace --all-targets -- -D warnings
cargo build --manifest-path adk-rust/Cargo.toml --workspace --release
bash scripts/quality-gate.sh
bash scripts/check_private_config_guard.sh
node scripts/check_portal_contract_sync.mjs
bash scripts/generate_release_sbom_v0_2.sh "$OUT_DIR"
(
cd "$OUT_DIR"
{
printf '%s\n' "FILES.txt"
find . -type f ! -name 'FILES.txt' ! -name 'SHA256SUMS.txt' -print \
| sort \
| sed 's#^\./##'
} > FILES.txt
find . -type f ! -name 'SHA256SUMS.txt' -print0 \
| sort -z \
| xargs -0 sha256sum > SHA256SUMS.txt
)
BUILD_SUCCESS=1
echo "release candidate built: $OUT_DIR"
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env node
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const root = path.resolve(__dirname, "..");
const contractPath = path.join(
root,
"adk-rust/crates/detmir-portal/src/contracts/openapi.json",
);
const requiredPublicPaths = [
"/api/contracts",
"/api/contracts/openapi.json",
"/api/contracts/typescript.d.ts",
"/api/reports",
"/api/executive",
"/api/workforce",
"/api/security",
"/api/forensics",
"/api/ueba",
"/api/pfsense",
"/api/incidents",
"/api/cases",
"/api/readiness/latest",
"/api/readiness/bundle",
"/api/readiness/verify",
];
function fail(message, details = []) {
console.error(message);
for (const detail of details) {
console.error(`- ${detail}`);
}
process.exit(1);
}
let contract;
try {
contract = JSON.parse(fs.readFileSync(contractPath, "utf8"));
} catch (error) {
fail(`failed to read OpenAPI contract: ${contractPath}`, [error.message]);
}
if (!contract || typeof contract !== "object" || !contract.paths || typeof contract.paths !== "object") {
fail("OpenAPI contract has no object 'paths' section.");
}
const contractPaths = Object.keys(contract.paths);
const forbiddenPaths = contractPaths.filter((contractPathName) =>
/dioxus|prototype-mirror|mirror/i.test(contractPathName),
);
if (forbiddenPaths.length > 0) {
fail("OpenAPI contract contains legacy/prototype paths.", forbiddenPaths);
}
const effectivePublicPaths = new Set();
for (const contractPathName of contractPaths) {
effectivePublicPaths.add(contractPathName);
if (contractPathName.startsWith("/") && !contractPathName.startsWith("/api/")) {
effectivePublicPaths.add(`/api${contractPathName}`);
}
}
const missingPaths = requiredPublicPaths.filter((requiredPath) => !effectivePublicPaths.has(requiredPath));
if (missingPaths.length > 0) {
fail("OpenAPI contract is missing required public API paths.", missingPaths);
}
console.log("portal contract sync guard: OK");
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -euo pipefail
violations=()
while IFS= read -r -d '' path; do
rest="${path#private-config/}"
case "$path" in
private-config/README.md|private-config/.gitkeep)
continue
;;
esac
if [[ "$rest" != */* && ( "$rest" == *.example || "$rest" == *.template ) ]]; then
continue
fi
violations+=("$path")
done < <(git ls-files -z -- private-config)
if (( ${#violations[@]} > 0 )); then
printf 'private-config guard failed: tracked private files are forbidden. Allowed files are README.md, .gitkeep, *.example, *.template.\\n' >&2
printf '%s\\n' "${violations[@]}" >&2
exit 1
fi
echo "private-config guard: OK"
+11
View File
@@ -7,6 +7,16 @@ cd "$ROOT_DIR"
TARGET_ROOT="${CARGO_TARGET_DIR:-$ROOT_DIR/adk-rust/target}"
RUST_BIN="${QUALITY_GATE_RUST:-}"
echo "[preflight] Private-config guard"
bash scripts/check_private_config_guard.sh
echo "[preflight] Portal contract sync guard"
if command -v node >/dev/null 2>&1; then
node scripts/check_portal_contract_sync.mjs
else
echo "node not found, skipping portal contract sync guard."
fi
rust_candidates=()
if [[ -n "$RUST_BIN" ]]; then
rust_candidates+=("$RUST_BIN")
@@ -39,6 +49,7 @@ fi
echo "[3/6] Node syntax check (if node available)"
if command -v node >/dev/null 2>&1; then
node --check scripts/aw-webui-browser-smoke.mjs >/dev/null
node --check scripts/check_portal_contract_sync.mjs >/dev/null
else
echo "node not found, skipping."
fi