Идея держать собственные заметки на чужих серверах постепенно перестаёт вызывать доверие у всё большего числа людей. Причин масса - от утечек данных у крупных сервисов до банальных случаев блокировки аккаунтов за непонятные нарушения. Приватные мысли, рабочие заметки, идеи для проектов, черновики писем - всё это оказывается заложником чужой инфраструктуры. Standard Notes предлагает любопытный компромисс между удобством современных приложений и полным контролем над данными. Приложение открытое, данные шифруются на клиенте, а сервер можно поднять на собственной машине за час работы.
Особенности Standard Notes и зачем вообще поднимать собственный сервер вместо использования облачного варианта
Standard Notes устроен по принципу сквозного шифрования. Весь текст заметок зашифровывается прямо в приложении перед отправкой на сервер. Даже если кто-то получит доступ к серверу, он увидит только бесполезные зашифрованные блоки без возможности прочитать содержимое. Ключ шифрования никогда не покидает устройство пользователя, его не знает сам сервер, не знают разработчики приложения, и при потере пароля восстановить данные невозможно в принципе. Это не баг, а фича - ценой неудобства при восстановлении получается настоящая конфиденциальность.
Официальный облачный сервис Standard Notes работает именно по такой схеме, и многим этого достаточно. Бесплатный тариф покрывает базовую синхронизацию между устройствами, платная подписка добавляет расширенные форматы, историю версий, загрузку файлов и другие возможности. Но при всём этом остаётся один концептуальный изъян - данные всё равно хранятся на чужих серверах. Даже зашифрованные. Даже у честной компании. Даже без возможности их прочитать. Философски это чувствуется по-разному, и часть пользователей предпочитает держать всё под собственным контролем.
Самостоятельное размещение решает эту дилемму. Тот же клиент, тот же протокол синхронизации, те же настольные и мобильные приложения - но сервер стоит в доверенной инфраструктуре. Это может быть домашний VPS за пять долларов в месяц, корпоративный сервер, виртуалка на чужом хостинге или даже Raspberry Pi дома под столом. Настройка одинакова, возможности одинаковы, разница только в том, кто физически контролирует железо.
Процесс развёртывания построен на связке Docker и Docker Compose. Такой подход оправдан - Standard Notes состоит из нескольких взаимодействующих сервисов (API-шлюз, аутентификация, синхронизация, файловое хранилище, база данных, кеш), и держать их всех в отдельных контейнерах намного проще, чем разворачивать вручную. Один скрипт управления запускает весь зоопарк разом, один скрипт останавливает, один показывает состояние.
Подготовка брандмауэра и открытие необходимых портов для работы веб-приложения
Перед любой серьёзной установкой надо убедиться, что система готова. Обновление пакетов - первый шаг.
$ sudo apt update && sudo apt upgrade
Standard Notes работает через веб-интерфейс, а значит, нужны открытые порты для HTTP и HTTPS. Проверяем текущее состояние файрвола.
$ sudo ufw status
На свежей системе обычно открыт только SSH - и правильно, всё остальное будет добавляться осознанно.
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
Открываем стандартные веб-порты.
$ sudo ufw allow http
$ sudo ufw allow https
Использование имён http и https вместо номеров 80 и 443 делает правила более читаемыми и защищает от опечаток. Опечатка в имени службы вызовет сразу ошибку, опечатка в номере порта молча откроет не тот порт, что создаст потенциальную проблему безопасности.
Повторная проверка статуса для подтверждения изменений.
$ sudo ufw status
В ответе должны появиться записи для 80 и 443 портов с разрешением для любых источников.
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
80/tcp ALLOW Anywhere
443/tcp ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
80/tcp (v6) ALLOW Anywhere (v6)
443/tcp (v6) ALLOW Anywhere (v6)
Минимальные требования к серверу - 2 ГБ оперативной памяти. Это связано с характером работы Docker-стека, где несколько контейнеров с базой данных, кешем и приложениями делят ресурсы одной машины. На виртуалке с 1 ГБ всё тоже запустится, но будет страдать от нехватки памяти и периодически подвисать.
Установка Docker и Docker Compose из официального репозитория разработчика
Стандартные репозитории Ubuntu содержат Docker, но обычно отстающей версии. Для продакшена лучше брать свежий прямо у разработчика. Импортируем GPG-ключ для проверки подписей.
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker.gpg
Добавляем сам репозиторий в список источников пакетов.
$ echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Конструкция с подстановкой архитектуры и кодового имени релиза через lsb_release делает команду универсальной. Она одинаково работает на Jammy, Focal и Bionic - система сама подставит правильные значения. Это удобно при написании скриптов развёртывания, работающих на разных версиях Ubuntu.
Обновляем списки пакетов с учётом нового репозитория.
$ sudo apt update
Ставим полный комплект компонентов Docker.
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
Каждый из четырёх пакетов решает свою задачу. Docker-ce - сам движок контейнеризации, центральный компонент всей системы. Docker-ce-cli - утилита командной строки для взаимодействия с движком. Containerd.io - низкоуровневая среда исполнения контейнеров, которую использует Docker внутри. Docker-compose-plugin - плагин для работы с compose-файлами, причём именно в новом стиле через docker compose без дефиса.
Добавление пользователя в группу docker избавляет от необходимости писать sudo перед каждой командой.
$ sudo usermod -aG docker ${USER}
Применение новых прав группы требует либо перелогина, либо команды su для обновления контекста.
$ su - $(USER)
Установка свежего Nginx из репозитория разработчика для роли обратного прокси
Nginx в роли обратного прокси перед Docker-контейнерами - классическое решение. Он обрабатывает HTTPS, отдаёт статику эффективнее, чем Node.js бэкенды, защищает контейнеры от прямого доступа из интернета. Свежий Nginx предпочтительнее сборки из стандартных репозиториев.
Импортируем ключ подписи.
$ curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
Добавляем репозиторий.
$ echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg arch=amd64] \
http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
Обновляем пакеты и ставим Nginx.
$ sudo apt update
$ sudo apt install nginx
Проверяем версию - она должна быть заметно свежее того, что идёт в стандартных репозиториях Ubuntu.
$ nginx -v
nginx version: nginx/1.22.0
Получение SSL-сертификатов Let's Encrypt для основного домена и субдомена файлового сервера
Без HTTPS современный веб-сервис разворачивать попросту неприлично. Передача паролей по открытому каналу, перехват трафика, уязвимости к атакам посредника - всё это ушло в прошлое вместе с появлением бесплатных сертификатов от Let's Encrypt. Установка Certbot через Snap даёт самую свежую версию.
$ sudo snap install core
Сам Certbot.
$ sudo snap install --classic certbot
Символическая ссылка для удобства использования.
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot
Получаем сертификат для основного домена приложения.
$ sudo certbot certonly --standalone --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. -d standardnotes.example.com
Разбор параметров полезен для понимания. Параметр --standalone означает, что Certbot сам запустит встроенный веб-сервер для прохождения проверки владения доменом. Это требует свободного 80-го порта на момент генерации. Параметр --agree-tos автоматически соглашается с условиями использования, без него процесс остановится и будет ждать интерактивного подтверждения. Флаг --no-eff-email отказывается от подписки на рассылку Electronic Frontier Foundation. Параметр --staple-ocsp включает OCSP stapling для ускорения проверки сертификата браузерами. Почта после -m используется для уведомлений об истекающих сертификатах.
Такая же процедура для субдомена файлового сервера. Standard Notes хранит файлы (если активирована соответствующая платная функция) отдельно от основного приложения, и для них нужен свой сертификат.
$ sudo certbot certonly --standalone --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. -d snotes-files.example.com
Диффи-Хеллман параметры для обмена ключами в TLS-соединении.
$ sudo openssl dhparam -dsaparam -out /etc/ssl/certs/dhparam.pem 4096
Генерация на слабом сервере займёт несколько минут. Процесс нагружает процессор, и ускорить его можно только более мощной машиной. Ключ в 4096 бит - компромисс между безопасностью и производительностью, годный на долгие годы вперёд.
Для автообновления сертификатов нужны хуки, которые останавливают и запускают Nginx перед проверкой. Это необходимо потому, что Certbot с --standalone требует свободного 80-го порта.
$ sudo nano /etc/letsencrypt/renewal/standardnotes.example.com.conf
В конец файла добавляется следующий блок.
pre_hook = systemctl stop nginx
post_hook = systemctl start nginx
Та же процедура повторяется для файла /etc/letsencrypt/renewal/snotes-files.example.com.conf. После настройки хуков имеет смысл прогнать сценарий обновления вхолостую - эта операция проверит работоспособность схемы без реального получения сертификата.
$ sudo certbot renew --dry-run
Отсутствие ошибок - зелёный свет для спокойной работы. Сертификаты будут обновляться автоматически дважды в месяц без какого-либо вмешательства администратора.
Клонирование репозитория Standard Notes и генерация секретных ключей для конфигурации
Переход в домашнюю директорию.
$ cd ~
Клонирование официального репозитория сборки сервера для самостоятельного размещения.
$ git clone --single-branch --branch main https://github.com/standardnotes/standalone.git
Флаг --single-branch с указанием ветки main оптимизирует клонирование - скачивается только нужная ветка без всей истории других. Для проекта средних размеров это ощутимо сокращает время и трафик.
Вход в директорию проекта.
$ cd standalone
Генерация стартовых конфигурационных файлов через встроенный скрипт установки.
$ ./server.sh setup
Скрипт создаёт шаблонные файлы окружения, которые теперь надо заполнить. Шесть разных секретных ключей нужны для работы различных компонентов системы - аутентификации, шифрования, подписи токенов. Каждый генерируется отдельной командой openssl.
$ openssl rand -hex 32
Команду нужно запустить шесть раз подряд, получая каждый раз новую случайную 64-символьную шестнадцатеричную строку. Все они разные, и использовать один ключ для нескольких параметров категорически нельзя - это подрывает всю модель безопасности. Значения лучше сразу сохранять в текстовый файл, чтобы потом не потеряться.
Редактируем основной файл окружения.
$ nano .env
Внутри меняются ключевые параметры.
NODE_ENV=production
..
AUTH_JWT_SECRET=c0f5bcf6f0f0dcca5b9078c3095e4255a055dfd6376b376733af0e50483cc629
..
DB_USERNAME=std_notes_user
DB_PASSWORD=changeme123
..
VALET_TOKEN_SECRET=977c978ca1d5ea22fe2fda65058905b191f724e33db6e47d0a41e034a082cb3b
..
FILES_SERVER_URL=https://snotes-files.example.com
Переменная NODE_ENV в значении production включает оптимизации Node.js, отключает лишнее логирование и режим отладки. Для развёртывания в рабочей среде этот флаг обязателен. AUTH_JWT_SECRET - секрет для подписи токенов аутентификации. Пароль базы данных changeme123 явно нужно заменить на что-то серьёзное - это один из тех примеров из документации, которые никогда не стоит оставлять в продакшене. VALET_TOKEN_SECRET используется для работы токенов доступа к файловому серверу. FILES_SERVER_URL указывает полный адрес субдомена для работы с файлами.
Второй конфигурационный файл находится в поддиректории docker.
$ nano docker/auth.env
Здесь тоже меняются ключи.
JWT_SECRET=54deb1b0b2499e8d875b0d5266dabef9003e13c1787a959a94e339363c10e56e
LEGACY_JWT_SECRET=c36aae01803a616213f22422b6d3f998a2beb2cb53af8b95bf578a8a3d046cec
..
PSEUDO_KEY_PARAMS_KEY=ea09d3f9122b49c653524cd2285a45fee88beb94f9b76d4d25420b521b080fcd
..
ENCRYPTION_SERVER_KEY=04decf379fbe3bb48cf95dbb5997031418b308e724a25d88cb0b2ed6da725efe
Назначение каждого ключа описано в документации проекта, но для базового развёртывания достаточно знать главное - все значения должны быть уникальными 64-символьными шестнадцатеричными строками, полученными через openssl rand.
Запуск всего стека сервисов и проверка их здоровья через скрипт управления
После настройки конфигов запуск сводится к одной команде.
$ ./server.sh start
Процесс занимает несколько минут при первом запуске. Docker скачивает все нужные образы (их несколько - для каждого сервиса свой), создаёт контейнеры, запускает базу данных, выполняет миграции, стартует приложения в правильном порядке. Зрелище не быстрое, но и не требующее участия.
Просмотр логов помогает следить за процессом в реальном времени.
$ ./server.sh logs
Ctrl+C прерывает просмотр логов, но не останавливает сами контейнеры. Текущее состояние всех сервисов показывается отдельной командой.
$ ./server.sh status
Ответ выводит подробную таблицу.
Services State:
NAME COMMAND SERVICE STATUS PORTS
api-gateway-standalone "./wait-for.sh auth …" api-gateway running 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp
auth-standalone "./wait-for.sh db 33…" auth running
auth-worker-standalone "./wait-for.sh db 33…" auth-worker running
cache-standalone "docker-entrypoint.s…" cache running 6379/tcp
db-standalone "docker-entrypoint.s…" db running 3306/tcp
files-standalone "./wait-for.sh db 33…" files running 0.0.0.0:3125->3000/tcp, :::3125->3000/tcp
syncing-server-js-standalone "./wait-for.sh db 33…" syncing-server-js running
syncing-server-js-worker-standalone "./wait-for.sh db 33…" syncing-server-js-worker running
Все сервисы должны быть в состоянии running. Любой другой статус - повод разбираться, что пошло не так. Чаще всего проблемы связаны с неправильно заполненными переменными окружения или недостатком ресурсов на сервере.
Проверка работоспособности через health-check эндпоинт.
$ curl http://localhost:3000/healthcheck
OK
Ответ OK подтверждает, что API-шлюз работает и отвечает на запросы.
Конфигурация Nginx для проксирования запросов к основному приложению и файловому серверу
Главный конфигурационный файл Nginx требует небольшой правки для корректной работы с длинными доменными именами.
$ sudo nano /etc/nginx/nginx.conf
Перед строкой включения дополнительных конфигов добавляется параметр.
server_names_hash_bucket_size 64;
Значение 64 байта достаточно для большинства доменов. Если используются длинные имена, можно увеличить. Дальше создаётся основной конфиг виртуального хоста.
$ sudo nano /etc/nginx/conf.d/standardnotes.conf
Содержимое получается объёмным, но каждая строка важна.
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name standardnotes.example.com;
client_max_body_size 50M;
access_log /var/log/nginx/standardnotes.access.log;
error_log /var/log/nginx/standardnotes.error.log;
ssl_certificate /etc/letsencrypt/live/standardnotes.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/standardnotes.example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/standardnotes.example.com/chain.pem;
ssl_session_timeout 5m;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
ssl_stapling on;
ssl_stapling_verify on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_cache off;
}
}
# enforce HTTPS
server {
listen 80;
listen [::]:80;
server_name standardnotes.example.com;
return 301 https://$host$request_uri;
}
Блок с HTTP/2 в параметрах listen включает поддержку более быстрого протокола. Client_max_body_size 50M определяет максимальный размер загружаемых файлов. Блок настроек SSL использует современные параметры - только TLSv1.2 и TLSv1.3, отключены устаревшие и уязвимые протоколы. Шифрсьюты тщательно подобраны по безопасности и совместимости. OCSP stapling ускоряет проверку сертификата браузером. Параметры dhparam указывают на сгенерированный ранее файл.
Секция location / просто проксирует все запросы на Docker-контейнер API-шлюза, слушающий порт 3000 на локальном интерфейсе. Отключение кеша через proxy_cache off принципиально - кеширование API-ответов привело бы к неправильной работе приложения.
Второй блок с перенаправлением с HTTP на HTTPS гарантирует, что никто не будет работать с приложением по незащищённому каналу.
Отдельный конфиг для файлового сервера.
$ sudo nano /etc/nginx/conf.d/files-standardnotes.conf
Содержимое практически идентично, меняются только имя домена, путь к сертификату и порт бэкенда.
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name snotes-files.example.com;
client_max_body_size 50M;
access_log /var/log/nginx/files-standardnotes.access.log;
error_log /var/log/nginx/files-standardnotes.error.log;
ssl_certificate /etc/letsencrypt/live/snotes-files.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/snotes-files.example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/snotes-files.example.com/chain.pem;
ssl_session_timeout 5m;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
ssl_stapling on;
ssl_stapling_verify on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
location / {
proxy_pass http://127.0.0.1:3125;
proxy_cache off;
}
}
# enforce HTTPS
server {
listen 80;
listen [::]:80;
server_name snotes-files.example.com;
return 301 https://$host$request_uri;
}
Порт 3125 - именно на нём внутри Docker работает сервис файлов. Проверка синтаксиса перед перезапуском - обязательный ритуал.
$ sudo nginx -t
Применение изменений.
$ sudo systemctl restart nginx
Подключение приложения к собственному серверу и активация платных функций
После успешного запуска открытие домена в браузере должно показать приветственное сообщение от Standard Notes. Это значит, что сервер работает и готов к подключениям. Но вводить заметки прямо через этот адрес нельзя - нужен клиент.
Официальное веб-приложение находится по адресу app.standardnotes.com. При создании аккаунта нужно раскрыть расширенные настройки, снять галку с варианта по умолчанию и указать адрес собственного сервера. После этого все данные пойдут на личный сервер, а не на официальный.
Платные функции в самостоятельно размещённом варианте активируются особым образом. Поскольку платёжная система работает только с облачным сервисом, для локальной установки есть специальная команда.
$ cd ~/standardnotes
$ bash ./server.sh create-subscription Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.
Команда искусственно создаёт подписку для указанного аккаунта, открывая доступ ко всем расширенным возможностям. После выполнения нужно перезагрузить веб-приложение в браузере - и все платные функции окажутся активными.
Стоит помнить, что такой подход работает исключительно в рамках собственной установки. Пользоваться чужими серверами без оплаты категорически нельзя - это прямое нарушение условий лицензии. Для самостоятельного размещения же такая опция предусмотрена самими разработчиками и не вызывает вопросов. Желающие поддержать проект могут отправить добровольное пожертвование, что многие и делают.
Настройка загрузки файлов и установка квот через SQL-запросы к базе данных
Загрузка файлов - одна из платных функций, и для её работы нужно несколько дополнительных действий. Сначала правильные права на директорию для файлов.
$ chmod -R 775 data
$ mkdir -p data/uploads
$ sudo chmod -R 755 data/uploads
$ sudo chown -R 1001.1001 data/uploads
Числа 1001.1001 - это UID и GID пользователя, под которым работает сервис файлов внутри контейнера. Несоответствие этих идентификаторов приведёт к ошибкам записи файлов.
Стандартная квота для пользователей в базовой настройке равна нулю, то есть загрузка запрещена. Для активации нужно вручную прописать квоту через SQL-запрос к базе данных внутри Docker-контейнера.
$ docker exec -it db-standalone mysql -u std_notes_user -p
Enter password:
Пароль берётся из значения DB_PASSWORD в файле .env. После успешного входа можно посмотреть доступные базы.
mysql > show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| standard_notes_db |
+--------------------+
2 rows in set (0.00 sec)
Переключение на базу Standard Notes.
mysql > use standard_notes_db;
Сам SQL-запрос для добавления квоты в 10 ГБ выглядит сложно, но работает без сюрпризов.
mysql> INSERT INTO subscription_settings(uuid, name, value, created_at, updated_at, user_subscription_uuid) VALUES (UUID(), "FILE_UPLOAD_BYTES_LIMIT", 10737418240, FLOOR(UNIX_TIMESTAMP(NOW(6))*1000000), FLOOR(UNIX_TIMESTAMP(NOW(6))*1000000), (SELECT us.uuid FROM user_subscriptions us INNER JOIN users u ON us.user_uuid=u.uuid WHERE u.email="Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. "));
Число 10737418240 - это 10 гигабайт в байтах (10 * 1024 * 1024 * 1024). При желании можно указать любое другое значение - 1073741824 для 1 гигабайта, 53687091200 для 50 гигабайт и так далее. После выполнения запроса выход из MySQL стандартный.
mysql > exit
Теперь в веб-приложении должна работать загрузка файлов. Для проверки достаточно открыть любую заметку, кликнуть по иконке вложений, выбрать файл и убедиться, что он успешно загружается. Загруженные файлы можно прикреплять прямо к заметкам или держать отдельно, скачивать, удалять и выполнять другие действия.
Самостоятельно размещённый Standard Notes - законченное решение для приватных заметок. В отличие от большинства альтернатив, здесь пользователь полностью контролирует свои данные на всех уровнях. Сквозное шифрование защищает от любого подглядывания, собственный сервер гарантирует независимость от третьих сторон, открытый код позволяет проверить работу системы. Ценой нескольких часов на первоначальную настройку получается инструмент, который будет работать годами без капризов и без риска неожиданной блокировки. Именно такие проекты постепенно меняют представление о том, как должны устроены современные сервисы - не как закрытые экосистемы, а как открытые платформы с контролем на стороне пользователя.