snens

Десктоп

Окно Tauri 2, внутри которого живёт ядро snens как дочерний процесс. Один установщик, одно обновление: ядро, тулы, плагин памяти и клиент едут вместе.

Раскладка после установки

Windows, установка per-user, без UAC:

%LOCALAPPDATA%\snens\
├── snens-desktop.exe      окно (Tauri)
├── snens.exe              ядро: HTTP/API + статика клиента
├── memory.exe             плагин долгой памяти
├── tools\                 read, edit, grep, run, shot, web, desk…
└── web\                   собранный клиент

Данные и конфиг — в %APPDATA%\snens, как у обычного snens: установка их не касается, удаление не трогает. Ссылки на тулы ядро раскладывает в bin/ в каталоге данных, а не установки: бандл только для чтения и перезаписывается обновлением, а bin/ — производная раскладка (на Windows — жёсткие ссылки, на unix — симлинки).

Почему ядро — сайдкар, а не служба

На Windows служба требует администратора и живёт в сессии 0, где нет ни профиля пользователя, ни UI. Поэтому ядро — обычный дочерний процесс окна, без консольного окна: у snens-desktop.exe стоит атрибут windows_subsystem = "windows", у дочернего ядра — CREATE_NO_WINDOW. Жизнь процесса привязана к окну.

Обновление одно на всё: сайдкары — часть бандла. На машине человека не нужны ни cargo, ни node, ни rustc.

Один процесс отдаёт и API, и клиент: snens serve умеет отдавать web/, поэтому отдельный vite в поставке не участвует. Окно всегда грузит свой клиент даже при подключении к чужому ядру — иначе экран подключения приезжал бы с сервера.

Трей и закрытие окна

Меню трея: «Открыть окно», «Перезагрузить интерфейс», «Выход (остановит ядро)». Крестик окна прячет окно, а не завершает приложение: ядро держит долгие ходы и подагентов. Выход — явный пункт меню, он же гасит ядро, чтобы следующая установка могла перезаписать файлы.

Пейринг: ядро на другом устройстве

Два режима, переключаются в настройках клиента (раздел «соединение»), а не флагами запуска:

  1. Локальное ядро (по умолчанию) — оболочка поднимает сайдкар на свободном порту, ждёт ready-файл с фактическим адресом и открывает окно на нём. Такое соединение на диск не сохраняется: при следующем запуске порт будет другой.
  2. Удалённое ядро — окно показывает форму подключения: адрес и код из snens pair на сервере. Адрес и токен ложатся в файл конфигурации приложения (%APPDATA%\ch.svuvi.snens\connection.json), и при следующем запуске оболочка своё ядро не поднимает. «Вернуться к своему ядру» стирает этот файл.

После смены соединения оболочка пересоздаёт окно: перезагрузка страницы оставила бы клиент на старом адресе. Токен попадает в окно инжектом, а не через localStorage.

Коды пейринга и отзыв устройств — Привилегии и доверие.

Первый запуск

  1. Установщик кладёт бандл в профиль пользователя и запускает приложение.
  2. Оболочка поднимает ядро и открывает окно на нём — экрана с адресом и токеном нет.
  3. Провайдер (адрес, модель, ключ) задаётся в настройках клиента. Пока его нет, ядро уже работает и отвечает: первый ход вернёт ошибку про ненайденный ключ, а не «сервер не запустился».
  4. Если ядро живёт на другом устройстве, в разделе «соединение» выбирается удалённый режим и вводится код с сервера.

Где лежат установщик и манифест

Адрес манифеста зашит в конфигурацию приложения, ключ проверки подписи — тоже. Каталог версии не перезаписывается: человек на прошлой версии ещё может качать её артефакт.

Сборка релиза

TAURI_SIGNING_PRIVATE_KEY_PATH=~/.config/snens/updater.key scripts/release.sh --check
TAURI_SIGNING_PRIVATE_KEY_PATH=~/.config/snens/updater.key scripts/release.sh

Скрипт делает четыре шага: раскладывает сайдкары и клиент (scripts/desktop-stage.sh — единственное место, где записано «что куда кладётся», бинарники получают суффикс target-triple), собирает бандлы nsis/appimage/deb с подписью, генерирует latest.json, затем копирует всё в каталог раздачи, выставляя каталогам 0755, файлам 0644.

Что нужно, чтобы собрать:

Версия берётся из desktop/tauri.conf.json — её меняют перед релизом, она же попадает в манифест.

Откат — установщик прошлой версии из её каталога; данные и конфиг установка не трогает.