MAATRIX / Блог / ActivityWatch на своём сервере: учёт времени, который бьёт по доверию

ActivityWatch на своём сервере: учёт времени, который бьёт по доверию

MAATRIX

Коммерческие трекеры рабочего времени с обязательными скриншотами каждые несколько минут и учётом нажатий клавиш почти всегда рано или поздно превращаются в источник конфликта: сотрудники начинают либо саботировать программу, либо имитировать активность вместо работы. ActivityWatch — честная альтернатива: открытый код, никаких скриншотов и передачи данных стороннему облаку, вся история активности физически лежит там, где вы сами решили её хранить. Но перенос данных на свой сервер снимает вопрос «кому ещё это видно», а не вопрос «зачем мы вообще это измеряем» — и если не продумать второй вопрос заранее, технически безупречное решение всё равно разрушит доверие в команде.

Что такое ActivityWatch и чем он отличается от шпионских трекеров

ActivityWatch — open-source система учёта активности за компьютером, которая работает по принципу local-first: данные сначала собираются и хранятся локально на машине пользователя, а не улетают сразу в чужой облачный сервис. Состоит из нескольких компонентов:

  • aw-server — локальный сервер с REST API и веб-интерфейсом, хранит события в базе на диске и отдаёт их по запросу (обычно слушает localhost:5600).
  • aw-watcher-afk — отслеживает, был ли пользователь «за клавиатурой» (any-from-keyboard) — движение мыши, нажатия клавиш, без записи их содержимого.
  • aw-watcher-window — фиксирует, какое окно активно и его заголовок (например, «Terminal — ssh user@server» или «Gmail — Входящие»), а не содержимое экрана.
  • aw-watcher-web (опционально) — расширение для браузера, которое подставляет реальный URL и заголовок вкладки вместо обобщённого «Chrome».

Принципиальные отличия от Hubstaff, Time Doctor и подобных сервисов:

ActivityWatchТипичный «шпионский» трекер
Скриншоты экранаНетЧасто, каждые N минут
Запись нажатий клавиш (кейлоггер)Нет, только факт активностиИногда да
КодОткрытый, можно проверить самомуЗакрытый
Где хранятся данныеЛокально, вы решаетеОблако вендора
Можно приостановить/исключить приложениеДа, штатноОбычно нет или платно
Управление со стороны сотрудникаПолное — свои данные видны ему в реальном времениОбычно нет

Это не игрушка для энтузиастов приватности — на ActivityWatch реально удобно смотреть собственную статистику: сколько времени ушло на код, сколько на переписку, где произошла незаметная утечка полутора часов в браузер. Вопрос в том, что происходит, когда эти данные начинают собирать не для себя, а для команды.

Локальная модель по умолчанию — и почему она не подходит команде «из коробки»

ActivityWatch спроектирован как инструмент для одного человека: watcher-ы шлют события в aw-server, который крутится на той же машине, а веб-интерфейс localhost:5600 открывает сам пользователь в браузере. Никакого «центрального сервера для команды» из коробки нет — это осознанное архитектурное решение авторов проекта в пользу приватности.

Если вам нужна командная картина (например, для биллинга клиентских часов на аутсорсе, а не для слежки), есть два рабочих подхода:

  1. Централизованный aw-server, к которому watcher-ы каждого сотрудника подключаются по сети вместо localhost. Технически возможно — сервер принимает соединения не только с loopback, если это указать явно, — но так теряется главное преимущество local-first: сырые данные (заголовки окон, вкладки браузера) сразу утекают на общий сервер, а не остаются под контролем автора данных.
  2. Периодическая выгрузка через REST API: каждый watcher продолжает писать в свой локальный aw-server, а отдельный скрипт по расписанию забирает агрегированные данные (не сырые события, а уже посчитанные категории и суммы) с эндпоинта /api/0/buckets через VPN и складывает их в общую базу на вашем сервере.

Второй вариант честнее по отношению к сотрудникам — сырые заголовки окон остаются у них, а команде передаётся уже осмысленная сводка. Дальше в статье разворачиваем именно эту схему, но приведём и первую — если вы уверены, что вам действительно нужны сырые данные (например, для полного аудита рабочей станции по требованию клиента), настройка отличается на пару параметров.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

Установка центрального сервера

Разворачиваем aw-server-rust (текущая рекомендуемая реализация сервера, переписанная с Python на Rust) на отдельном VPS. Скачиваем актуальный релиз со страницы GitHub-релизов проекта ActivityWatch — фиксированную версию тут указывать не будем, берите последнюю стабильную на момент установки:

sudo useradd -r -m -d /opt/activitywatch -s /usr/sbin/nologin aw
cd /opt/activitywatch
sudo -u aw wget https://github.com/ActivityWatch/activitywatch/releases/latest/download/activitywatch-linux-x86_64.zip
sudo -u aw unzip activitywatch-linux-x86_64.zip

Сервер запускаем не через штатный aw-qt (трей-приложение, оно рассчитано на десктоп), а напрямую бинарником aw-server из распакованного архива, привязав его к адресу, доступному по VPN, а не к публичному интерфейсу:

# /etc/systemd/system/aw-server.service
[Unit]
Description=ActivityWatch server
After=network.target

[Service]
Type=simple
User=aw
WorkingDirectory=/opt/activitywatch
ExecStart=/opt/activitywatch/aw-server --host 10.20.0.1 --port 5600 --storage sqlite
Restart=on-failure

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now aw-server

Обратите внимание на --host 10.20.0.1 — это адрес сервера внутри WireGuard-сети, а не публичный IP. Если вам нужна настройка самого VPN для команды с нуля, это отдельная задача — можно опереться на типовую схему VPN для удалённой команды. Публиковать aw-server напрямую в интернет не стоит: aw-server-rust не задумывался как многопользовательский сервис с полноценной аутентификацией, и открытый порт 5600 без VPN — прямой путь слить историю активности всей команды случайному сканеру портов.

Если всё же нужен доступ снаружи VPN (например, для просмотра дашборда через браузер без поднятого туннеля), закройте сервер через nginx с базовой аутентификацией и TLS:

server {
    listen 443 ssl;
    server_name aw.example.com;

    ssl_certificate     /etc/letsencrypt/live/aw.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/aw.example.com/privkey.pem;

    auth_basic "ActivityWatch";
    auth_basic_user_file /etc/nginx/.htpasswd_aw;

    location / {
        proxy_pass http://10.20.0.1:5600;
        proxy_set_header Host $host;
    }
}

Настройка клиентов и сбор данных без утечки сырых заголовков

На рабочих машинах сотрудников ставим обычный ActivityWatch (официальные сборки под Windows/macOS/Linux) — он продолжает писать в свой локальный aw-server, ничего не меняется в повседневной работе. Задача — регулярно и предсказуемо выгружать уже агрегированные данные на общий сервер, не трогая сырые события.

Скрипт агрегации, который каждый сотрудник запускает у себя по cron (или планировщику Windows) раз в день, суммирует активные часы по категориям и отправляет только итог:

#!/usr/bin/env bash
# aw-daily-summary.sh — считает суммарное время по категориям за сутки
# и отправляет агрегат на командный сервер. Сырые события никуда не уходят.

DAY=$(date -d "yesterday" +%Y-%m-%d)
BUCKET=$(curl -s http://localhost:5600/api/0/buckets/ | jq -r 'keys[] | select(contains("aw-watcher-window"))')

curl -s "http://localhost:5600/api/0/buckets/${BUCKET}/events?start=${DAY}T00:00:00&end=${DAY}T23:59:59" \
  | jq '[.[] | {app: .data.app, duration: .duration}] | group_by(.app) | map({app: .[0].app, total: (map(.duration) | add)})' \
  > /tmp/aw-summary-${DAY}.json

curl -s -X POST https://aw.example.com/team-summary \
  -H "Authorization: Bearer ${AW_TEAM_TOKEN}" \
  -H "Content-Type: application/json" \
  --data @/tmp/aw-summary-${DAY}.json

Приёмная сторона на сервере — минимальный обработчик, который складывает сводки в базу (SQLite или Postgres — на объёме в десятки сотрудников хватит с запасом любого из вариантов) для дальнейшей визуализации. Здесь намеренно не приводим готовый бэкенд под приём — под конкретный стек (Flask/FastAPI/Node) он собирается за пару часов, а важнее архитектурное решение: сервер получает {app: "VS Code", total: 14400}, а не {title: "gmail.com — Переписка с юристом по разводу"}. Обобщение до уровня приложения — не техническая мелочь, а сознательная граница между «мы считаем часы» и «мы читаем, чем вы заняты».

Технически прозрачно — не значит психологически нейтрально

Здесь важно назвать вещи своими именами. Всё, что описано выше — открытый код, локальное хранение, агрегация вместо сырых логов, — снимает технические риски: утечку данных, зависимость от вендора, скрытый сбор лишнего. Но ни один из этих пунктов не решает организационную проблему, из-за которой люди вообще боятся систем учёта времени: ощущение, что их измеряют, а не доверяют им.

Прозрачность архитектуры и прозрачность намерений — разные вещи. Сотрудник может прочитать исходный код, увидеть, что скриншотов нет, и всё равно чувствовать себя под наблюдением — потому что вопрос не «что технически собирается», а «что с этим будут делать дальше». Если руководитель открывает дашборд раз в неделю и молча делает выводы о том, кто «мало активен», доверие ломается независимо от того, self-hosted это решение или облачное с кейлоггером. Разница только в качестве собираемых данных, а не в эффекте от их использования.

Где именно ломается доверие: три типичных сценария

Сценарий первый — «активное время» как метрика продуктивности. ActivityWatch честно показывает, что человек час не двигал мышью. Но это может быть чтение документации, звонок без камеры, обдумывание архитектуры перед кодом — то есть настоящая работа, которая не оставляет цифрового следа. Если менеджер трактует AFK-время как «час прогула», система начинает наказывать за размышление и поощрять механическое дёргание мышью. Люди быстро это считывают и начинают оптимизировать под метрику, а не под результат — держат курсор в движении, открывают лишние окна, чтобы заголовок менялся.

Сценарий второй — сравнение людей друг с другом. Данные ActivityWatch честны в рамках одного человека и его собственной динамики, но бессмысленны для сравнения между разными ролями. У разработчика в статистике будет много времени в IDE и терминале, у менеджера — в почте и календаре, у дизайнера — в графическом редакторе с паузами на разглядывание референсов. Таблица «кто активнее» на основе этих цифр меряет разные профессии одной линейкой и демотивирует тех, чья работа по природе менее «кликабельна».

Сценарий третий — тихое внедрение без объяснения цели. Даже если технически всё сделано аккуратно, сам факт установки трекера без предварительного разговора воспринимается как знак недоверия. Формулировка «мы же ничего плохого не делаем, зачем возражать» не работает: люди реагируют не на то, что происходит технически, а на то, что решение о наблюдении за ними приняли без них.

Как внедрить, чтобы не сломать то, ради чего вроде бы всё затевалось

Если цель — не слежка, а реальная задача (биллинг часов по клиентским проектам, собственная аналитика для самого сотрудника, оценка нагрузки команды в целом, а не по именам), несколько принципов снижают риск конфликта почти до нуля:

  • Назовите цель вслух и зафиксируйте её письменно. «Мы считаем часы по проекту X для выставления счёта клиенту» — это конкретно и проверяемо. «Чтобы видеть, кто как работает» — расплывчато и читается как повод для будущих придирок.
  • Данные по умолчанию принадлежат тому, кто их создал. Сырые события (заголовки окон, посещённые сайты) не должны покидать машину сотрудника без явного согласия на каждый конкретный случай выгрузки. Агрегированные суммы по категориям — можно, конкретные заголовки — только если для этого есть отдельно объяснённая причина.
  • Никаких индивидуальных сравнений в общих отчётах. Если нужна командная сводка — считайте её по проекту или по неделе целиком, а не строкой «Иванов — 32 часа, Петров — 41 час» в общем канале.
  • Дайте право приостановить и исключить. У ActivityWatch штатно есть возможность держать watcher выключенным вручную и настраивать список исключённых приложений (личный банк-клиент, мессенджер с семьёй) — сделайте это явной опцией, а не тем, что нужно выяснять из документации самостоятельно.
  • Обсудите с командой до, а не после установки. Разговор «мы хотим считать часы по такой-то причине, вот как это устроено технически, вот что вы можете отключить» занимает пятнадцать минут и снимает девять десятых будущих претензий. Если данные сотрудников подпадают под требования к персональным данным (а рабочая активность, привязанная к конкретному человеку, обычно подпадает), стоит свериться с тем, как вообще организована защита персональных данных сотрудников на вашем сервере — это не только вопрос этики, но и формальных обязательств.
  • Пересматривайте, если система перестаёт быть добровольной по факту. Формальное «можете отключить» не работает, если отключивший автоматически попадает под подозрение. Если так происходит — механизм согласия сломан, и это стоит признать, а не защищать задним числом.

Если вы уже используете общие инструменты для команды (пароли, VPN-доступы, инфраструктуру), внедрение трекера — повод для такого же честного разговора, что и при отказе от пересылки паролей в чат: речь не про недоверие к людям, а про снижение рисков системы в целом. Формулировка «это не про контроль, а про защиту и учёт» работает только тогда, когда она правдива и подкреплена реальными техническими ограничениями, а не декларацией.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

ActivityWatch видит содержимое экрана или переписки?

Нет. Он фиксирует факт активности (было ли движение мыши/клавиатуры) и метаданные — активное приложение и заголовок окна. Содержимого документов, сообщений, паролей он не читает и не может прочитать по архитектуре.

Можно ли использовать ActivityWatch без централизованного сервера, только для себя?

Да, и это основной сценарий использования — большинство пользователей вообще не выгружают данные никуда, они смотрят собственную статистику через локальный веб-интерфейс на localhost:5600.

Что делать, если сотрудник просто выключит watcher на время «неудобных» задач?

Технически это возможно — и это нормально, если система добровольная. Если для вас критично, чтобы трекинг было нельзя выключить, вы уже не про добровольный учёт, а про контроль, и стоит явно это признать и обсудить отдельно, а не решать через технические ограничения.

Нужен ли VPN для централизованного сервера обязательно?

Настоятельно рекомендуется. aw-server не проектировался как многопользовательский сервис с продуманной аутентификацией по умолчанию, поэтому открытый в интернет порт — риск утечки истории активности всей команды.

ActivityWatch подходит для биллинга часов фрилансеров/подрядчиков?

Да, это один из практичных сценариев — категории по приложениям и проектам дают достаточно точную картину для выставления счёта, не требуя скриншотов и не создавая ощущения слежки, если оговорить это заранее как часть договорённости о работе.

Чем это лучше готового облачного тайм-трекера вроде Toggl?

Данные остаются у вас физически, нет ежемесячной платы за место, легче кастомизировать под свои категории и правила. Минус — Toggl из коробки даёт готовую командную панель, а с ActivityWatch агрегацию для команды придётся собирать самостоятельно, как показано выше.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →