100 KiB
Runbook: перевод DetMir на Rust / ADK-Rust
Дата фиксации: 2026-06-01
Цель: постепенно заменить хрупкие Python/shell operational scripts на самодостаточные Rust-бинарники, не ломая текущий production-контур DetMir.
Этот runbook является рабочим планом миграции. Если фактический runtime расходится с этим документом, сначала фиксируется baseline, затем обновляется runbook.
Текущий resume snapshot проекта хранится в
.ai/runtime/detmir-current-session.md. Перед продолжением миграции после
перерыва или context reset сначала читать его, затем RUNBOOK.md и
<OPERATOR_CODEX_HOME>/skills/detmir-rust-migration/references/current-state.md.
1. Целевое состояние
В результате миграции DetMir должен иметь:
- единый Rust workspace в
adk-rust/; - один standalone-бинарник или crate на каждую операционную функцию;
- стабильные machine-readable JSON-контракты вместо парсинга human text;
- одинаковые exit codes для старых и новых команд;
- systemd units/timers, не зависящие от ноутбука, venv, pip и локальных путей;
- безопасный auto-heal с dry-run, lock, cooldown, allowlist и rollback;
- ADK-compatible envelopes там, где вывод передается агентам/боту/LLM.
Rust не должен использоваться как самоцель. Если компонент надежнее оставить в Ansible, PowerShell или Playwright, он остается там до появления практической причины для переноса.
2. Текущая отправная точка
Уже создано:
adk-rust/Cargo.toml- workspace;adk-rust/crates/detmir-status- read-only status module with text, JSON and ADK JSON output;/usr/local/bin/detmir-statusна Proxmox, проверен против/var/lib/detmir-ai/latest-state.json;/usr/local/bin/detmir-adk-statusна Proxmox, проверен против/var/lib/detmir-ai/latest-state.jsonкак compatibility binary.
Ключевые текущие legacy-компоненты:
| Компонент | Текущая реализация | Риск переноса |
|---|---|---|
detmir-status |
Rust binary + thin compatibility wrapper | выполнено |
detmir-check |
Rust binary + thin compatibility wrapper | выполнено |
detmir-dlp |
Rust SSH wrapper + thin compatibility wrapper | выполнено |
detmir-auto |
Rust production via systemd drop-in + legacy script retained | switched |
detmir-heal-safe |
Rust binary deployed + legacy script retained | switched for Rust auto |
tsj_guardian_watchdog.sh |
Rust service via systemd drop-in + legacy shell retained | switched |
Telegram /status/decision backend |
Rust helper + permanent Python bot runtime | backend only |
aw-rus-healthd |
Rust production unit, Python entrypoint removed from repo | done |
dlp-health-check |
Rust production binary, Python entrypoint removed from repo | done |
| DLP aggregator | Rust production via systemd drop-in + legacy Python retained | switched |
| AW DLP Influx exporter | Rust production via systemd drop-in + legacy Python retained | switched |
| AW worktime Influx exporter | Rust production via systemd drop-in + legacy Python retained | switched |
| AW worktime prewarm | Rust production via systemd drop-in + legacy shell retained | switched |
| AW worktime API | Rust production via systemd drop-in + legacy Python retained | switched |
| DLP syslog forwarder | Rust production via systemd drop-in + legacy Python retained | switched |
| DLP webhook sender | Rust production via systemd drop-in + legacy Python retained | switched |
| DLP CEF exporter | Rust production via systemd drop-in + legacy Python retained | switched |
tsj_guardian_bot.py |
Permanent Python Telegram runtime | не переносить |
| AW remaining worktime modules | mostly Rust production; inspect leftovers before next item | средний |
| deploy/install scripts | shell/Ansible/PowerShell | высокий, переносить последними |
3. Архитектура workspace
Целевая структура:
adk-rust/
Cargo.toml
Cargo.lock
RUNBOOK.md
crates/
detmir-core/
detmir-aw-client/
detmir-systemd/
detmir-state/
detmir-status/
detmir-check/
detmir-dlp/
detmir-auto/
detmir-heal-safe/
tsj-guardian-status/
tsj-guardian-watchdog/
Назначение shared crates:
| Crate | Назначение |
|---|---|
detmir-core |
ошибки, CLI output, timeouts, retry, exit codes, config loading |
detmir-aw-client |
ActivityWatch HTTP API, buckets, events, timestamps |
detmir-systemd |
безопасный wrapper вокруг systemctl, allowlist, dry-run |
detmir-state |
чтение/запись /var/lib/detmir-ai, atomic writes, retention |
detmir-report |
Markdown/JSON reports, ADK envelopes, report bundle assembly |
Бинарные модули должны быть маленькими и собираться из shared crates. Логику не
дублировать между check, status, auto и ботом.
4. Общие контракты для каждого Rust-модуля
Каждый новый модуль обязан иметь:
--jsonдля машинного вывода;--prettyили обычный text output для человека, если команда операторская;--config <path>либо documented env vars;--dry-runдля любых действий, которые меняют состояние;--timeoutтам, где есть сеть/SSH/HTTP;--no-colorесли вывод может попадать в systemd/Telegram/report;- стабильные exit codes:
0- OK;1- usage/config/runtime error;2- проверка выполнена, но состояние WARN/FAIL;3- action запрещен safety policy;
- structured logs через
tracingдля daemon/action-команд; - unit tests на чистую логику;
- fixture tests на реальные JSON samples;
- README с примером локального и серверного запуска.
5. Safety policy
Нельзя сразу заменять управляющие скрипты без shadow-mode.
Обязательные правила:
- сначала read-only parity, потом mutation;
- старый и новый модуль должны некоторое время работать параллельно;
- новый модуль не получает право писать state, рестартить сервисы или удалять файлы до прохождения acceptance gates;
- все state writes только через atomic temp file + rename;
- heal-команды только по allowlist units;
- no implicit sudo: если нужен
sudo, он должен быть явно виден в deploy/unit; - lock file обязателен для
autoиheal; - cooldown обязателен для restart/start actions;
- каждый risky action пишет audit entry;
- rollback должен быть одной командой systemd/service symlink switch.
6. Фазы миграции
Phase 0. Baseline и фиксация контрактов
Цель: перед переносом зафиксировать, что именно считается корректным поведением.
Действия:
- Снять текущие outputs legacy-команд:
detmir-status;detmir-check --json;detmir-dlp;detmir-autoна зеленом контуре;detmir-heal-safeв dry-safe сценарии или на mock host.
- Сохранить sanitized fixtures в
adk-rust/fixtures/. - Зафиксировать JSON schemas:
- status summary;
- DetMir check result;
- DLP health result;
- auto run state;
- heal action log.
- Зафиксировать exit code matrix старых команд.
- Зафиксировать runtime paths:
/var/lib/detmir-ai;/usr/local/bin;- relevant systemd units/timers.
Гейт готовности:
cd adk-rust
cargo fmt --all -- --check
cargo check --workspace
Результат фазы: миграция не начинается вслепую; есть baseline для сравнения.
Phase 1. Rust foundation
Цель: создать shared crates, чтобы не плодить разные реализации HTTP, времени, ошибок, JSON и systemd.
Модули:
detmir-core;detmir-state;detmir-aw-client;detmir-systemd.
Минимальная функциональность:
- RFC3339/UTC timestamp parsing;
- HTTP JSON client with timeout/retry;
- TCP check;
- atomic JSON write;
- retention cleanup;
- exit-code helper;
- common
StatusLevel:OK,WARN,FAIL; - bucket mode enum:
fresh,inactive_ok,event_driven; - systemd read-only checks;
- systemd action allowlist, но без включения mutation by default.
Гейт:
cd adk-rust
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
Phase 2. Read-only operator modules
Цель: заменить самые безопасные команды, не меняющие состояние.
Порядок:
detmir-status- объединить с совместимым
detmir-adk-status; - читать
/var/lib/detmir-ai/latest-state.json; - выдавать text, JSON, ADK JSON;
- заменить shell wrapper после parity.
- объединить с совместимым
detmir-check- перенести Python checks в Rust;
- сохранить семантику
FRESH,INACTIVE,EVENT-DRIVEN,STALE,DEAD; - сохранить
summary.bucket_ok/stale/dead/service_failures/service_warnings; - не менять thresholds без отдельного решения.
detmir-dlp- сначала переписать SSH wrapper;
- затем отдельно переносить
dlp-health-check.pyна AW server.
Shadow-mode для detmir-check:
detmir-check --json > /tmp/detmir-check.old.json
adk-rust/target/release/detmir-check --json > /tmp/detmir-check.new.json
jq -S '.summary' /tmp/detmir-check.old.json > /tmp/old.summary.json
jq -S '.summary' /tmp/detmir-check.new.json > /tmp/new.summary.json
diff -u /tmp/old.summary.json /tmp/new.summary.json
Гейт:
- old/new agree on
ok; - old/new agree on summary counters;
- new module returns same exit code class;
- no network side effects;
- server run succeeds from systemd-like environment.
Phase 3. State/report orchestration
Цель: заменить detmir-auto без немедленной замены heal.
Первый Rust detmir-auto должен:
- брать lock;
- создавать run directory;
- запускать
detmir-checkиdetmir-dlp; - писать
summary-before.json; - писать
state-*.json; - обновлять symlinks:
latest-run;latest-state.json;latest-report.md;
- выполнять retention cleanup;
- уметь
--no-heal; - по умолчанию на первом этапе не выполнять heal, а только писать
would_heal=true.
Pollinations/LLM report:
- не должен быть hard dependency для статуса;
- failure LLM report не должен ломать state update;
- raw JSON summary всегда должен сохраняться даже при ошибке report generation.
Гейт:
- Rust
detmir-auto --no-healдает такой жеlatest-state.jsonпо смыслу; - symlink updates atomic enough for readers;
- timer можно прогнать вручную без изменения heal behavior;
detmir-statusвидит новое state без изменений.
Текущее состояние:
detmir-auto-rustустановлен на Proxmox как shadow binary/usr/local/bin/detmir-auto-rust;detmir-auto-rustумеет вызывать Rust heal через--enable-healилиDETMIR_AUTO_HEAL=1, но shadow unit явно запускается с--no-heal;- production
detmir-auto.serviceпереключен на/usr/local/bin/detmir-auto-rustчерез drop-in/etc/systemd/system/detmir-auto.service.d/20-rust-switch.conf; - legacy
/usr/local/bin/detmir-autoсохранен для rollback; - отдельный
detmir-auto-rust-shadow.timerотключен после успешного production timer observation; unit-файлы и shadow state сохранены; - shadow пишет только в
/var/lib/detmir-ai/shadow/detmir-auto-rust; detmir-auto-rust-shadow.serviceиспользуетSuccessExitStatus=2, чтобы найденный shadow FAIL фиксировался в JSON, но не загрязнялsystemctl --failed;- перед стартом shadow service проверяет, что production
detmir-auto.serviceне активен; - последняя systemd shadow-проверка после retry в AW client:
severity=OK,check_rc=0,dlp_rc=0, buckets8/0/0, DLP22/0/0. - production Rust start проверен:
ExecStart=/usr/local/bin/detmir-auto-rust --command-timeout-seconds 180, latest-stateOK,systemctl --failedпустой. - scheduled production timer cycle проверен после switch: process status
0, latest-stateOK, report generated,systemctl --failedпустой.
Phase 4. Safe heal
Цель: перенести detmir-heal-safe, но только после read-only parity.
Обязательные guardrails:
--dry-rundefault на первых deploy;- allowlist units:
activitywatch-server.service;aw-worktime-api.service;aw-worktime-ui-bridge.timer;activitywatch-dlp-aggregator.timerтолько если unit существует;
- запрет wildcard restart;
systemctl reset-failedтолько для allowlist;- cooldown между restart попытками;
- audit log в run directory;
- hard timeout на SSH/systemctl;
- never touch Windows/RDP recovery from this module.
Гейт:
- dry-run показывает exact actions;
- на зеленом контуре не рестартит ничего;
- на mock/failing unit рестартит только allowlisted unit;
- после heal всегда запускается повторный check;
- exit code не маскирует FAIL.
Текущее состояние:
- создан crate
detmir-heal-safe; - binary развернут на Proxmox как
/usr/local/bin/detmir-heal-safe-rust; - production
/usr/local/bin/detmir-heal-safeпока не заменен; - production Rust auto использует
/usr/local/bin/detmir-heal-safe-rustчерезDETMIR_HEAL_BIN; - default mode: dry-run, mutation только через
--apply; - optional timer start выключен по умолчанию, включается только
--start-optional; - green dry-run проверен: restart/start не планируются;
- green apply проверен: выполнены только
reset-failedи DLP health snapshot, контур осталсяOK,systemctl --failedпустой. - red/mock heal test проверен на Proxmox в isolated state-dir:
summary-before=FAIL, heal вызван, check/dlp повторены, final stateOK, process rc0.
Phase 5. Watchdog и lightweight services
Цель: заменить простые shell watchdogs и wrappers.
Порядок:
tsj-guardian-watchdog.sh->tsj-guardian-watchdog;- lightweight service wrappers;
- bounded cleanup/retention jobs.
Особое внимание:
- bot heartbeat file parsing;
- duplicate
gostinstance dedupe; - service restart only on stale/missing heartbeat;
- no killing unrelated processes.
Гейт:
- dry-run mode показывает, какой PID был бы убит;
- process matching покрыт тестами;
- systemd timer/service rollback сохранен.
Текущее состояние:
- создан crate
tsj-guardian-watchdog; - binary развернут на Proxmox как
/usr/local/bin/tsj-guardian-watchdog-rust; - production
tsj-guardian-watchdog.serviceпереключен на Rust через drop-in/etc/systemd/system/tsj-guardian-watchdog.service.d/20-rust-switch.conf; - legacy
/opt/infra-admin/tsj-bot/tsj_guardian_watchdog.shсохранен; - dry-run выявил bug legacy-подхода:
pgrep -fне матчился как literal из-за+вgostpattern; - Rust версия использует literal process scan через
ps; - one-shot apply удалил лишний duplicate
gostPID и оставил systemd MainPID; - service mode работает как:
/usr/local/bin/tsj-guardian-watchdog-rust --apply --loop-forever --interval-seconds 60
Rollback:
sudo rm -f /etc/systemd/system/tsj-guardian-watchdog.service.d/20-rust-switch.conf
sudo systemctl daemon-reload
sudo systemctl restart tsj-guardian-watchdog.service
Phase 6. AW server health and DLP modules
Цель: перенести серверные Python health checks/exporters.
Порядок:
aw-server/aw-rus-healthd.py- выполнено, production черезaw-rus-healthd.service.d/20-rust-switch.conf;scripts/dlp-health-check.py- выполнено,/usr/local/bin/dlp-health-checkзаменён Rust-бинарником;scripts/aggregate_dlp_events.py- выполнено, production черезactivitywatch-dlp-aggregator.service.d/20-rust-switch.conf;- DLP integrations:
- syslog forwarder - выполнено, production через
aw-dlp-syslog-forwarder.service.d/20-rust-switch.conf; - webhook sender - выполнено, production через
aw-dlp-webhook-sender.service.d/20-rust-switch.conf; - CEF exporter - выполнено, production через
aw-dlp-cef-exporter.service.d/20-rust-switch.conf;
- syslog forwarder - выполнено, production через
aw-server/aw-dlp-influx-exporter.py- выполнено, production черезaw-dlp-influx-exporter.service.d/20-rust-switch.conf;aw-server/aw-worktime-influx-exporter.py- выполнено, production черезaw-worktime-influx-exporter.service.d/20-rust-switch.conf;aw-server/aw-worktime-prewarm.sh- выполнено, production черезaw-worktime-prewarm.service.d/20-rust-switch.conf;aw-server/aw-worktime-ui-bridge.py- выполнено, production черезaw-worktime-ui-bridge.service.d/20-rust-switch.conf;aw-server/aw-worktime-autoheal.sh- выполнено, production черезaw-worktime-autoheal.service.d/20-rust-switch.conf;- remaining worktime modules.
Правила:
- exporters должны сохранять Prometheus/Influx output format;
- API endpoints не менять без compatibility layer;
- для каждого exporter сначала golden output fixture;
- service unit меняется только после side-by-side run.
Гейт:
- old/new metrics names match;
- dashboard datasource health не ломается;
- unit tests старого поведения перенесены или сохранены;
systemctl --failedclean после deploy.
Phase 7. Telegram bot backend helpers
Цель: не переносить Telegram bot runtime на Rust. Python остается постоянным production runtime для Telegram polling/sending, proxy, retries и side effects. На Rust выносятся только backend helpers, status aggregation, decision/gating и безопасные read-only/action contracts, которые бот вызывает как внешние команды.
Порядок:
[done]Бот продолжает жить на Python.[started]Команды/status,/detmir, health summaries начинают читать Rust JSON. Первым вынесенtsj-guardian-status: read-only helper для строкиdetmir_autoиз/var/lib/detmir-ai/latest-state.json; вторым шагом тот же helper начал рендеритьaw_rus_sloизAW_RUS_SLO_SUMMARY_CMD.- Recovery/actions вызывают Rust binaries с dry-run/audit.
[decision]Bot runtime на Rust не переносится; новые улучшения Telegram делаются в Python runtime или через Rust backend helpers.
Почему так:
- Telegram runtime, proxy, retries и async edge cases уже стабильно покрыты Python-кодом и тестами;
- перенос runtime даст мало пользы и высокий риск регрессий;
- максимальная польза от Rust сначала в backend-командах и safety contracts.
Гейт:
/statusпоказывает те же строки или лучше;- smoke message проходит;
- watchdog активен;
- restart bot не теряет config/secrets;
- секреты не попадают в logs/tests.
Phase 8. Deploy/install scripts
Цель: переносить последними, только после стабилизации runtime binaries.
Что может остаться не на Rust:
- Ansible playbooks;
- Windows PowerShell collector deployment;
- Playwright browser smoke;
- packaging scripts, если они надежны и редко исполняются.
Что имеет смысл перенести:
- validation CLI;
- install-kit consistency checker;
- local report generators;
- deterministic packaging helpers.
Гейт:
- install-kit output byte/content expected;
- rollback installer path documented;
- production deploy не зависит от laptop-only paths.
7. Deployment workflow
Сборка:
cd /mnt/usb_hdd2/Projects/ActivityWatch-Russian/adk-rust
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
cargo build --release --workspace
Установка одного бинарника на Proxmox:
install -o root -g root -m 0755 \
target/release/<binary> \
/usr/local/bin/<binary>
Через Ansible:
ansible proxmox -i ansible/inventory.ini -m copy -a \
'src=/mnt/usb_hdd2/Projects/ActivityWatch-Russian/adk-rust/target/release/<binary> dest=/usr/local/bin/<binary> owner=root group=root mode=0755 backup=yes'
Проверка на сервере:
<binary> --version || true
<binary> --help
<binary> <safe-read-only-command> --json
systemctl --failed
8. Rollback workflow
Для каждого заменяемого компонента должен быть rollback:
- Старый script остается на сервере как
<name>.legacyили в package backup. - systemd unit меняется через drop-in или controlled template.
- Перед switch:
- сохранить
systemctl cat <unit>; - сохранить checksum старого binary/script;
- сохранить latest known good command.
- сохранить
- Rollback command документируется в PR/run note.
Текущий rollback для detmir-auto после Rust switch:
sudo rm -f /etc/systemd/system/detmir-auto.service.d/20-rust-switch.conf
sudo systemctl daemon-reload
sudo systemctl restart detmir-auto.service
Перед switch сохранены:
/var/lib/detmir-ai/switch-backups/detmir-auto.service.before-rust-20260531-192809.txt
/var/lib/detmir-ai/switch-backups/detmir-auto.sha256.before-rust-20260531-192809.txt
Пример:
cp -a /usr/local/bin/detmir-check /usr/local/bin/detmir-check.legacy
install -o root -g root -m 0755 target/release/detmir-check /usr/local/bin/detmir-check
detmir-check --json
Если новый binary провален:
mv /usr/local/bin/detmir-check.legacy /usr/local/bin/detmir-check
systemctl restart detmir-auto.timer || true
9. Acceptance checklist
Перед заменой legacy-команды:
- old/new CLI documented;
- old/new JSON compared on real fixture;
- exit codes match by class;
- unit tests pass;
- integration smoke pass on target host;
- no secrets in logs;
- systemd environment tested;
- rollback command known;
- docs updated;
- operator command example added.
Перед включением mutation/recovery:
--dry-runverified;- allowlist verified;
- lock verified;
- cooldown verified;
- audit log verified;
- green contour causes no action;
- failing contour causes only intended action;
- post-action check verified;
- rollback tested.
10. Quality gates
Локально:
cd adk-rust
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
Production readiness checklist:
-
public defaults are still sanitized: no live hosts, IPs, domains, tokens, evidence paths, case IDs, hashes, or operator home paths in tracked files;
-
live values are supplied only through private inventory/env or server-side runtime env;
-
enabled Influx exporters have live URL, org, bucket, token and hosts;
-
private production config passes:
scripts/check_production_inventory_placeholders.sh private-config/runtime.env private-config/ansible-vars.ymlRun this guard only on private production override files. Public tracked default/example files may intentionally contain
HOST-EXAMPLEand TEST-NET values for release hygiene. -
ansible-playbook -i ansible/inventory.ini ansible/deploy_aw_server.yml --syntax-checkpasses; -
aw-worktime-influx-exporter.serviceandaw-dlp-influx-exporter.servicecomplete once and write points; -
detmir-readiness --jsonreturnsstatus=OK; -
readiness bundle is generated with
--output-dir /var/lib/activitywatch/health/readiness-bundle; -
readiness bundle integrity passes:
cd /var/lib/activitywatch/health/readiness-bundle sha256sum -c sha256sums.txt openssl dgst -sha256 -verify public-key.pem \ -signature sha256sums.txt.sig sha256sums.txt -
readiness signing public-key fingerprint for the shipped/customer contour is fixed and documented:
READINESS_PUBLIC_KEY_SHA256=<READINESS_PUBLIC_KEY_SHA256_FINGERPRINT>Verification command:
sha256sum /var/lib/activitywatch/health/readiness-bundle/public-key.pem jq -r '.signature.public_key_fingerprint_sha256' \ /var/lib/activitywatch/health/readiness-bundle/detmir-readiness-status.json -
detmir-readiness-status.jsonanddetmir-readiness.promexpose the latest OK/WARN/FAIL state independently from systemd unit result; -
detmir-readiness.timeris enabled for daily bundle generation; -
DetMir portal readiness endpoints return latest/bundle/verify data;
-
detmir-check --json,detmir-status --jsonand Grafana check are green; -
rollback path for changed binaries/env files is known.
Full readiness procedure is documented in docs/PRODUCTION_READINESS_RU.md.
На Proxmox:
detmir-readiness --json
detmir-status --json
detmir-check --json
detmir-dlp
detmir-auto --no-heal
systemctl --failed
На AW server:
aw-rus-healthd --json
dlp-health-check --json
/usr/local/bin/aw-slo-monitor-rust --state-dir /var/lib/activitywatch/slo-rust-shadow --json
systemctl --failed
Для Telegram:
systemctl is-active tsj-guardian-bot tsj-guardian-watchdog gost-tg
11. Приоритет ближайших работ
Немедленный порядок:
[done]Создатьdetmir-core,detmir-state,detmir-aw-client.[done]Объединитьdetmir-statusиdetmir-adk-statusв один Rust binary.[done]Перенестиdetmir-check.[done]Сделать shadow comparedetmir-checkold/new.[done]Перенестиdetmir-dlpwrapper.[done]Начатьdetmir-auto --no-heal.[done]Накопить scheduled shadow/prod parity дляdetmir-auto-rust, затем выполнить controlled switch.[done]Перенестиdetmir-heal-safeс dry-run/apply guardrails и подключить к Rust auto.[done]Наблюдать timer цикл Rust production и отключить redundant shadow timer.[done]Перенестиtsj-guardian-watchdog.shна Rust service loop.[done]Перенести AW-RUS SLO monitor/summary path на Rust:aw-slo-monitor-rustпишетaw-slo-samples.jsonl,aw-slo-summary.jsonиaw-slo-summary.txt; production включен через/etc/systemd/system/aw-slo-monitor.service.d/20-rust-switch.conf.[done]Выполнить controlled correction старых ложных SLO bad-сэмплов: backup создан, 133 записи с единственной причинойworktime_today_html body too small ... < 5000исправлены, 59 не-ложных/неоднозначных bad-сэмплов оставлены в истории.[done]Вынести Telegram/statusaggregation layer в Rust:tsj-guardian-status --status-textсобирает полный текст статуса, Python runtime Telegram оставлен и использует старую сборку только как fallback.[done]Вынести incident/escalation decision path в Rust:tsj-guardian-statusпринимает JSON через stdin и решает suggestions, transient quorum/defer и timeout escalation/fallback. Python runtime выполняет только side effects и сохраняет fallback на старую логику.[done]Вынести operator action routing/gating в Rust:tsj-guardian-status --operator-action-decisionнормализует action aliases, назначает handler/risk, блокирует неизвестные action и защищает update install/rollback confirm без pending-запроса. Python runtime продолжает выполнять команды и сохраняет fallback.[done]Вынести DLP policy/mode decision path в Rust:tsj-guardian-status --dlp-policy-decisionопределяет monitor/enforce/mixed, считает block/total по endpoint groups, строит toggle/target policy plan и changed_rules. Python runtime продолжает выполнять API PUT и Windows policy sync, fallback на старую Python-логику сохранен.[done]Вынести confirmation state machines в Rust:tsj-guardian-status --confirmation-decisionрешает TTL/expire, cancel, first_confirm и apply code/stage validation для pfSense, OpenVPN, Proxmox selection и Proxmox restore. Python runtime продолжает создавать pending-запросы и выполнять side effects послеallowed=true, fallback на старую Python-валидацию сохранен.[done]Вынести autoheal/action plan extraction в Rust:tsj-guardian-status --autoheal-plan-decisionклассифицирует AW-Rus failures в Windows collector heal, server-side DLP heal, worktime rebuild, SLO no-direct-target и задержку перед повторной проверкой. Python runtime продолжает выполнять recovery-команды и повторный probe, fallback на старую Python-маршрутизацию сохранен. Production deploy проверен на Proxmox: sample decisions OK,tsj-guardian-bot,tsj-guardian-watchdogиgost-tgactive,detmir-statusOK /ok_for_operator=true, свежий journal после правильной выкладки helper без повторных unexpected-argument warnings.[done]Укрепить deploy artifacts после миграции:deploy_aw_server.ymlпереведен на единыйaw_rust_release_dirдля Rust binarystat/copy, чтобыCARGO_TARGET_DIRработал одинаково для Proxmox/Telegram и AW-server deploy. Добавленscripts/check_detmir_rust_release_artifacts.sh; провереноCARGO_TARGET_DIR=<OPERATOR_HOME>/.cache/detmir-adk-rust-target cargo build --release --workspace, artifact check OK,deploy_aw_server.yml --syntax-checkOK.[done]Прогнать production AW-server deploy contract и закрыть найденные эксплуатационные дефекты:- Influx tokens для deploy берутся из окружения; при восстановлении с сервера значения не печатать.
- Legacy DB merge выключен по умолчанию через
aw_legacy_db_merge_enabled: false, потому что backup SQLite на 12G root FS уперся вNo space left on device. Повторять merge только после отдельного disk plan. - Browser smoke по умолчанию принудительно использует
chromium-cli;node-playwrightна текущем Node ломаетсяrimraf: callback function required. aw-rus-healthd-rustтеперь проверяет AW interactive buckets поmetadata.end, а event buckets выбирают свежайшее событие из окнаlimit=20с учетомtimestamp + duration; это устраняет ложные stale/warn на длинных ActivityWatch событиях и guard heartbeat ordering.- После reset runtime budget
AWatchRusCollectorGuardи однократного перезапуска AW watcher tasks production verification зеленый:aw-rus-healthd-rustok=14 warn=0 fail=0,detmir-statusOK,systemctl --failedна AW и Proxmox пусто,deploy_aw_server.yml --syntax-checkOK.
[done]Выполнить безопасную уборку AW app data и защитить timers от лавинного восстановления:- удалены только старые browser-smoke run-директории, manual profile,
неиспользуемый
/opt/activitywatch/releases/v0.13.2и неиспользуемый/opt/activitywatch/aw-rus-ops/venv; - текущий
aw-server-rust-v0.13.2, SQLite DB/WAL/SHM и rollback-critical backups не удалялись; - root FS улучшен примерно с
96%до85%; - AW timers переведены на staggered
OnCalendar, чтобы после ручного restart/daemon-reload не запускать все missed persistent jobs одной пачкой; - legacy
aw_to_influx_exporter.serviceудален из systemd, потому что production уже использует Rust DLP/worktime Influx exporters; aw-rus-healthd-rustполучил timeout для wrapper-команд, чтобыaw-health-check/dlp-health-checkне могли подвесить healthd.
- удалены только старые browser-smoke run-директории, manual profile,
неиспользуемый
[done]Перенести DLP content analyzer text path в Rust:- добавлен
dlp-content-analyzercrate; - CLI совместим с
aw-dlp-content-analyzer --text --dictionary-pack --regex-pack; - реализованы dictionary/regex pack matching и checksum validators для ИНН, СНИЛС, паспорта РФ;
- позиции совпадений считаются в Python-compatible character offsets, не byte offsets;
- image/OCR path оставлен через legacy Python fallback, чтобы не тащить OCR/Tesseract в Rust на этом шаге;
/usr/local/bin/aw-dlp-content-analyzerтеперь wrapper, который предпочитает/usr/local/bin/aw-dlp-content-analyzer-rust, но сохраняет Python fallback;- parity на AW server проверен на
152-fz-pdn,contacts,secrets,financial: match counts, values и offsets совпали; - artifact check расширен
dlp-content-analyzer.
- добавлен
[done]Перенести DLP admin CLI в Rust:- добавлен
dlp-admin-clicrate; - сохранены команды
policies list/active,incidents list,cases list/create,health check; - HTTP client использует no-proxy для локальных AW/DLP сервисов;
/usr/local/bin/dlp-admin-cliустановлен как Rust production CLI, legacy/usr/local/bin/dlp-admin-cli.pyбольше не является repo/runtime path; рабочий CLI:/usr/local/bin/dlp-admin-cli;- parity на AW server совпал с Python для
health,policies active,incidents list --since-hours 24 --limit 5,cases list --limit 5; - production verification зеленый: AW failed units 0,
aw-rus-healthd-rust14/0/0,dlp-health-check22/0/0,detmir-statusOK,detmir-checkOK, Proxmox failed units 0; - artifact check расширен
dlp-admin-cli.
- добавлен
[done]Перенести DLP policy engine service в Rust:- добавлен
dlp-policy-enginecrate; - сохранены существующая SQLite schema/DB и API: policies list/create/get/update/delete, active bundle/version, submit/approve/draft/activate, rollback, agent heartbeat/desired, audit;
- checksum policy совместим с Python:
json.dumps(..., ensure_ascii=False, sort_keys=True, separators=(",", ":"))+ SHA-256; - env contract сохранен:
AW_DLP_POLICY_ENGINE_BIND_HOST,AW_DLP_POLICY_ENGINE_PORT,AW_DLP_POLICY_ENGINE_DB_PATH; - shadow parity на AW server совпал с Python для list/active/version и agent heartbeat/desired;
- Rust исправляет documented
/api/0/dlp/policies/audit?limit=N: legacy FastAPI возвращал 422 из-за route-order конфликта с{policy_id}; - production
/usr/local/bin/aw-dlp-policy-engine-rustподключен через/etc/systemd/system/aw-dlp-policy-engine.service.d/20-rust-switch.conf; - rollback: удалить этот drop-in,
systemctl daemon-reload, restartaw-dlp-policy-engine.service; - production verification зеленый: active policy
default-policyv6, audit endpoint OK, AW failed units 0,aw-rus-healthd-rust14/0/0,dlp-health-check22/0/0,detmir-statusOK,detmir-checkOK, Proxmox failed units 0; - artifact check расширен
dlp-policy-engine.
- добавлен
[done]Перенести DLP case management service в Rust:- добавлен
dlp-case-managementcrate; - сохранены существующая SQLite schema/DB и API:
/health, cases list/create/get/update, comments list/create, Hayabusa forensics link; - сохранены self-test rejection и evidence SHA-256 contract:
json.dumps(..., ensure_ascii=False, sort_keys=True, separators=(",", ":"))+ SHA-256; - env contract сохранен:
AW_DLP_CASE_BIND_HOST,AW_DLP_CASE_PORT,AW_DLP_CASE_DB_PATH; - local HTTP smoke на temp DB проверил create/list/patch/comment/hayabusa link/self-test reject;
- shadow parity на AW server совпал с Python для health, case list/filter, case detail и comments на копии production DB;
- production
/usr/local/bin/aw-dlp-case-management-rustподключен через/etc/systemd/system/aw-dlp-case-management.service.d/20-rust-switch.conf; - rollback: удалить этот drop-in,
systemctl daemon-reload, restartaw-dlp-case-management.service; - production verification зеленый: case list OK/latest id 35,
AW failed units 0,
aw-rus-healthd-rust14/0/0,dlp-health-check22/0/0,detmir-statusOK,detmir-checkOK, Proxmox failed units 0; - artifact check расширен
dlp-case-management.
- добавлен
[done]Перенести DLP compliance report scheduler/generator в Rust:- добавлен
dlp-compliancecrate; - сохранены CLI
--month,--profile,--profiles,--stdout-json; - сохранены env fallback для AW API:
AW_DLP_AW_API_BASE,AW_SERVER_URL, defaulthttp://127.0.0.1:5600; - сохранены output/template envs и artifact names:
<profile>-<YYYY-MM>.html/json; - shadow parity на AW server в temp output dirs совпал с Python для
152-fz,pci-dss: profile/period/aw_api_base/stats и наличие artifacts; - production
/usr/local/bin/aw-dlp-compliance-rustподключен через/etc/systemd/system/aw-dlp-report-scheduler.service.d/20-rust-switch.conf; - rollback: удалить этот drop-in и
systemctl daemon-reload; - production oneshot run успешен:
152-fz-2026-06иpci-dss-2026-06html/json созданы, timer active, AW failed units 0,aw-rus-healthd-rust14/0/0,dlp-health-check22/0/0,detmir-statusOK,detmir-checkOK, Proxmox failed units 0; - artifact check расширен
dlp-compliance.
- добавлен
[done]Перенести Hayabusa case alert/link helpers в Rust:- добавлен
hayabusa-toolscrate с бинарникамиaw-hayabusa-case-alert-rustиaw-hayabusa-link-case-rust; - сохранены CLI/env контракты legacy helpers:
--case-id,--intake-json,--case-api-base,--mode,--link-source,AW_HAYABUSA_*; case-alertсохраняет scoring/severity,top_rules, comment text, case create/update/link path и Telegram alert text;- Telegram request errors в Rust не печатают bot token/URL;
/usr/local/bin/aw-hayabusa-case-alertи/usr/local/bin/aw-hayabusa-link-caseтеперь wrapper-команды, которые предпочитают Rust binaries и fallback на Python из/opt/activitywatch/aw-rus-ops/hayabusa;- shadow compare на реальном
/opt/hayabusa/state/latest-intake.jsonсAW_HAYABUSA_AUTO_CASE_ENABLED=falseиAW_HAYABUSA_TELEGRAM_ENABLED=falseдалsummary_equal=true; - production verification зеленый: AW failed units 0,
aw-rus-healthd-rust14/0/0,dlp-health-check22/0/0,detmir-statusOK, Proxmox failed units 0; - artifact check расширен
aw-hayabusa-case-alert-rustиaw-hayabusa-link-case-rust.
- добавлен
[done]Перенести remaining Hayabusa offline helpers в Rust:hayabusa-toolsрасширен бинарникамиaw-hayabusa-from-windows-rustиaw-hayabusa-autoprocess-rust;from-windowsсохраняет server-side workflow: Ansible WinRM export, latest zip discovery, fetch в drop-dir,aw-hayabusa accept,process-inbox, optional link-case и печатьLATEST_INTAKE;autoprocessсохраняет drop-dir scanner, lock file,.caseid/.meta.jsonsidecars, host/mode/link_source inference, case-alert capture, sidecar/archive move в report dir;/usr/local/bin/aw-hayabusa-from-windowsи/usr/local/bin/aw-hayabusa-autoprocessтеперь wrapper-команды, которые предпочитают Rust и fallback на Python из/opt/activitywatch/aw-rus-ops/hayabusa;aw-hayabusa-drop.serviceтеперь запускает/usr/local/bin/aw-hayabusa-autoprocessнапрямую, безpython3, чтобы wrapper мог выбрать Rust;- no-mutation smoke на AW server:
--helpдля всех Hayabusa helpers, empty drop-dir дляautoprocess,systemctl catExecStart OK; - production verification зеленый: AW failed units 0,
aw-rus-healthd-rust14/0/0,dlp-health-check22/0/0,detmir-statusOK, Proxmox failed units 0; - artifact check расширен
aw-hayabusa-from-windows-rustиaw-hayabusa-autoprocess-rust.
[done]Перенести AW ops helperaw-health-checkв Rust:- добавлен crate
aw-health-check; - сохранены проверки legacy shell: systemd services/timer,
ActivityWatch
/api/0/info, Worktime/health, DLP transport freshness черезdlp-health-check --json, и drift checks дляstartOfDay,always_active_pattern,landingpage; - env contract читается из процесса и
/etc/activitywatch/aw-server.env; - production
/usr/local/bin/aw-health-checkтеперь Rust-required wrapper: вызывает/usr/local/bin/aw-health-check-rustи падает с ошибкой, если Rust binary отсутствует; - shadow parity на AW server совпал с shell по exit code и ключевым health-строкам;
- production verification зеленый:
aw-health-checkOK,aw-rus-healthd-rust14/0/0,dlp-health-check22/0/0,detmir-statusOK,ok_for_operator=true, failed units 0; - artifact check расширен
aw-health-check.
- добавлен crate
[done]Перенести AW ops helpercheck-aw-dataв Rust:- добавлен crate
check-aw-data; - корневой
check-aw-data.shстал Rust-first wrapper с fallback наscripts/legacy/check-aw-data.sh; - на AW server production
/usr/local/bin/check-aw-dataтеперь wrapper, который предпочитает/usr/local/bin/check-aw-data-rust, по умолчанию используетhttp://127.0.0.1:5600и fallback на/opt/activitywatch/aw-rus-ops/check-aw-data.sh; - сохранены операторские статусы
FRESH,STALE,DEAD,EMPTY,EVENT-DRIVEN,INACTIVEи CORS probe; - быстрый режим по умолчанию использует
metadata.endиз/api/0/buckets; глубокое чтение event ids включается флагом--with-event-ids; - production verification:
/usr/local/bin/check-aw-data --no-colorзавершился за 2s, buckets fresh/event-driven/inactive as expected,CORS: OK (HTTP 200); - final gates зеленые: AW failed units 0,
aw-rus-healthd-rust14/0/0,dlp-health-check22/0/0,detmir-auto --no-healOK,detmir-statusOK иok_for_operator=true; - artifact check расширен
check-aw-data.
- добавлен crate
[done]Перенести AW maintenance helperaw-prune-local-stateв Rust:- добавлен crate
aw-prune-local-state; /usr/local/bin/aw-prune-local-state.shтеперь Rust-first wrapper, который без аргументов запускает Rust binary в legacy-compatible--applyрежиме и fallback на shell из/opt/activitywatch/aw-rus-ops/aw-prune-local-state.sh;- Rust binary по умолчанию работает как dry-run, поддерживает
--json,--apply, retention/keep параметры и строгий allowlist путей; - safety policy: не удалять SQLite DB вне
backups/db, не удалятьswitch-backups,before-rust,rollbackи корневые state каталоги; - cleanup расширен на старые
browser-smokerun-директории как project app data; rollback-critical backups и AW SQLite DB не трогались; - production recovery во время проверки: rootfs AW server был 100%
заполнен,
aw-server-rustпопал вpoisoned lock; через Rust helper удалены старые browser-smoke runs на 347 MB суммарно, затемactivitywatch-server.serviceвосстановлен без удаления DB; - final gates зеленые: AW failed units 0,
aw-rus-healthd-rust14/0/0,dlp-health-check22/0/0,check-aw-dataOK,detmir-auto --no-healOK,detmir-statusOK иok_for_operator=true; - Telegram bot/watchdog временно остановлены по операторской команде и не перезапускались в рамках этого шага;
- artifact check расширен
aw-prune-local-state.
- добавлен crate
[done]Устранить нехватку места на AW server через Proxmox resize:- CT
203(aw-server,<AW_SERVER_HOST>) rootfs расширен через Proxmoxpct resize 203 rootfs +20G; - перед resize сохранен config backup:
/var/lib/detmir-ai/switch-backups/ct203-aw-server.before-rootfs-resize-20260602T054826Z.conf; - rootfs изменился с
12Gна32G, свободное место стало около20G; - Proxmox storage
local-btrfsпосле resize имеет около106Gдоступного места; - AW
/api/0/infoи/api/0/bucketsпроверены с AW server и Proxmox; - final gates зеленые: AW health 14/0/0, DLP health 22/0/0,
detmir-checkservice_warnings=0,detmir-auto --no-healOK,detmir-statusOK иok_for_operator=true, failed units 0; - Telegram bot/watchdog оставлены
inactiveпо операторской команде.
- CT
[done]Остановить неконтролируемый ростaw-session-events:- read-only SQLite audit показал, что
/var/lib/activitywatch/aw-server-rust/sqlite.dbзанимает около6.8G,freelist_count=0; VACUUM сам по себе не освободит место, потому что размер занят live events; - основной источник роста:
aw-session-events_HOST-EXAMPLE- около6.9Mстрок и~5GBpayload, с пиками1.2M-2.4Mprocess-level событий в сутки за 2026-05-29..2026-06-01; - production live config на RDP был
pollSeconds=5иsessionEvents.processEventsEnabled=true; - отключена постоянная process-level публикация:
aw_windows_process_events_enabled=falseв production/example/default Ansible vars иsessionEvents.processEventsEnabled=falseв liveC:\ProgramData\AWatch-rus\deployment-config.json; - live config backup сохранен на RDP:
C:\ProgramData\AWatch-rus\switch-backups\deployment-config.before-disable-process-events-20260602T061836Z.json; - старый
worktime-session-collector.ps1PID8476остановлен, collector поднят заново штатнымиActivityWatch Launch [...]tasks/guard; - delta-gate:
metadata.endbucketaw-session-events_HOST-EXAMPLEостался2026-06-02T06:27:11.197Zчерез 75 секунд, постоянный поток остановлен; - базовый сбор не сломан:
detmir-check --jsonOK,detmir-statusseverity OK, AW failed units 0, Proxmox failed units 0, AW API и Worktime API отвечают; - SQLite row deletion/compact не выполнялись на этом этапе: сначала стабилизирован источник роста, retention/trim старых process events - отдельный destructive этап с backup.
- read-only SQLite audit показал, что
[done]Controlled trim старых process-levelaw-session-events:- перед mutation сохранен полный rollback backup SQLite DB/WAL/SHM:
/var/lib/activitywatch/backups/db/aw-sqlite-before-session-events-trim-20260602T064127Z; - на время операции остановлены AW-related timers/services и
activitywatch-server.service, чтобы не было writer'ов к SQLite; - scoped delete удалил только события bucket
aw-session-events_HOST-EXAMPLEсeventType=process_startилиeventType=process_stop; - удалено
6,906,190шумных process-level событий; - сохранены logon events: после trim в
aw-session-events_HOST-EXAMPLEосталось174события, recent samples имеютeventType=logon; PRAGMA integrity_checkдо и послеVACUUM:ok;- DB уменьшилась с
6.8Gдо350M, rootfs AW server вернулся к38%использования и около19Gfree; - после restart AW был
poisoned lockиз-за timer запросов во время старта; выполнен controlled restart при остановленных timers, затем/api/0/infoи/events?limit=1вернулись OK; - timers включены обратно, failed units на AW и Proxmox:
0; - final gates зеленые:
aw-health-checkOK,dlp-health-check22/0/0,detmir-checkbucket_ok=8 stale=0 dead=0,detmir-auto --no-healOK,detmir-statusseverity OK иok_for_operator=true; - Telegram bot/watchdog оставлены
inactiveпо операторской команде.
- перед mutation сохранен полный rollback backup SQLite DB/WAL/SHM:
[done]Добавить Rust guard от повторного роста AW SQLite:- добавлен crate
aw-db-health; - helper read-only открывает
/var/lib/activitywatch/aw-server-rust/sqlite.dbчерез SQLite read-only flags и не меняет DB; - проверяет:
- размер
sqlite.db(warn=2GiB,fail=5GiBпо умолчанию); - размер
sqlite.db-wal(warn=256MiB,fail=1GiB); - число строк
aw-session-events_<host>(warn=10000,fail=100000); - recent process-level events за окно 600 секунд
(
warn=1,fail=100); - latest
eventType/sourceдля быстрой диагностики; - опционально
--windows-configиsessionEvents.processEventsEnabled;
- размер
- поддерживает text output и
--json; aw-health-check-rustтеперь вызывает/usr/local/bin/aw-db-health --jsonи включает DB growth guard в общий AW health path;scripts/check_detmir_rust_release_artifacts.shтребуетaw-db-health;ansible/deploy_aw_server.ymlустанавливает/usr/local/bin/aw-db-healthи обновленный/usr/local/bin/aw-health-check-rust; установка health helpers продублирована до Influx token assert, чтобы DB guard обновлялся даже если playbook позже останавливается на пустом внешнем Influx token;- production
/usr/local/bin/aw-db-health --json:ok=true,db=352.3MiB,wal=4.0MiB, session rows174, recent process events0, latesteventType=logon; - production
/usr/local/bin/aw-health-check:AW DB growth guard passed; - local gates:
cargo fmt --all -- --check,cargo test -p aw-db-health3 passed,cargo test -p aw-health-check2 passed, release build OK, artifact check OK; - final gates зеленые:
dlp-health-check22/0/0,detmir-checkOK,detmir-auto --no-healOK,detmir-statusseverity OK, failed units на AW/Proxmox0; - Telegram bot/watchdog оставлены
inactive. 35.1.[done]Добавить автономную guarded чистку AW SQLite: - добавлен crate
aw-db-maintenance; - helper dry-run по умолчанию, mutation только с
--apply; - allowlist удаления: только bucket
aw-session-events_<host>и только событияeventType=process_startилиeventType=process_stop; - logon/session events не удаляются;
- retention по умолчанию
7дней; - перед удалением создается SQLite backup в
/var/lib/activitywatch/backups/db/aw-sqlite-before-db-maintenance-*.db; если строк к удалению нет, backup не создается и DB не пишется; - удаление chunked, без
VACUUM, чтобы не создавать длительный downtime; - добавлены
aw-server/aw-db-maintenance.serviceиaw-server/aw-db-maintenance.timer; - timer включен на AW server:
OnCalendar=Sun *-*-* 03:30:00,RandomizedDelaySec=15m,Persistent=true; - production dry-run:
planned_delete_rows=0,deleted_rows=0; - ручной service smoke:
Result=success,ExecMainStatus=0,planned_delete_rows=0,deleted_rows=0,backup_created=false; - production timer active; ближайший запуск по remote
systemctl list-timers:Sun 2026-06-07 03:40:05 UTC; - gates:
cargo fmt --all -- --check,cargo test -p aw-db-maintenance3 passed,cargo clippy -p aw-db-maintenance --all-targets -- -D warnings, release build OK, artifact check OK,quality-gate.shOK,ansible-playbook deploy_aw_server.yml --syntax-checkOK; - final production gates: AW failed units
0,aw-db-healthOK (sqlite.db=359.6MiB, WAL4.0MiB, session rows174, recent process events0, latesteventType=logon), DetMir status OK withdlp_counts={ok:22,warn:0,fail:0},ok_for_operator=true.
- добавлен crate
[done]Устранить blocker полного AW server deploy на Influx token:- проблема:
deploy_aw_server.ymlпадал на assertaw_worktime_influx_enabled=true, потому что локальные envAW_WORKTIME_INFLUX_TOKENиAW_DLP_INFLUX_TOKENпустые; - runtime
/etc/activitywatch/aw-server.envна AW server уже содержал оба token, поэтому правильный путь - сохранить remote secrets, а не отключать exporters; - playbook теперь через
slurpсno_log: trueчитает текущий/etc/activitywatch/aw-server.env, выбирает effective token: local env token если задан, иначе existing remote token; - asserts проверяют effective tokens и не печатают секреты;
- запись
/etc/activitywatch/aw-server.envиспользует effective tokens, чтобы full deploy не затирал рабочие Influx credentials пустыми значениями; - полный
ansible-playbook -i inventory.ini deploy_aw_server.yml -e aw_rust_release_dir=<OPERATOR_HOME>/.cache/detmir-adk-rust-target/releaseпрошел до конца:failed=0,ok=282; - final gates после deploy зеленые:
aw-db-healthOK,aw-health-checkOK,dlp-health-check22/0/0,detmir-checkOK,detmir-auto --no-healOK,detmir-statusseverity OK, failed units на AW/Proxmox0; - Telegram bot/watchdog оставлены
inactive.
- проблема:
[done]Закрепить safe default для WindowsprocessEventsEnabled:- исправлены Windows deploy/recovery defaults, чтобы новый deploy,
ensemble/domain deploy, hardening recovery и общий
New-ActivityWatchDeploymentConfigне включали process-levelaw-session-eventsбез явного параметра; - изменены
windows/deploy-single-user.ps1,windows/deploy-ensemble.ps1,windows/deploy-domain-users.ps1,windows/hardening-recovery.ps1,windows/ActivityWatch.Windows.Common.psm1иwindows/validate-deployment.ps1; - синхронизированы текстовые копии в
install-kit-awindows-20260427-211240/windows/; - invariant: existing live config сохраняется, но missing/new config
default всегда
sessionEvents.processEventsEnabled=false; включать можно только явно и временно для forensic/debug окна; - local gates: PowerShell AST parse для измененных scripts/modules OK,
ansible-playbook -i inventory.ini deploy_aw_windows.yml --syntax-checkOK; - production deploy: обновленные PowerShell файлы скопированы в
C:\Program Files\AWatch-rus\windowsс UTF-8 BOM для Windows PowerShell 5.1; remote backup перед заменой:C:\ProgramData\AWatch-rus\switch-backups\windows-safe-default-20260602T091213Z; hardening-recovery.ps1выполнен по existingC:\ProgramData\AWatch-rus\deployment-config.json; после recovery config осталсяprocessEventsEnabled=false,logonEnabled=true,pollSeconds=5, users включаютHOST-EXAMPLE\Администратор;- exact task check:
ActivityWatch Launch [HOST-EXAMPLE_Администратор]существует, ошибочныйActivityWatch Launch [HOST-EXAMPLE_Administrator]отсутствует,ActivityWatch Recoveryсуществует; AWatchRusCollectorGuardrunning/automatic; Windowsvalidate-deployment.ps1вернулoverallOk=True;- production AW DB guard: session rows
174, recent process events0, latesteventType=logon, DB около353MiB; - final gates зеленые:
check-aw-databuckets fresh/event-driven OK,aw-health-checkOK,dlp-health-check22/0/0,detmir-checkbucket_ok8, stale0, dead0,detmir-auto --no-healOK,detmir-statusseverity OK иok_for_operator=true, failed units на AW/Proxmox0; - краткий transient после recovery: один прогон
detmir-autoувиделfile-operations sendFailuresDelta; повтор через 30 секунд показалsendFailuresDelta=0, поэтому коррекция collectors не потребовалась; - Telegram bot/watchdog оставлены
inactiveпо принятому решению.
- исправлены Windows deploy/recovery defaults, чтобы новый deploy,
ensemble/domain deploy, hardening recovery и общий
[done]Пересобрать и проверить Windows install kit:- выполнен штатный
./scripts/rebuild_install_kit.sh; - пересобраны:
install-kit-awindows-20260427-211240/,install-kit-awindows-20260427-211240.zip,install-kit-awindows-20260427-211240.tar.gzиMANIFEST.txt; ./scripts/check_install_kit_vs_repo.sh: compared62, missing0, mismatched0, PowerShell mismatches0;- manifest checksum verification: OK, entries
66; - archives verification: zip и tar.gz распакованы во временные каталоги,
опасные defaults не найдены; подтверждены
[bool]$ProcessEventsEnabled = $false,effectiveProcessEventsEnabled ... else { $false },sessionProcessEventsEnabled ... else { $false },aw_windows_process_events_enabled: false; - PowerShell AST parse для kit scripts/modules OK;
ansible-playbook -i inventory.example.ini deploy_aw_windows.yml --syntax-checkиз install-kit ansible каталога OK.
- выполнен штатный
[done]Перенести install-kit consistency checker на Rust:- добавлен crate
check-install-kit-vs-repo; scripts/check_install_kit_vs_repo.shтеперь Rust-first wrapper: ищетCHECK_INSTALL_KIT_VS_REPO_RUST,$CARGO_TARGET_DIR/release/check-install-kit-vs-repo,adk-rust/target/release/check-install-kit-vs-repo,/usr/local/bin/check-install-kit-vs-repo, затем использует Python fallback;- сохранен текстовый output contract:
Compared files,Missing in repo,Mismatched content,PowerShell mismatches; добавлен--json; - поведение усилено: missing/mismatch теперь дают non-zero exit code, чтобы рассинхрон install-kit не проходил молча;
scripts/check_detmir_rust_release_artifacts.shтеперь требуетcheck-install-kit-vs-repo;- после обновления wrapper выполнен
./scripts/rebuild_install_kit.sh, чтобы install kit нес актуальный Rust-first wrapper; - gates:
cargo fmt --all -- --check,cargo test -p check-install-kit-vs-repo(3 passed),cargo clippy -p check-install-kit-vs-repo --all-targets -- -D warnings, release build OK,./scripts/check_install_kit_vs_repo.shOK, negative mismatch smoke rc1,./scripts/validate_install_kit.shOK, artifact check OK.
- добавлен crate
[done]Перенести install-kit validator на Rust:- добавлен crate
validate-install-kit; scripts/validate_install_kit.shтеперь Rust-first wrapper: ищетVALIDATE_INSTALL_KIT_RUST,$CARGO_TARGET_DIR/release/validate-install-kit,adk-rust/target/release/validate-install-kit,/usr/local/bin/validate-install-kit, затем использует legacy Bash/Python fallback;- сохранен stage output:
[1/4] Required files presence,[2/4] Manifest checksum verification,[3/4] Manifest completeness,[4/4] Archive composition check,validate_install_kit: OK; добавлен--json; - Rust validator проверяет required files, SHA256 из
MANIFEST.txt, полноту manifest относительно install-kit directory, совпадение состава zip/tar.gz и корректный archive prefix; scripts/rebuild_install_kit.shтеперь включаетscripts/validate_install_kit.shв install-kitscripts/;scripts/check_detmir_rust_release_artifacts.shтеперь требуетvalidate-install-kit;- после wrapper change выполнен
./scripts/rebuild_install_kit.sh; - gates:
cargo fmt --all -- --check,cargo test -p validate-install-kit(4 passed),cargo clippy -p validate-install-kit --all-targets -- -D warnings, release build OK, Rust-first./scripts/validate_install_kit.shOK (MANIFEST complete: 67 files tracked,Archives match: 68 files),./scripts/check_install_kit_vs_repo.shOK (compared 62, mismatches0), negative manifest checksum smoke rc1, negative archive mismatch smoke rc1, artifact check OK.
- добавлен crate
[done]Перенести InnoSetup installer verifier на Rust:- добавлен crate
verify-innosetup-installer; scripts/verify_innosetup_installer.shтеперь Rust-first wrapper: ищетVERIFY_INNOSETUP_INSTALLER_RUST,$CARGO_TARGET_DIR/release/verify-innosetup-installer,adk-rust/target/release/verify-innosetup-installer,/usr/local/bin/verify-innosetup-installer, затем использует legacy Wine/Bash fallback;- Rust verifier создает чистый Wine prefix, silent-install'ит
windows/installkit/innosetup/AWatch-rus-InstallKit.exeвC:\AWatchRusExtract, сверяет payload guard/policy файлов с репозиторием и проверяет self-test markercollector guard self-test OK; - первый прогон корректно поймал устаревший installer payload:
aw-collector-guard.ps1иinstall-collector-guard-service.ps1отличались от repo после safe-default hardening; - installer пересобран штатным
windows/installkit/innosetup/build_with_wine.sh, после чего Rust-first verifier завершилсяverify_innosetup_installer: OK; scripts/check_detmir_rust_release_artifacts.shтеперь требуетverify-innosetup-installer.
- добавлен crate
[done]Перенести install-kit rebuild path на Rust:- добавлен crate
rebuild-install-kit; scripts/rebuild_install_kit.shтеперь Rust-first wrapper: ищетREBUILD_INSTALL_KIT_RUST,$CARGO_TARGET_DIR/release/rebuild-install-kit,adk-rust/target/release/rebuild-install-kit,/usr/local/bin/rebuild-install-kit, затем использует legacy Bash fallback;- Rust rebuild сохраняет
*.deployment-config.json, пересобирает directory tree,MANIFEST.txt,.zipи.tar.gz; - копирование реализовано через read + replace-write, а не
fs::copy, потому что текущий mounted-диск возвращалOperation not permittedна truncate/copy existing files; - install-kit теперь включает сам
scripts/rebuild_install_kit.sh, чтобы комплект был самодостаточным для дальнейшей пересборки; - Rust-first rebuild verified через
validate_install_kit,check_install_kit_vs_repo, archive scriptcmp, and release artifact check.
- добавлен crate
[done]Перенестиquality-gate.shна Rust-first orchestrator:- добавлен crate
quality-gate; scripts/quality-gate.shтеперь Rust-first wrapper: ищетQUALITY_GATE_RUST,$CARGO_TARGET_DIR/release/quality-gate,adk-rust/target/release/quality-gate,/usr/local/bin/quality-gate, затем использует legacy Bash fallback;- Rust orchestrator сохраняет stage contract: Bash syntax,
ShellCheck when available, Node syntax when available, PowerShell parse
and collector guard self-test when
pwshavailable, Ansible syntax whenansible-playbookavailable; - final gates:
cargo fmt --all -- --check, targeted tests forverify-innosetup-installer,rebuild-install-kit,quality-gate,validate-install-kit, clippy-D warnings, release builds, Rust-firstquality-gate: OK, install-kit validate/check OK, InnoSetup verifier OK, and DetMir read-only status OK withok_for_operator=true.
- добавлен crate
[done]Перенести offline Sigma/Hayabusa IOC extraction path на Rust:- добавлен crate
extract-ioc-from-sigma; scripts/build_dlp_ioc_from_hayabusa.shтеперь Rust-first wrapper: ищетEXTRACT_IOC_FROM_SIGMA_RUST,$CARGO_TARGET_DIR/release/extract-ioc-from-sigma,adk-rust/target/release/extract-ioc-from-sigma,/usr/local/bin/extract-ioc-from-sigma, затем использует legacy Pythonscripts/extract_ioc_from_sigma.py;- Rust binary сохраняет CLI-контракт
--rules-root,--out-dir,--table-nameи output filesioc_blacklist.json,ioc_blacklist.csv,ioc_blacklist.sql; - extraction contract сохранен:
Image|endswith,CommandLine|contains,OriginalFileName,Hashes|SHA256и SHA256 values embedded inHashesstrings; - parity fix: Rust использует Unicode lowercase и PyYAML-compatible
True/Falserendering для YAML 1.1 boolean-like literals, чтобы не расширять IOC set относительно legacy Python; - real Hayabusa rules parity:
rules_scanned=4963,iocs_extracted=13510,commandline_contains=10192,original_filename=457,process_image_endswith=1171,sha256=1690,missing_in_rust=0,extra_in_rust=0; - gates:
cargo fmt --all -- --check,cargo test -p extract-ioc-from-sigma(3 passed),cargo clippy -p extract-ioc-from-sigma --all-targets -- -D warnings, release build OK,scripts/check_detmir_rust_release_artifacts.shOK, Rust-firstscripts/quality-gate.shOK.
- добавлен crate
[done]Перенестиscripts/rdp-worktime-report.shна Rust-first helper:- добавлен crate
rdp-worktime-report; scripts/rdp-worktime-report.shтеперь Rust-first wrapper: ищетRDP_WORKTIME_REPORT_RUST,$CARGO_TARGET_DIR/release/rdp-worktime-report,adk-rust/target/release/rdp-worktime-report,/usr/local/bin/rdp-worktime-report, затем использует legacy embedded Python fallback;- Rust binary сохраняет CLI/env contract:
--day today|yesterday,--from YYYY-MM-DD --to YYYY-MM-DD,AW_BASE_URL,AW_WORKTIME_HOST,AW_WORKTIME_DEFAULT_SAMPLE_SECONDS,AW_WORKTIME_MAX_SAMPLE_SECONDS,OUT_DIR; - output contract сохранен:
rdp-worktime-<FROM>_<TO>.csv,rdp-worktime-<FROM>_<TO>.json, stdout paths plusCSV:/JSON:; - calculation contract сохранен: bucket
aw-worktime-sessions_<host>, active samples byactive=trueor Russian/English active state, interval merge, sampleSeconds/pollSeconds, duration and next-timestamp fallback, canonicalHOST\useruserId; - live parity on yesterday against Python fallback:
rust_csv_rows=3,python_csv_rows=3,csv_equal=True, JSONhost/bucket_id/from/to/rowsall equal; - gates:
cargo fmt --all -- --check,cargo test -p rdp-worktime-report(3 passed),cargo clippy -p rdp-worktime-report --all-targets -- -D warnings, release build OK,scripts/check_detmir_rust_release_artifacts.shOK,scripts/quality-gate.shOK, DetMir read-only status OK withdlp_counts={ok:22,warn:0,fail:0}andok_for_operator=true.
- добавлен crate
[done]Перенести Proxmox DetMir contour smoke на Rust-first helper:- добавлен crate
aw-contour-smoke; scripts/aw-contour-smoke-gateway.shтеперь Rust-first wrapper: ищетAW_CONTOUR_SMOKE_RUST,$CARGO_TARGET_DIR/release/aw-contour-smoke,adk-rust/target/release/aw-contour-smoke,/usr/local/sbin/aw-contour-smoke,/usr/local/bin/aw-contour-smoke, затем использует legacy Bash fallback;scripts/aw-contour-smoke-local.shумеет временно доставлять release binary на Proxmox для shadow smoke без постоянного platform change;- remote parity: Rust и legacy оба вернули
OK=30 WARN=0 FAIL=3 SKIP=0,rc=2; все 3 fail совпали и относятся к существующему gateway-authHTTP 401на/go/proxmox-gui,/go/file1c-brief,/go/file1c-actions, не к Rust regression; - gates:
cargo fmt --all -- --check,cargo test -p aw-contour-smoke(2 passed),cargo clippy -p aw-contour-smoke --all-targets -- -D warnings, release build OK,bash -nwrappers OK, artifact check OK,quality-gate.shOK.
- добавлен crate
[done]Перенестиscripts/diag_and_manual_restart.shна Rust-first diagnostic helper:- добавлен crate
diag-and-manual-restart; - wrapper ищет
DIAG_AND_MANUAL_RESTART_RUST,$CARGO_TARGET_DIR/release/diag-and-manual-restart,adk-rust/target/release/diag-and-manual-restart,/usr/local/bin/diag-and-manual-restart, затем использует legacy Bash fallback; - здоровый контур не перезапускается: helper запускает
aw-health-checkиdlp-health-check, печатаетDiagnostics: healthy. Restart not needed.и выходит0; - mutation path требует явного подтверждения или
--yes; live restart не запускался, потому что контур был зеленый; - gates: fmt/test/clippy/release OK,
bash -nOK, Rust-first healthy path rc0, legacy fallback healthy path rc0, artifact check OK,quality-gate.shOK, DetMir read-only OK.
- добавлен crate
[done]Перенести browser smoke launcher на Rust-first helper:- добавлен crate
aw-browser-smoke; scripts/aw-webui-browser-smoke.shтеперь Rust-first wrapper: ищетAW_BROWSER_SMOKE_RUST,$CARGO_TARGET_DIR/release/aw-browser-smoke,adk-rust/target/release/aw-browser-smoke,/usr/local/bin/aw-browser-smoke, затем использует legacy Node fallback;- Playwright/Chromium логика остается в
scripts/aw-webui-browser-smoke.mjs; Rust отвечает за устойчивый запуск,NODE_PATH, passthrough args и сохранение child exit code; - fake-node проверки подтвердили passthrough
.mjspath, user args,NODE_PATHи exit-code propagation для Rust launcher и legacy fallback; - live browser smoke через Rust-first wrapper:
ok=true, страницыaw_webui_home,worktime_today_html,worktime_management_htmlвсе OK; - gates:
cargo fmt --all -- --check,cargo test -p aw-browser-smoke(3 passed),cargo clippy -p aw-browser-smoke --all-targets -- -D warnings, release build OK,bash -nOK, artifact check OK,quality-gate.shOK, DetMir read-only OK withdlp_counts={ok:22,warn:0,fail:0}andok_for_operator=true.
- добавлен crate
[done]Перенестиcheck-aw-full.shна Rust-first read-only helper:- добавлен crate
check-aw-full; - root wrapper
check-aw-full.shищетCHECK_AW_FULL_RUST,$CARGO_TARGET_DIR/release/check-aw-full,adk-rust/target/release/check-aw-full,/usr/local/bin/check-aw-full, затем выполняет embedded legacy Bash fallback; - для прямого legacy compare добавлен
CHECK_AW_FULL_FORCE_LEGACY=1; - Rust helper сохраняет операторский контракт: AW server connectivity,
CORS probe, 8 bucket freshness rows, RDP WinRM/SSH TCP checks, summary
FRESH/STALE/DEAD, recovery hint when stale/dead collectors exist; - live parity: Rust-first и legacy оба показали connectivity OK, CORS OK,
WinRM OK, SSH OK,
FRESH=8 STALE=0 DEAD=0; bucket statuses совпали, один live event id уWindow watcherожидаемо сдвинулся между последовательными запусками; - gates:
cargo fmt --all -- --check,cargo test -p check-aw-full(4 passed),cargo clippy -p check-aw-full --all-targets -- -D warnings, release build OK,bash -nOK, artifact check OK,quality-gate.shOK, DetMir read-only OK withdlp_counts={ok:22,warn:0,fail:0}andok_for_operator=true.
- добавлен crate
[done]Перенести merge engine на Rust:- добавлен crate
merge-aw-server-dbs; - рабочий runtime path:
adk-rust/target/release/merge-aw-server-dbsи/usr/local/bin/merge-aw-server-dbs; - legacy
scripts/merge_aw_server_dbs.pyудален из repo/runtime path; ansible/deploy_aw_server.ymlустанавливает/usr/local/bin/merge-aw-server-dbsи использует его без Python;- Rust сохраняет контракт
--base,--overlay,--output, SQLite backup-copy base DB, bucket matching by id/key/name, duplicate event suppression by(starttime,endtime,data), JSON summary; - offline parity fixture: Rust replacement дал ожидаемый JSON
inserted_buckets=1,inserted_events=3и одинаковые SQLite table rows (buckets=3,events=5) на кейсе duplicate event + name-conflict bucket; - gates:
cargo fmt --all -- --check,cargo test -p merge-aw-server-dbs(2 passed),cargo clippy -p merge-aw-server-dbs --all-targets -- -D warnings, release build OK, artifact check OK,quality-gate.shOK, DetMir read-only OK after DLP baseline retry withdlp_counts={ok:22,warn:0,fail:0}andok_for_operator=true.
- добавлен crate
[done]Перенестиscripts/prod_backup_restore.shв safe-by-default Rust planner/checker:- добавлен crate
prod-backup-restore; scripts/prod_backup_restore.shтеперь Rust-first wrapper: ищетPROD_BACKUP_RESTORE_RUST,$CARGO_TARGET_DIR/release/prod-backup-restore,adk-rust/target/release/prod-backup-restore,/usr/local/bin/prod-backup-restore;- обычный запуск больше не выполняет restore при наличии Rust artifact: он печатает plan-only flow и помечает destructive steps;
- если Rust artifact отсутствует, wrapper отказывается запускать destructive legacy случайно и просит собрать Rust planner;
- старый Python destructive flow удален;
--apply-legacyбольше не поддерживается; - Rust
--applyнамеренно запрещен на этом этапе; --check-inputs --jsonвалидирует env/files/commands без вывода секретов, проверяетAW_SSH_PASSWORD,AW_WINRM_PASSWORD,sshpass,ansible-playbook, inventory иmerge-aw-server-dbs;- план сохраняет порядок restore flow: scp Rust merge binary, remote backup dir,
DB existence checks, DB backups, stop
activitywatch-server, merge DB, install merged DB, 3 ansible playbooks, final AW validation; - safe smoke: plan text OK, JSON valid (
steps=12,missing_count=0), destructive steps present and marked, password names/values не попадают в planned commands, Rust--applyexits rc1; - gates:
cargo fmt --all -- --check,cargo test -p prod-backup-restore(3 passed),cargo clippy -p prod-backup-restore --all-targets -- -D warnings, release build OK,bash -n scripts/prod_backup_restore.sh, artifact check OK,quality-gate.shOK, DetMir read-only OK withdlp_counts={ok:22,warn:0,fail:0}andok_for_operator=true; transientservice_warnings=1disappeared on immediatedetmir-check --jsonrepeat (service_warnings=0).
- добавлен crate
[done]Перенестиscripts/prod_rollout.shв Rust-first safe planner/orchestrator:- добавлен crate
prod-rollout; scripts/prod_rollout.shтеперь Rust-first wrapper: ищетPROD_ROLLOUT_RUST,$CARGO_TARGET_DIR/release/prod-rollout,adk-rust/target/release/prod-rollout,/usr/local/bin/prod-rollout;- обычный запуск больше не стартует production deploy автоматически: он показывает plan-only rollout flow;
- реальный rollout через Rust доступен только явно:
scripts/prod_rollout.sh --apply; - старый Bash rollout сохранен только явно:
scripts/prod_rollout.sh --apply-legacy; --check-inputs --jsonвалидируетAW_SSH_PASSWORD,AW_WINRM_PASSWORD,git,ansible,ansible-playbook, inventory,quality-gate.sh,deploy_aw_server.yml,deploy_aw_windows.ymlиpost_validate_aw_windows.ymlбез вывода секретов;- план сохраняет legacy порядок:
quality-gate, Ansible ping AW server, Ansible win_ping AW Windows,deploy_aw_server --check --diff, realdeploy_aw_server,deploy_aw_windows --check --diff, realdeploy_aw_windows,post_validate_aw_windows; - mutation в плане помечена только для
deploy-aw-serverиdeploy-aw-windows; - apply-runner пишет stdout/stderr каждого шага в
.rollout-logs/<timestamp>/, общий ход - вrollout.log; - safe smoke:
scripts/prod_rollout.sh --check-inputs --jsonвернулmode=plan-only,missing_count=0,steps=8, mutationsdeploy-aw-serverиdeploy-aw-windows; - gates:
cargo fmt --all -- --check,cargo test -p prod-rollout(3 passed),cargo clippy -p prod-rollout --all-targets -- -D warnings, release build OK,bash -n scripts/prod_rollout.sh, artifact check OK.
- добавлен crate
[done]Перенестиaw-server/ensure-reliability.shв Rust-first dry-run/apply helper:- добавлен crate
aw-ensure-reliability; aw-server/ensure-reliability.shтеперь Rust-first wrapper: ищетAW_ENSURE_RELIABILITY_RUST,$CARGO_TARGET_DIR/release/aw-ensure-reliability,adk-rust/target/release/aw-ensure-reliability,/usr/local/bin/aw-ensure-reliability;- обычный запуск больше не делает
chown,systemctl stop/start, logrotate write или health timer write; он показывает dry-run plan; - реальный Rust repair требует explicit
--apply; - старый Bash repair доступен только explicit
--apply-legacy; - Rust helper сохраняет legacy action set: env check,
ownership/mode repair for
/var/lib/activitywatch,/var/log/activitywatch,/opt/activitywatch, logrotate install, health-check script/timer/service install, ordered AW service restart and enable; deploy_aw_server.ymloptional устанавливает/usr/local/bin/aw-ensure-reliabilityдо Influx token checks, если release artifact доступен;- production binary доставлен на AW server, но
--applyне запускался; - production dry-run:
apply=false,ok=true,missing=0,executed=0,steps=25; - final production gates: AW failed units
0, DetMir status OK withservice_warnings=0,dlp_counts={ok:22,warn:0,fail:0},ok_for_operator=true; - gates:
cargo fmt --all -- --check,cargo test -p aw-ensure-reliability(2 passed),cargo clippy -p aw-ensure-reliability --all-targets -- -D warnings, release build OK,bash -n aw-server/ensure-reliability.sh, artifact check OK,ansible-playbook deploy_aw_server.yml --syntax-checkOK.
- добавлен crate
[done]Перевести Linux install scripts в Rust-first safe planner слой:- добавлен crate
aw-linux-install; - wrappers теперь Rust-first и dry-run по умолчанию:
scripts/install_aw_linux_client.sh,scripts/install_aw_linux_remote_worker.sh,scripts/install_aw_linux_web_category_logger.sh,scripts/install_aw_console_ssh_logger.sh,scripts/install_aw_pve_webadmin_logger.sh; - real install требует explicit
--apply; - old shell install требует explicit
--apply-legacy; aw-linux-install --applyзапускает соответствующий legacy script с--apply-legacy, сохраняя embedded Python collectors и shell install contract;remote_workerlegacy path исправлен: вложенные client/console/web installers вызываются с--apply-legacy, чтобы real legacy install не превратился в dry-run после оборачивания;- PVE webadmin logger не запускался и Proxmox platform не менялся; это только safe planner wrapper;
- gates:
cargo fmt --all -- --check,cargo test -p aw-linux-install(2 passed),cargo clippy -p aw-linux-install --all-targets -- -D warnings, release build OK,sh -nдля всех пяти wrappers OK, dry-run JSON для всех пяти wrappers OK, artifact check OK.
- добавлен crate
[done]Закрепить проверку актуализации и правильности Grafana данных:- добавлен crate
detmir-grafana-check; - check читает Grafana dashboard API, валидирует форму dashboard,
отсутствие старых
aw_window_event/aw_afk_event, наличие текущих worktime measurements и выполняет все panel queries через Grafana datasource API; - freshness panel считается обязательным: значение свежести должно быть не
старше
DETMIR_GRAFANA_MAX_FRESHNESS_MINUTES(production default 360); - результат пишется в Grafana CT 201:
/var/lib/detmir-grafana-check/latest.jsonиlatest.txt; - установлен
detmir-grafana-check.serviceи timer каждые 15 минут; detmir-checkтеперь читает последний Grafana-check artifact черезsudo -n /usr/sbin/pct exec 201и считаетgrafana-datarequired service check, так что общийdetmir-statusкраснеет при сломанной или устаревшей Grafana;- добавлен playbook
ansible/deploy_grafana_check.yml; scripts/check_detmir_rust_release_artifacts.shтеперь требуетdetmir-grafana-check;- production verification:
detmir-grafana-checkOK (ok=13,warn=0,fail=0), 7/7 panels have rows, freshness около 120 min, timer active, Grafana CT failed units 0, Proxmox failed units 0,detmir-checkservice_failures=0,detmir-auto --no-healrc 0,detmir-statusOK /ok_for_operator=true.
- добавлен crate
[done]Реализовать read-only MVPdetmir-portal:- добавлен crate
detmir-portal; - portal работает как Rust web service на Proxmox:
/usr/local/bin/detmir-portal,detmir-portal.service, bind127.0.0.1:8720; - внешний route добавлен в существующий nginx gateway:
https://<PUBLIC_GATEWAY_FQDN>/portal/; - UI содержит вкладки
Оператор,Руководитель,Владелец,Инциденты ИБ; - API реализованы:
/api/health,/api/summary,/api/operator,/api/manager,/api/owner,/api/incidents,/api/links; - источники read-only:
detmir-status --json,detmir-check --json,systemctl --failed, AW Worktime API, 1C analytics health; - portal не делает write/heal/restart actions и не трогает pfSense, Telegram runtime, NAT/DNS/VPN;
- добавлен deployment playbook
ansible/deploy_detmir_portal.yml; - gateway playbook теперь проверяет
/portal/api/healthс auth; scripts/check_detmir_rust_release_artifacts.shтеперь требуетdetmir-portal;- gates:
cargo fmt --all -- --check,cargo test -p detmir-portal(3 passed),cargo clippy -p detmir-portal --all-targets -- -D warnings, release build OK,ansible-playbook deploy_detmir_portal.yml --syntax-check,ansible-playbook deploy_proxmox_web_gateway.yml --syntax-check, artifact check OK; - production verification:
detmir-portalactive, nginx active,/portal/api/healthOK through gateway auth, all sources true, external/portal/returns protected401without auth, gateway/healthzreturnsok,detmir-statusOK /ok_for_operator=true, Proxmox failed units 0; - browser verification through production tunnel: desktop and mobile
nonblank, tabs work, API requests 200, console errors 0. Screenshots:
.playwright-cli/page-2026-06-02T17-31-49-049Z.png,.playwright-cli/page-2026-06-02T17-33-50-318Z.png,.playwright-cli/page-2026-06-02T17-34-04-725Z.png. - portal link repair after Grafana/gateway smoke: quick links now open in a
separate tab,
worktime_reportpoints to explicit HTML/reports/worktime/management?format=html&host=HOST-EXAMPLE,1С действияis shown in the portal, and gateway/r/aw-worktimeis pinned to the same HTML report. Production smoke: AW UI, Worktime, 1C brief, and 1C actions returned200 text/html; Grafana links correctly redirect to/loginwhen no Grafana session exists. - incident operator workflow added to portal: incidents now have stable
IDs, operator state is persisted in
/var/lib/detmir-portal/incidents-state.json, audit events append to/var/lib/detmir-portal/audit.jsonl, nginx passes$remote_userasX-Remote-User, and the UI exposesВ работу/Назначитьactions. This layer only records acknowledgement/assignment metadata; it does not heal, restart, mutate AW/DLP/1C, or touch pfSense/Telegram. Gates:cargo fmt --all -- --check,cargo test -p detmir-portal(5 passed),cargo clippy -p detmir-portal --all-targets -- -D warnings, release build OK. Production smoke:POST /portal/api/incidents/actionreturned200 application/json, actor resolved todetmir, state/audit files were written, smoke state was removed after validation, portal health stayed200,detmir-statusstayedOK / ok_for_operator=true, and failed units stayed0. Local Playwright smoke against a temporary portal instance verifiedИнциденты ИБ,В работу, persisted UI state, disabled ack button, and zero JS errors. Инциденты ИБtightened to a DLP-focused card: the tab now filters the card to DLP/incident/case items, adds a synthetic DLP-count incident whendlp_counts.warn/failis non-zero, and shows only DLP/Grafana dashboard links (detmir-dlp-security,detmir-dlp-management,awatch-dlp-overview, Grafana catalog). Worktime, 1C, AW UI, and other non-incident operational links are intentionally not shown in this card. Local Playwright smoke verified DLP incident rendering, dashboard links, no Worktime/1C text in the card, and zero JS errors.- DLP evidence viewing layer added to the portal:
detmir-portalnow exposes/api/dlp/evidenceand safe screenshot routes by opaque evidence id. The service reads DLP warehouse SQLite read-only, extractsscreenshotSha256/dimensions from raw event JSON, and serves image files only from an allowlisted evidence root after canonical path, extension, size, and SHA-256 validation. Evidence views and downloads append toevidence-audit.jsonl. Production uses an AW-server evidence-only service,/usr/local/bin/detmir-portal-evidencewithdetmir-portal-evidence.service, because the DLP warehouse lives on the AW server. Proxmox nginx gateway routes/portal/api/dlp/evidence*to<AW_SERVER_HOST>:8721. Current production verification: AW evidence APIok=true, gateway evidence routeok=true,db_available=true, 11 DLP evidence rows returned,screenshot_available=0because current stored rows do not yet contain screenshot metadata, both portal/evidence services active, failed units 0,detmir-statusOK / ok_for_operator=true. Local HTTP smoke verified byte-identical screenshot serving and audit logging; local Playwright smoke verified theДоказательстваblock,СКРИН,Открыть,Скачать, and zero JS errors. - DLP evidence screenshot delivery is now automated in production:
the AW evidence API accepts authenticated uploads at
POST /api/dlp/evidence/uploadin evidence-only mode. Uploads require a server-generated Bearer token stored outside git, validate base64 body, PNG/JPEG magic, max size, and exact SHA-256 before atomic write to/var/lib/activitywatch/dlp-evidence/screenshots/<sha256>.(png|jpg). Windows RDP host runssync-dlp-evidence-artifacts.ps1through scheduled taskActivityWatch DLP Evidence Syncevery 5 minutes as SYSTEM. The sync scansC:\ProgramData\AWatch-rus\incident-artifactsplus configured artifact roots, uploads new PNG screenshots, and keeps local upload state inC:\ProgramData\AWatch-rus\dlp-evidence-sync-state.json. Production controlled test: a visible PNG was created on Windows, sync uploaded it (uploaded=1,failed=0), a temporary warehouse row made it visible in the portal withscreenshot_available=true, gateway preview returnedimage/pngwith matching SHA, external Playwright verified the portal UI and preview, audit recorded upload/view, unauthenticated upload returned 403, and the synthetic row/files/state were removed. Final state: evidence count returned to 11, synthetic hit false, AW/Proxmox failed units 0,detmir-statusOK, sync task Ready withlastTaskResult=0. - during this deploy,
detmir-grafana-checkwas corrected so empty detail-only panels for employees/applications are WARN, not FAIL. The mandatory freshness/summary panels still fail the check when stale or empty. This removed a false red state shortly after midnight whentodaydetail rows were legitimately empty. Production verification: Grafana checkok=truewithfail=0,detmir-auto --no-healrc0, portal healthtrue,detmir-statusOK / ok_for_operator=true, and failed units0. docs/DETMIR_THREAT_MODEL_RU.mdadded as the current working threat model for DetMir. It records the product as an operational control and technical audit platform, not a certified DLP/SIEM/EDR/XDR or FSTEC SZI. It also records Igor as the declared product owner, lists assets, trust zones, attacker/operator-failure classes, implemented evidence controls, residual risks, and the hardening roadmap.docs/DETMIR_RUSSIAN_SOFTWARE_REGISTRY_POSITIONING_RU.mdadded as the registry/product positioning note. Current decision: lead with DetMir as an operational control and IT infrastructure management platform, use09.10as the primary Russian software registry class target, keep DLP/security/evidence/Hayabusa as applied modules, and prepare website, operator/admin docs, ownership package, screenshots, and dependency inventory before any registry filing.- registry proof package skeleton added:
docs/ADMIN_GUIDE_RU.md,docs/OPERATOR_GUIDE_RU.md,docs/INSTALL_RU.md,docs/ARCHITECTURE_RU.md,docs/OWNERSHIP_RU.md,docs/THIRD_PARTY_LICENSES_RU.md, anddocs/REGISTRY_CHECKLIST_RU.md. Naming decision fixed across the docs:DetMiris the product,AWatch-rusis the repository/technical base, and the external formula isDetMir, программный комплекс на базе AWatch-rus.
- добавлен crate
Отложить:
- post-MVP развитие
detmir-portal: role-aware views, safe check-now action, daily owner report, historical trends, AI summary with strict source citations, action buttons with allowlist and audit log. Детальный план:docs/DETMIR_PORTAL_GUI_PLAN_RU.md; - перенос Telegram bot runtime снят с плана: Python остается постоянным runtime, Rust используется только для backend helpers;
- перенос оставшихся install/runtime scripts на Rust;
- переписывание PowerShell Windows collector path;
- любые network/firewall/VPN изменения;
- pfSense полностью frozen/no-touch: текущего потенциала достаточно, NAT/gateway tooling не переносить и не дергать без отдельной явной команды.
12. Stop conditions
Миграцию остановить и откатить конкретный модуль, если:
- новый binary дает иной
severityбез объяснимой причины; - меняет exit code class на зеленом или красном контуре;
- пишет неполный/corrupt state;
- вызывает restart на зеленом контуре;
- ломает Telegram
/status; - ломает Grafana/Influx/Prometheus metric names;
- требует laptop-only path или интерактивный shell;
- выводит секреты в stdout/journald/report.
13. Рабочий принцип
Правильный перенос на Rust - это не переписывание строк один-в-один.
Для каждого legacy script надо сделать:
- понять контракт;
- зафиксировать fixture;
- написать typed model;
- реализовать read-only parity;
- включить shadow-mode;
- заменить production command;
- только потом добавлять action/mutation;
- удалить legacy только после периода стабильной эксплуатации.