feat(detmir): add rust-first operations tooling

This commit is contained in:
igor04091968
2026-06-02 17:57:58 +03:00
parent 60670d30a8
commit 19e3682bc8
263 changed files with 51678 additions and 718 deletions
+21 -1
View File
@@ -2,7 +2,7 @@
Дата фиксации: `2026-05-24`
Последнее runtime-уточнение: `2026-05-28`
Последнее runtime-уточнение: `2026-05-30`
Этот файл предназначен как единая рабочая опора по `DetMir`: что именно входит в систему, где это живет, каким инструментарием проект надо планировать и сопровождать, и какой операционный контур считать промышленным.
@@ -81,6 +81,26 @@
- Grafana dashboards без авторизации корректно редиректят на login, это не считается отказом; с сохраненной admin-учеткой проверены фактические страницы и datasource health;
- gateway `/go/file1c-brief`, `/go/file1c-actions`, `/go/aw-ui` ведет на рабочие внутренние surface.
### 2.3 Runtime-семантика операторских сигналов от 2026-05-30
После Phase 8 операторские проверки должны читаться по смыслу статуса, а не только по возрасту последнего события.
| Статус | Как трактовать |
|---|---|
| `FRESH` | Данные свежие, источник сейчас активен или недавно обновлялся. |
| `INACTIVE` | Нормальное состояние для desktop/window/DLP endpoint buckets, если worktime-сессия свежая, но интерактивной активности нет. Это не инцидент. |
| `EVENT-DRIVEN` | Нормальное состояние для buckets, которые пишутся только при событии: `aw-session-events_*`, `aw-dlp-incidents_*`, `aw-dlp-review_*`, `aw-dlp-rules_*`. Отсутствие новых событий само по себе не отказ. |
| `STALE` | Потенциальная деградация активного источника; проверять collector/session placement. |
| `DEAD` / `EMPTY` | Отказ или неинициализированный обязательный источник; требуется диагностика. |
Операторские правила:
- `./check-aw-full.sh` с `FRESH=8 STALE=0 DEAD=0` считается зеленым контуром, даже если отдельные строки показывают `INACTIVE` или `EVENT-DRIVEN`.
- `./check-aw-data.sh` должен использовать ту же семантику, что и full-check; `EVENT-DRIVEN` и `INACTIVE` не являются поводом для ручного recovery.
- SLO-строка бота `aw_rus_slo: recovered ... current_sample=OK ... budget_remaining_seconds<0` означает исторически сожженный error budget при здоровом текущем контуре. Это не активная авария.
- SLO становится инцидентом только при текущем `current_sample=FAIL` и исчерпанном бюджете или при stale SLO summary.
- `aw-browser-smoke.timer` хранит ограниченное число запусков через `AW_BROWSER_SMOKE_KEEP_RUNS` и ограничен `TimeoutStartSec=180`; рост `/var/lib/activitywatch/browser-smoke` выше нескольких сотен MB надо считать regression в retention/config.
### 2.2 Стабилизация management report, bridge и recovery от 2026-05-28
До стабилизации слабые места были такими:
+198
View File
@@ -0,0 +1,198 @@
# Cross-OS Native DLP Enforcement
## Цель
Расширять AWatch-rus как легкую DLP-систему без тяжелого монолитного endpoint-agent.
Базовый принцип: сначала использовать естественные механизмы ОС и управляемых браузеров,
а собственный код держать тонким слоем политики, телеметрии и корреляции.
Это не заменяет текущий Windows/RDP contour. Он остается основным production-контуром:
- `AWatchRusCollectorGuard` следит за collector health;
- `aw-rus-healthd` принимает решение по свежести бакетов и активности хоста;
- `tsj-guardian-bot` наблюдает и лечит только реальные деградации;
- DLP policy engine остается центральным источником правил.
## Единая модель политики
Новые OS-specific механизмы должны сводиться к одному контракту:
```json
{
"nativeControls": {
"mode": "monitor",
"channels": {
"removableStorage": {"action": "audit"},
"print": {"action": "audit"},
"clipboard": {"action": "audit"},
"browserUpload": {"action": "audit"},
"appExecution": {"action": "audit"}
}
}
}
```
Допустимые действия:
- `audit`: только событие в `aw-dlp-endpoint-signals_<host>`;
- `warn`: событие плюс локальное уведомление пользователя;
- `block`: блокировка штатным механизмом ОС или managed browser;
- `blockWithOverride`: блокировка с управляемым исключением, если платформа это поддерживает;
- `disabled`: канал выключен.
Для rollout по умолчанию используется `monitor/audit`. `block` включается только по одному
каналу и одной группе хостов после накопления baseline.
На Windows это правило enforced в `dlp-endpoint-signals-collector.ps1`: если endpoint-правило
просит `action: "block"`, но `nativeControls.mode` не равен `enforce` или канал не разрешает
`block`, collector подавляет enforcement и пишет инцидент с `enforcementSuppressed=true`.
## Windows
Windows остается самой зрелой платформой для ближайшего enforcement.
Предпочтительный порядок:
1. USB/removable storage:
- мониторинг: текущий `dlp-endpoint-signals-collector.ps1`;
- enforcement: GPO/registry/device installation restrictions и `Set-Disk -IsReadOnly`;
- область блокировки: запись на removable media, не чтение.
2. Print:
- мониторинг: `Microsoft-Windows-PrintService/Operational`;
- enforcement: отмена print job штатным spooler API/CIM;
- rollout: сначала только документы с совпадением `documentRegex`.
3. Clipboard:
- мониторинг: текущий endpoint collector;
- enforcement: очистка clipboard только для high-confidence правил;
- риск: UX и ложные срабатывания, поэтому `block` не включать глобально.
4. App execution:
- enforcement: AppLocker или WDAC/App Control for Business;
- назначение: не “DLP content”, а сужение каналов утечки через запрещенные приложения.
5. Browser upload:
- легкий путь: managed browser policy + URL/category rules;
- сильный путь: расширение браузера или коммерческий DLP browser control, если нужен inline file upload block.
Ограничение: AppLocker/WDAC управляют запуском кода, но не поведением уже запущенного приложения.
Поэтому они дополняют DLP, а не заменяют USB/print/browser enforcement.
## macOS
Для macOS нельзя идти через kernel extension как основной путь. Современный естественный
вариант - System Extensions и профиль управления через MDM.
Предпочтительный порядок:
1. Monitor-only agent:
- LaunchDaemon + Swift/Go helper;
- публикация событий в AW buckets;
- локальный health heartbeat по аналогии с `aw-rus-collector-guard_<host>`.
2. Endpoint Security system extension:
- file open/write/exec telemetry;
- deny только после отдельного PoC и подписи/entitlements;
- обязательная MDM-подготовка approval profile.
3. Network Extension:
- для DNS/proxy/web egress контроля;
- лучше применять к managed domains, не как полный MITM по умолчанию.
4. MDM restrictions:
- screen capture, AirDrop, external media, profile-level restrictions;
- это самый легкий enforcement, если парк управляется MDM.
Ограничение: без MDM и Apple Developer entitlements macOS enforcement будет хрупким.
Для неуправляемых Mac оставляем monitor-only.
## Linux
Linux должен быть легким и дистрибутивно-нейтральным. Не начинать с “универсального агента,
который перехватывает все syscalls”.
Предпочтительный порядок:
1. fanotify file gate:
- мониторинг и permission events для чувствительных каталогов;
- хороший MVP для removable mount points, home/project shares, export directories;
- блокировка через permission response до открытия файла.
2. auditd/journald collectors:
- дешево для exec, sudo, mount, removable media, ssh/scp hints;
- подходит для server/workstation baseline.
3. eBPF telemetry:
- использовать для observability: process, connect, file metadata;
- enforcement только через BPF LSM на поддерживаемых ядрах и после отдельной совместимости.
4. Desktop clipboard/print:
- monitor-only через DE-specific tools (`wl-paste`, `xclip`, CUPS logs);
- block режим только для управляемых рабочих станций.
Ограничение: Linux enforcement сильно зависит от ядра, LSM stack и дистрибутива. Поэтому
первая production-версия должна быть fanotify + auditd, а eBPF оставить как расширяемый слой.
## ChromeOS и managed browser
Для ChromeOS не надо писать свой endpoint-agent. Если устройства управляются через Google
Admin Console, использовать ChromeOS Data Controls:
- copy/paste;
- printing;
- screen capture/screen sharing;
- file open/upload/transfer;
- removable storage.
Для Windows/macOS/Linux браузерный контур должен быть policy-first:
- Edge/Chrome enterprise policies;
- URL/category lists из текущего DLP policy engine;
- расширение браузера только когда нужен inline upload/file decision, а не только telemetry.
## Priority Backlog
### P0 - сейчас
- Привести все health-check скрипты к единой inactive/guard-aware классификации.
- Хранить `nativeControls` в policy document как forward-compatible секцию.
- Не включать новый `block` глобально; только `audit` и `warn`.
### P1 - Windows production
- USB write-block profile через GPO/PowerShell.
- Print cancel по `documentRegex`.
- Browser upload detection по managed URL categories.
- Guard heartbeat для applied nativeControls version/checksum.
### P2 - Linux MVP
- `aw-linux-file-gate` на fanotify для monitor/audit по каталогам.
- `aw-linux-audit-collector` для mount/usb/scp/sudo/process telemetry.
- systemd unit + heartbeat bucket.
### P3 - macOS MVP
- `aw-macos-monitor` LaunchDaemon без enforcement.
- MDM profile checklist.
- System Extension PoC только для managed Macs.
### P4 - managed browser / ChromeOS
- Транслятор DLP policy domains/categories в Chrome/Edge policy bundle.
- ChromeOS Data Controls mapping для организаций с Google Workspace.
## Rollout Rules
- Каждая новая платформа стартует в `monitor`.
- `block` разрешен только после минимум 7 дней clean baseline.
- Любой block должен писать:
- rule id;
- action;
- platform;
- native mechanism;
- enforcement result;
- override id, если применимо.
- Если local guard/heartbeat старше порога, центральный бот не делает широкий restart,
а переводит платформу в degraded и запускает platform-specific recovery.
## Sources
- Microsoft AppLocker overview: https://learn.microsoft.com/en-us/windows/security/application-security/application-control/app-control-for-business/applocker/applocker-overview
- Microsoft AppLocker policy design: https://learn.microsoft.com/en-za/windows/security/application-security/application-control/app-control-for-business/applocker/understand-applocker-policy-design-decisions
- Microsoft Purview Chrome DLP extension: https://learn.microsoft.com/en-us/purview/dlp-chrome-learn-about
- Apple system extensions deployment: https://support.apple.com/guide/deployment/system-extensions-in-macos-depa5fb8376f/web
- Linux fanotify manual: https://man7.org/linux/man-pages/man7/fanotify.7.html
- eBPF docs: https://docs.ebpf.io/
- ChromeOS Data Controls: https://support.google.com/chrome/a/answer/11587610
+32
View File
@@ -13,10 +13,31 @@ Phase 2.5 расширяет DLP endpoint collector функциями **акт
Во всех случаях пользователь получает Windows-уведомление (balloon notification) с описанием причины блокировки.
Для cross-OS развития используется отдельный native-first профиль:
`docs/dlp-cross-os-native-enforcement.md`. Он задает правило: Windows enforcement оставляем
на штатных GPO/AppLocker/Spooler/Storage механизмах, macOS строим через System Extensions/MDM,
Linux через fanotify/auditd/eBPF, ChromeOS через Data Controls. Тяжелый универсальный агент
не является целевой архитектурой.
## Конфигурация политики
Формат `dlp-policy.json` не изменился — поле `action` в правиле теперь поддерживает значение `"block"` наряду с `"alert"` (по умолчанию).
Важно: для production safety `action: "block"` в конкретном endpoint-правиле не включает
блокировку сам по себе. Реальное enforcement-действие выполняется только если:
- `nativeControls.mode` равно `"enforce"`;
- `nativeControls.rollout.allowGlobalBlock=true`;
- и `nativeControls.channels.<channel>.action` равно `"block"` или `"blockWithOverride"`.
Если правило просит `block`, но native controls находятся в `monitor`, collector пишет инцидент
как `alert` и добавляет поля:
- `requestedAction: "block"`;
- `enforcementMode`;
- `nativeChannelAction`;
- `enforcementSuppressed: true`.
### Пример: блокировка USB записи
```json
@@ -27,6 +48,17 @@ Phase 2.5 расширяет DLP endpoint collector функциями **акт
"severity": "medium",
"cooldownSeconds": 300
},
"nativeControls": {
"mode": "enforce",
"rollout": {
"allowGlobalBlock": false
},
"channels": {
"clipboard": {"action": "block"},
"usb": {"action": "block"},
"print": {"action": "block"}
}
},
"endpoint": {
"usb": [
{
+58
View File
@@ -0,0 +1,58 @@
# MetaGPT AW Scout
`scripts/metagpt-aw-scout.sh` is the safe project wrapper for using the
MetaGPT-configured LLM provider with ActivityWatch-Russian tasks.
Default mode is direct LLM scout through `/home/igor/.metagpt/config2.yaml`.
This avoids MetaGPT Browser/Editor tools, which are too noisy for operational
checklists.
Optional MetaGPT team mode is still available:
```bash
METAGPT_AW_ENGINE=team scripts/metagpt-aw-scout.sh smoke
```
Do not use full `--implement` mode with the free Groq tier for this project. It
pulls too much context and usually exceeds TPM limits.
## Setup
Use a valid provider key outside git:
```bash
export GROQ_API_KEY="gsk_..."
```
The global wrapper `/home/igor/bin/metagpt-lab` can write the key into
`/home/igor/.metagpt/config2.yaml`. The scout script reads that config and does
not print secrets.
## Presets
```bash
scripts/metagpt-aw-scout.sh qa-rollback
scripts/metagpt-aw-scout.sh smoke
scripts/metagpt-aw-scout.sh grafana
scripts/metagpt-aw-scout.sh install-kit
scripts/metagpt-aw-scout.sh windows-i18n
```
Free-form task:
```bash
scripts/metagpt-aw-scout.sh "Review risk of changing aw-worktime-ui-bridge foreground cache"
```
Reports are saved under:
```text
.ai/metagpt/
```
## Current Rule
The LLM is only a scout. Codex/operator must verify every material claim against
local files, live services, Ansible output, Grafana, and ActivityWatch APIs
before changing production behavior. Use `METAGPT_AW_ENGINE=team` only for
experiments; direct mode is the operational default.
+74 -5
View File
@@ -11,8 +11,9 @@ systemctl status nginx --no-pager
nginx -t
curl -I -sS -H 'Host: dm.iri1968.dpdns.org' http://127.0.0.1/
curl -k -fsS -H 'Host: dm.iri1968.dpdns.org' https://127.0.0.1/healthz
curl -k -I -sS -H 'Host: dm.iri1968.dpdns.org' https://127.0.0.1/go/proxmox-gui
curl -k -H 'Host: dm.iri1968.dpdns.org' -fsS https://127.0.0.1/ | grep -F 'dm.iri1968.dpdns.org'
curl -k -I -sS -H 'Host: dm.iri1968.dpdns.org' https://127.0.0.1/ | head
curl -k -u "$(awk -F= '/^user=/{u=$2}/^password=/{p=$2}END{print u\":\"p}' /root/proxmox-web-gateway.credentials)" \
-H 'Host: dm.iri1968.dpdns.org' -fsS https://127.0.0.1/ | grep -F 'dm.iri1968.dpdns.org'
```
Playbook для повторного rollout:
@@ -22,6 +23,49 @@ ANSIBLE_HOST_KEY_CHECKING=False \
ansible-playbook -i ansible/inventory.ini ansible/deploy_proxmox_web_gateway.yml
```
### Public gateway через pfSense
Публичная схема:
```text
Internet -> dm.iri1968.dpdns.org -> pfSense WAN 178.178.98.83 -> NAT 80/443 -> nginx 10.10.10.2
```
Нормальное состояние:
- `https://dm.iri1968.dpdns.org/healthz` -> `200 ok` без auth;
- `https://dm.iri1968.dpdns.org/` без auth -> `401`;
- `http://dm.iri1968.dpdns.org/healthz` -> `301` на HTTPS;
- после Basic Auth:
- `/` -> gateway index;
- `/r/file1c/brief` -> 1C brief;
- `/r/grafana/api/health` -> Grafana health;
- `/r/aw/api/0/info` -> AW server info.
Gateway credential хранится только на `10.10.10.2`:
```sh
sudo cat /root/proxmox-web-gateway.credentials
```
pfSense NAT backup перед автоматической правкой:
```sh
ls -1t /opt/infra-admin/backups/pfsense-gateway-nat-*.json | head
```
Проверка pfSense REST/API и pending firewall changes:
```sh
set -a
. /home/igor/.config/tsj-bot/pfsense.env.readonly
set +a
curl -ksS -H "X-API-Key: $PFSENSE_API_KEY" "$PFSENSE_URL/api/v2/firewall/apply"
```
NAT должен содержать `WAN tcp 443 -> 10.10.10.2:443` и `WAN tcp 80 -> 10.10.10.2:80`.
WAN rules должны содержать pass на `10.10.10.2:80` и `10.10.10.2:443`.
### На Proxmox
```sh
@@ -61,16 +105,41 @@ ss -ltnp | grep 5600
```sh
/usr/local/bin/aw-health-check
/usr/local/bin/dlp-health-check --json
```
Что проверяет дополнительно:
- свежесть DLP bucket-ов (`aw-dlp-endpoint-signals_*`, `aw-file-operations_*`);
- наличие transport/self-test telemetry (`queueDepth`, `eventsEnqueued`, `eventsFlushed`, `sendFailures`) в endpoint self-test;
- последние значения transport-счетчиков по каждому `aw-dlp-endpoint-signals_*` bucket;
- runtime-срез `aw-file-operations_*`: последние `collector_health`, счетчики очереди/отправки, последние операции без полного пути;
- runtime-срез `aw-dlp-incidents_*`: по умолчанию только metadata/age без тяжёлого чтения events; при ручном `--incident-sample-limit > 0` — количество реальных incident events в sample, self-test count, severity/action/rule rollup и до 5 последних incidents с коротким `message_excerpt`;
- API-доступность базовых сервисов.
Интерпретация:
- `FAIL` — есть критичная проблема (service/API/stale transport);
- `WARN` — сигнал для оператора (например, bucket еще не активирован на хосте), но без hard-fail.
- `WARN` — сигнал для оператора (например, bucket еще не активирован на хосте, `queueDepth > 100`, новый рост `sendFailuresDelta >= 1`, нет `collector_health` в sample), но без hard-fail.
- `sendFailures` — накопительный счётчик collector-а; основной health-check предупреждает только по delta относительно сохранённого baseline в `AW_DLP_HEALTH_STATE_DIR`, чтобы старые восстановленные ошибки не висели вечным warning.
Пороги можно переопределить без изменения collector-ов:
```sh
/usr/local/bin/dlp-health-check --json \
--endpoint-queue-warn-depth 100 \
--endpoint-send-failure-warn-count 1 \
--incident-sample-limit 0 \
--fileops-sample-limit 20 \
--fileops-queue-warn-depth 100 \
--fileops-send-failure-warn-count 1
```
Для разового разбора последних DLP incidents можно включить sampling вручную:
```sh
/usr/local/bin/dlp-health-check --json --incident-sample-limit 20
```
Если AW-server медленно отдаёт большой `aw-dlp-incidents_*` bucket, такой запуск может вернуть `incident-runtime: WARN` по timeout. Это не должно использоваться как hard-fail основного мониторинга.
## Проверка RU patch
@@ -399,8 +468,8 @@ curl -fsS 'http://127.0.0.1:5600/api/0/buckets/aw-dlp-incidents_SHARKON2025/even
1) Проверить/обнулить очереди:
```powershell
$q1 = 'C:\ProgramData\AWatch-rus\file-operations-queue.jsonl'
$q2 = 'C:\ProgramData\AWatch-rus\dlp-endpoint-signals-queue.jsonl'
$q1 = 'C:\ProgramData\AWatch-rus\file-operations-queue*.jsonl'
$q2 = 'C:\ProgramData\AWatch-rus\dlp-endpoint-signals-queue*.jsonl'
Get-Item $q1,$q2 | Select Name,Length,LastWriteTime
```
+18
View File
@@ -16,6 +16,24 @@
| Deployment | Ansible + PowerShell ensemble + InnoSetup + Proxmox LXC |
| Linux | Remote worker, console/SSH logger, web category logger |
## Cross-OS enforcement strategy update
Новый целевой путь зафиксирован в `docs/dlp-cross-os-native-enforcement.md`: AWatch-rus
расширяется не тяжелым универсальным агентом, а нативными механизмами ОС и managed browser.
Приоритет платформ:
| Платформа | Легкий естественный путь | Enforcement-first канал |
|-----------|--------------------------|--------------------------|
| Windows | GPO/AppLocker/Spooler/Storage + текущие PowerShell collectors | USB write-block, print cancel |
| macOS | MDM + System Extensions/Endpoint Security, без kext | monitor-first, затем managed deny |
| Linux | fanotify + auditd, eBPF как telemetry/LSM extension | sensitive directory/removable gate |
| ChromeOS | Google Admin Data Controls | copy/paste, print, file transfer/upload |
| Managed browser | Edge/Chrome enterprise policy, extension только для inline upload | cloud upload policy |
Общее правило rollout: `audit` -> `warn` -> точечный `block`; глобальный `block` запрещен
до baseline и guard heartbeat.
## Ключевые разрывы до InfoWatch TM уровня
### 🔴 Критические (без них это не DLP, а мониторинг)
+20 -1
View File
@@ -6,18 +6,37 @@
## `aw-prune-local-state.sh`
Скрипт `aw-server/aw-prune-local-state.sh` устанавливается в:
Команда `aw-server/aw-prune-local-state.sh` устанавливается в:
```text
/usr/local/bin/aw-prune-local-state.sh
```
Сейчас это Rust-first wrapper. Если доступен
`/usr/local/bin/aw-prune-local-state-rust`, wrapper использует его; иначе
fallback идет на legacy shell script из
`/opt/activitywatch/aw-rus-ops/aw-prune-local-state.sh`.
Назначение:
- чистит старые backup-файлы в `{{ aw_server_data_dir }}/backups`;
- отдельно удерживает последние DB backups и JSON backups;
- удаляет временные архивы из `/tmp`: `activitywatch-*.zip`, `hayabusa-*.zip`, `aw-hayabusa-profiles.txt`;
- удаляет временные WebUI/worktime artifacts старше одного дня: `aw-worktime-ui-bridge.py`, `views-default.json`, `apply_webui_ru_patch.out`.
- чистит старые `browser-smoke` run-директории в
`{{ aw_server_data_dir }}/browser-smoke`, удерживая последние запуски.
Rust binary по умолчанию работает в dry-run режиме. Production wrapper без
аргументов запускает его с `--apply`, чтобы сохранить поведение systemd timer.
Safety policy:
- не удалять основную ActivityWatch SQLite DB;
- не удалять rollback-critical backups: `switch-backups`, `before-rust`,
`rollback`;
- не удалять корневые state-каталоги;
- удалять только пути из явного allowlist: `backups`, `backups/db`,
`browser-smoke`, ограниченный набор временных файлов в `/tmp`.
## systemd unit и timer
+3 -3
View File
@@ -18,10 +18,10 @@
## Process events
Новый флаг:
Флаг:
```yaml
aw_windows_process_events_enabled: true
aw_windows_process_events_enabled: false
```
Он попадает в deployment config как:
@@ -30,7 +30,7 @@ aw_windows_process_events_enabled: true
sessionEvents.processEventsEnabled
```
Когда флаг включен, Windows collector публикует process-level изменения в session events bucket. Это дает server-side слою больше контекста для active session detection и forensic review.
По умолчанию флаг выключен во всех путях deploy/recovery: постоянная публикация process-level изменений в `aw-session-events_<host>` создает чрезмерный поток событий и быстро раздувает SQLite на AW server. Включать его следует только явно и временно для forensic/debug окна, после чего возвращать `false`.
## Localized Administrator
+15 -1
View File
@@ -6,6 +6,12 @@
Use this model on `SHARKON2025`-style hosts with multiple user sessions.
- `AWatchRusCollectorGuard` service:
- runs under `LocalSystem`
- is the preferred local control plane for collector supervision
- in `shadow` mode only publishes state/heartbeat
- in `enforce` mode starts only session-appropriate collectors/tasks with cooldown and restart budget
- publishes `aw-rus-collector-guard_<HOST>`
- `ActivityWatch Launch [HOST_user]` tasks:
- `AtLogOn`
- `InteractiveToken`
@@ -13,8 +19,9 @@ Use this model on `SHARKON2025`-style hosts with multiple user sessions.
- `ActivityWatch Recovery` task:
- `AtStartup`
- `SYSTEM`
- stays enabled even when `AWatchRusCollectorGuard` is active
- keeps only the global `worktime-session-collector` alive
- may re-trigger user launch tasks, but only for users whose sessions currently exist
- may re-trigger user launch tasks for managed live or disconnected sessions
- interactive collectors/watcher binaries belong to the user-session path, not to Session 0
Collector ownership in this model:
@@ -53,10 +60,17 @@ Do not mix the two startup models on the same RDP host:
- no permanent standalone-service loop together with per-user launch/recovery tasks
- no blind `Start-ScheduledTask` for all configured users
- no validation rule that treats users without sessions as failed collector startup
- no bot-driven collector recovery as the primary control plane
During migration, `AWatchRusCollectorGuard` may run in `shadow` mode beside the existing
recovery task. In `enforce` mode it becomes the primary control plane; `ActivityWatch Recovery`
still remains enabled as fallback/bootstrap and must not be disabled by deploy scripts.
## Hardening rules
- start launch tasks only for users with real sessions
- treat managed disconnected RDP sessions as real recovery targets when their launch tasks exist
- keep only one global `worktime-session-collector`
- validate by session-aware expectations, not by “all configured users must currently run”
- use guard heartbeat and bucket freshness as health signals
- keep `deploy_aw_windows.yml`, `deploy-ensemble.ps1`, `hardening-recovery.ps1`, and installer assumptions aligned