CH-J Toolsуправление серверами через SSH
Меню

Сохранение в удаленном редакторе и мониторинг задержки

Загрузить файл

Языковая редактура этого перевода продолжается. Если фрагмент неясен, сверьтесь с английским оригиналом.

Основные причины

Прежний редактор загружал рабочий файл в родительский каталог цели, куда учетная запись SSH в /etc или /var часто не имеет права записи. Переименование через SFTP не давало ни авторизованного пути через sudo, ни проверки конфликта с исходным состоянием, ни проверки метаданных безопасности, ни надежной семантики перезаписи. Обработчик ошибок удалял временную копию и тем самым уничтожал материал для восстановления даже тогда, когда завершение было неопределенным.

Ранее статус сеанса показывал только состояние соединения. Индикатор терминала не получал ни измерения ICMP, ни измерения отклика аутентифицированного SSH.

Протокол сохранения

  1. Фиксированная вспомогательная программа SSH открывает обычный файл с единственной жесткой ссылкой, используя операции относительно дескриптора и O_NOFOLLOW для каждого компонента пути. Она фиксирует содержимое, хеш SHA-256, устройство/inode, UID, GID, все биты прав доступа, наносекундные времена изменения содержимого и статуса, а также все читаемые расширенные атрибуты. Core проверяет хеш и декодирует строгий UTF-8 с сохранением BOM. Каждый документ получает непрозрачный editId, привязанный к своему плагину, сеансу терминала и идентичности сервера.
  2. Непривилегированная подготовительная операция определяет домашний каталог аутентифицированной учетной записи через pwd.getpwuid(os.getuid()), создает $HOME/ch-j-sm/ с режимом 0700 и создает закрытый эксклюзивный журнал восстановления. Небезопасный домашний каталог, рабочая область в виде символической ссылки или рабочая область другой учетной записи отклоняются.
  3. Core загружает через SFTP скрытый случайный 128-битный рабочий файл .tmp с эксклюзивным созданием (wx), режимом 0600 и шестидесятисекундным тайм-аутом канала/загрузки. Журнал содержит цель, исходное состояние и ожидаемый хеш рабочей копии; учетных данных он не содержит.
  4. Весь этап завершения выполняется одним вызовом SSH exec, при необходимости через существующий исполнитель sudo. Вспомогательная программа безопасно открывает закрытую рабочую копию, вычисляет ее хеш и сбрасывает на диск. Она блокирует исходный inode, проверяет его исходное состояние, создает эксклюзивный файл назначения рядом с целью, записывает все содержимое, применяет и проверяет метаданные, еще раз проверяет оригинал и сбрасывает замену на диск.
  5. os.replace в том же каталоге атомарно заменяет цель. Родительский каталог сбрасывается на диск, после чего проверяются итоговый путь, хеш, inode и метаданные. Исходный путь остается доступным до замены. Рабочий файл никогда не перемещается между файловыми системами и никогда не удаляется на этапе завершения.
  6. После того как Core получает проверенный успех, отдельная очистка повторно проверяет итоговое содержимое и метаданные безопасности и только затем удаляет рабочий файл и журнал. Сбой очистки все равно сообщается как подтвержденное сохранение с recoveryAvailable: true.

Неудачная загрузка, неудачный sudo, конфликт, ошибка метаданных или неудачная замена сохраняют оригинал и весь подготовленный материал. Неполные загрузки помечаются как таковые и не могут быть восстановлены как полный отредактированный документ. Потеря связи SSH, отсутствие кода завершения, тайм-ауты и неожиданные результаты завершения сообщаются как неизвестные, но никогда как сохраненные. Core блокирует повторное сохранение документа с неопределенным результатом; автоматического повтора замены нет.

Метаданные, привилегии и границы безопасности

Вспомогательная программа сохраняет и проверяет UID, GID и все биты прав POSIX, включая биты исполнения, setuid, setgid и sticky. Расширенные атрибуты Linux воспроизводятся байт в байт, включая POSIX ACL, capabilities и метки SELinux. Владелец и содержимое файла устанавливаются до атрибутов безопасности, поскольку запись/chown могут сбросить capabilities или специальные биты прав. Унаследованные атрибуты файла назначения, которых нет у оригинала, удаляются. Время изменения отражает новую запись и не копируется с оригинала.

Нечитаемый или непредставимый атрибут прерывает операцию; метаданные никогда не отбрасываются молча. Подписи IMA/EVM явно отклоняются, поскольку копирование подписей целостности, привязанных к содержимому/inode, сделало бы их недействительными. Символические ссылки в пути цели или домашнего каталога, файлы с несколькими жесткими ссылками и необычные файлы отклоняются. Не используется ни перезапись на месте, ни более слабый резервный вариант перезаписи через SFTP.

Обычные сохранения с правом записи выполняются от имени учетной записи SSH. Защищенные сохранения требуют явного { sudo: true }; пароль sudo необязателен для sudo без пароля. Пароли проверяются и передаются только через stdin канала, но никогда в команде, журнале, рабочем файле, логе или удаленной ошибке. Сеансы SSH от root используют существующий прямой исполнитель root. Неудачная авторизация оставляет оригинал и рабочую копию нетронутыми. Все сохранения, выполняемые от root (прямой SSH от root или sudo), дополнительно отклоняют каталоги цели/предков, принадлежащие другим учетным записям, доступные им на запись или имеющие ACL доступа, вместо того чтобы доверять привилегированному пути, контролируемому другой учетной записью.

Фиксированная вспомогательная программа запускается с python3 -I -B: она игнорирует пользовательские пути Python, рабочий каталог и пользовательские site packages и не записывает байт-код. Пути и аргументы JSON экранируются для оболочки в Core. Плагины никогда не получают произвольный SSH exec или дескрипторы SFTP. Существующие песочница, CSP, разрешения манифеста, проверка ключа хоста и проверка обновлений продолжают действовать.

Plugin API и оставшаяся интеграция Monaco

Отдельно распространяемый интерфейс Monaco/File Manager отсутствует в этом checkout. Его интеграцию в UI, запрос sudo, обработку несохраненного буфера и переводы здесь не удалось реализовать или протестировать. Встроенным исходником плагина является плагин Hash & Checksum. Никакой замены исходников File Manager не придумывалось.

Существующие методы остаются доступными. Дополнительные методы/параметры предоставляют preload в песочнице и среда выполнения с проверкой разрешений:

const opened = await chjPlugin.files.readText(sessionId, path);
// For a protected read, explicitly pass { sudo: true, sudoPassword }.

const result = await chjPlugin.files.saveText(sessionId, path, editor.getValue(), {
  editId: opened.editId,
  sudo: true,           // Include only after the user authorizes elevation.
  sudoPassword         // Omit for passwordless sudo; never persist this value.
});

if (result.ok && result.value.status === "saved") {
  // Mark this exact submitted editor version as saved. Later edits stay dirty.
} else {
  // Keep the Monaco model dirty. Translate result.error.code/status.
  // result.error.recoveryId/recoveryPath identify retained working material.
}

await chjPlugin.files.closeText(opened.editId);
const recovery = await chjPlugin.files.listRecovery(sessionId);
const copy = await chjPlugin.files.readRecovery(sessionId, recoveryId);
// Review copy.status and copy.text; open a fresh document before another save.
await chjPlugin.files.cleanupRecovery(sessionId, recoveryId);
// Cleanup rejects anything whose target is not currently confirmed.

Методы восстановления принимают необязательный последний аргумент { sudo: true, sudoPassword }, если проверка цели требует повышения прав. Они сохраняют рабочую область аутентифицированного пользователя даже при выполнении через sudo. Журналы переживают перезапуск приложения и повторное подключение SSH; восстановление никогда не повторяет завершение автоматически. Перед явным сохранением сравните восстановленный буфер с заново открытой целью.

files.read защищает чтение, проверку и освобождение дескрипторов. files.write защищает сохранение и очистку восстановления. writeText для существующих плагинов по-прежнему отклоняет промис при сбое; saveText возвращает структурированную оболочку { ok, value/error }, поскольку Electron отбрасывает пользовательские свойства отклоненных объектов Error. Устаревший вызов writeText с тремя аргументами может использовать исходное состояние единственного открытого документа. Несколько активных документов для одного пути требуют editId; неоднозначные устаревшие вызовы безопасно завершаются ошибкой, вместо того чтобы выбрать исходное состояние другого документа. Плагинам следует освобождать неиспользуемые дескрипторы.

Внешний интерфейс должен сразу показывать сохранение, при сбое оставлять буферы несохраненными, предлагать sudo только после ошибок прав/аутентификации, показывать конфликты без перезаписи и предлагать восстановление после потери соединения или неизвестного результата. Он должен переводить эти состояния на чешский, немецкий и английский. Предлагаемые тексты состояний:

Состояние Чешский Немецкий Английский
Сохранение Ukládání… Wird gespeichert… Saving…
Подтверждено Uloženo Gespeichert Saved
Сбой Uložení selhalo Speichern fehlgeschlagen Save failed
Требуется sudo Vyžadováno oprávnění sudo sudo-Autorisierung erforderlich Sudo authorization required
Конфликт Původní soubor byl změněn Originaldatei wurde geändert Original file changed
Отключено Spojení bylo přerušeno Verbindung unterbrochen Connection lost
Неизвестно Výsledek uložení není znám Speicherergebnis unbekannt Save result unknown
Восстановление Obnova je dostupná Wiederherstellung verfügbar Recovery available

Определения задержки и жизненный цикл

Ping — задержка, сообщаемая реальным ответом ICMP echo на стороне клиента от разрешенного IP-адреса, который использует SSH. Core асинхронно запускает системную программу ping с фиксированным массивом аргументов, без оболочки и без запуска с правами администратора. Аргументы для Linux, macOS (включая ping6) и Windows выбираются отдельно. Заблокированный ICMP, отсутствующая программа, тайм-аут, некорректный вывод или ответ Windows только с верхней границей, например <1ms, дают недоступное значение, а не выдуманное число.

SSH RTT — монотонная длительность performance.now() фиксированного запроса exec true через существующий аутентифицированный клиент SSH. Она включает установку канала, обработку на сервере и доставку ответа; это не чистое сетевое RTT и не длительность входа по SSH. Переведенная подсказка индикатора объясняет это различие.

Мониторинг начинается сразу после подключения терминала. Пробы выполняются параллельно и асинхронно с пятисекундным тайм-аутом запроса/процесса. Следующее измерение планируется через двенадцать секунд после завершения предыдущего, поэтому они не перекрываются. Отключение прерывает ICMP, сбрасывает таймеры, отменяет незавершенную работу SSH и очищает значения. Проверки идентичности записи не позволяют прежнему соединению публиковать значения после переподключения. Сбой ICMP никогда не меняет состояние соединения SSH.

Задержка передается внутри существующего события state / данных IPC ssh:state:

latency: { pingMs: 24.1, sshRttMs: 31.25, measuredAt: "…" }

Недоступные значения равны null. Компактный индикатор терминала использует миллисекунды, локализованный текст недоступности и подписи на чешском/немецком/английском. При отключении он скрывается. Дополнительные возможности выполнения в renderer или запросы IPC не потребовались.

Проверка и эксплуатационные ограничения

Пользователь просил сохранить изменения в существующем рабочем дереве, сейчас это feature/hash-checksum-plugin; переключения ветки и посторонней очистки не было.

Файлы, измененные в этой задаче