Внести изменения

Редактировать эту документацию

Переведите документацию или откройте весь проект на своём компьютере.

Ничего не будет опубликовано, пока вы не решите отправить свою работу.

Протокол манифеста обновлений

Открытый протокол JSON, который приложения Wails используют для обнаружения и проверки собственных обновлений; манифест можно размещать на любом хостинге статических файлов или динамическом сервере обновлений.

Протокол Wails Update Manifest — это небольшой открытый контракт в формате JSON между приложением Wails и источником обновлений. Обновления Wails может предоставлять любой ресурс, способный передавать файл JSON по HTTPS: бакет S3, GitHub Pages, CDN или динамический сервер обновлений, который предоставляет выпуски только при наличии лицензии.

Клиентская часть поставляется вместе с фреймворком в виде провайдера endpoint (github.com/wailsapp/wails/v3/pkg/updater/providers/endpoint). На этой странице приведено справочное описание формата обмена данными для разработчиков серверной части.

Цели проектирования

  1. Поддержка статического хостинга. Для полноценной реализации достаточно одного файла манифеста на каждый канал со списком артефактов для всех платформ. Серверный код не требуется.
  2. Поддержка динамического сервера. При каждой проверке клиент отправляет platform, arch, version и channel, поэтому сервер может вернуть ровно один артефакт, применить правила лицензирования или вернуть 204 No Content, если у вызывающей стороны уже установлена актуальная версия.
  3. Проверка прежде всего. Манифест содержит контрольные суммы и подписи для каждого артефакта, а средство обновления Wails проверяет их с помощью открытого ключа, закреплённого в двоичном файле приложения во время сборки. Источник обновлений никогда не выбирает собственный корень доверия.

Запрос

Клиент отправляет GET на настроенный URL-адрес манифеста с Accept: application/json и всеми заголовками, настроенными приложением (например, Authorization: License <key>).

URL-адрес может содержать заполнители, которые клиент заменяет при каждой проверке:

Заполнитель Заменяется на
{{platform}} Текущая ОС в виде значения Go GOOS (darwin, windows, linux)
{{arch}} Текущая архитектура в виде значения Go GOARCH (amd64, arm64, …)
{{version}} Текущая установленная версия
{{channel}} Настроенный канал выпуска, если он задан

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

text
# Dynamic server: reads query parameters
https://updates.example.com/check
  -> GET /check?platform=darwin&arch=arm64&version=1.0.0&channel=stable

# Static host: one manifest per platform/arch/channel path
https://cdn.example.com/updates/{{platform}}/{{arch}}/{{channel}}.json
  -> GET /updates/darwin/arm64/stable.json?version=1.0.0

Статические хостинги просто игнорируют полученные параметры запроса.

Ответ

Статус Значение
200 OK Далее следует манифест. Клиент определяет, является ли указанная версия обновлением.
204 No Content Сервер сравнил версии, и у вызывающей стороны установлена актуальная версия.
404 Not Found Ничего не опубликовано (обрабатывается так же, как наличие актуальной версии).
Любой другой Ошибка. Средство обновления переходит к следующему настроенному провайдеру.

Тело 200 представляет собой документ манифеста:

json
{
  "schemaVersion": 1,
  "version": "2.1.0",
  "channel": "stable",
  "name": "Summer Release",
  "notes": "## What's new\n\n- Faster startup\n- New themes",
  "publishedAt": "2026-07-03T10:00:00Z",
  "artifacts": [
    {
      "url": "MyApp-2.1.0-darwin-arm64.zip",
      "platform": "darwin",
      "arch": "arm64",
      "filetype": "zip",
      "size": 8388608,
      "digestAlgo": "sha512",
      "digest": "base64-encoded digest bytes",
      "signatureAlgo": "ed25519ph",
      "signature": "base64-encoded signature bytes"
    },
    {
      "url": "MyApp-2.1.0-windows-amd64.zip",
      "platform": "windows",
      "arch": "amd64",
      "filetype": "zip",
      "size": 9437184,
      "digestAlgo": "sha512",
      "digest": "...",
      "signatureAlgo": "ed25519ph",
      "signature": "..."
    }
  ]
}

Поля верхнего уровня

Поле Тип Обязательное Примечания
schemaVersion int нет Версия протокола. Если поле отсутствует, подразумевается 1. Клиенты отклоняют значения, соответствующие более новым, неизвестным им версиям.
version string да Версия, соответствующая спецификации SemVer 2.0.0, с начальным v или без него.
channel string нет Информационное поле. Клиент, настроенный на другой канал, считает, что обновление отсутствует.
name string нет Понятное пользователю название выпуска, отображаемое в окне обновления.
notes string нет Примечания к выпуску в формате Markdown, отображаемые в окне обновления.
publishedAt string нет Метка времени в формате RFC 3339.
artifacts array да По одной записи для каждого доступного для скачивания артефакта. Порядок отражает предпочтения издателя.
metadata object нет Данные в виде произвольных пар «ключ — значение», передаваемые приложению без изменений.

Клиенты игнорируют неизвестные поля, поэтому серверы могут добавлять собственные поля, не нарушая работу клиентов. Специфичные для сервера дополнения следует помещать в metadata.

Поля артефакта

Поле Тип Обязательное Примечания
url string да Абсолютный или относительный по отношению к URL манифеста. Только http(s).
platform string нет Значение Go GOOS. Поддерживаются распространённые псевдонимы (macos, win, …). Пустое значение соответствует любой платформе.
arch string нет Значение Go GOARCH. Поддерживаются распространённые псевдонимы (x86_64, aarch64, …). Пустое значение соответствует любой архитектуре.
filename string нет По умолчанию используется последний сегмент пути из url.
filetype string нет По умолчанию используется расширение имени файла.
size int нет Размер в байтах, используемый для отображения хода загрузки.
digestAlgo / digest string / base64 нет sha256 или sha512.
signatureAlgo / signature string / base64 нет ed25519, ed25519ph или ecdsa-p256. При наличии signature поле signatureAlgo обязательно. О том, что подписывает каждый алгоритм, см. в руководстве по средству обновления.

Клиент выбирает первый артефакт, у которого platform и arch соответствуют текущей системе. Значения Base64 принимаются как с заполнением, так и без него.

Сравнение версий

Решение о том, является ли манифест обновлением, всегда принимается на стороне клиента согласно порядку приоритетов SemVer 2.0.0: значение version в манифесте должно быть строго новее установленной версии. Благодаря этому статический хостинг работает корректно без дополнительных усилий: манифест всегда описывает последний выпуск, а клиенты с актуальной версией просто ничего не делают. При этом динамические серверы по-прежнему могут использовать 204 для экономии трафика.

Проверка и доверие

Контрольные суммы и подписи передаются в манифесте, но корень доверия — нет: подписи проверяются по открытому ключу, который приложение закрепило через updater.Config.PublicKey во время сборки. Скомпрометированный или подменённый источник обновлений не может предоставить собственный ключ. Если артефакт содержит подпись, но в приложении не закреплён ключ, проверка завершается отказом. То же происходит, если для подписи не объявлено значение signatureAlgo или подпись не удаётся декодировать: клиенты никогда не переходят без уведомления к проверке только по дайджесту.

Артефакты только с дайджестом устанавливаются после проверки дайджеста. Это защищает от повреждения данных, но защита от подмены зависит от TLS и от того, что сам хост не скомпрометирован. Добавляйте подписи ко всему, что критично для безопасности.

Чтобы подписать артефакт по схеме ed25519ph, используемой фреймворком, достаточно нескольких строк кода Go:

go
digest := sha512.Sum512(artifactBytes)
sig, _ := privateKey.Sign(nil, digest[:], &ed25519.Options{Hash: crypto.SHA512})
manifest.Artifacts[i].DigestAlgo = "sha512"
manifest.Artifacts[i].Digest = base64.StdEncoding.EncodeToString(digest[:])
manifest.Artifacts[i].SignatureAlgo = "ed25519ph"
manifest.Artifacts[i].Signature = base64.StdEncoding.EncodeToString(sig)

На практике писать этот код почти никогда не приходится: CLI делает это за вас.

Публикация с помощью CLI wails3

Группа команд wails3 updater охватывает весь процесс публикации. Для выпуска нужны три команды:

bash
# Once per application: create the signing keypair.
wails3 updater genkey
# updater.key      keep secret (CI secret store), signs every release
# updater.key.pub  embed in the app and pass as updater.Config.PublicKey

# Per release: digest, sign and describe every artifact in one manifest.
wails3 updater manifest -version 2.1.0 -channel stable \
    -key updater.key -notes-file notes.md \
    -url-prefix "https://cdn.example.com/myapp/2.1.0" \
    bin/updates/

# Before uploading: re-verify the files exactly as a shipped app would.
wails3 updater verify -manifest manifest.json -publickey updater.key.pub

manifest принимает файлы или каталоги (материалы ключей, .json, вспомогательные файлы контрольных сумм и примечаний пропускаются автоматически), потоково обрабатывает каждый артефакт с помощью SHA-512, подписывает дайджест алгоритмом Ed25519ph, если указан -key, и определяет platform и arch по стандартным именам файлов, таким как MyApp-2.1.0-darwin-arm64.zip. Поддерживаются распространённые псевдонимы, например macOS, win64, x86_64 и aarch64. Если определить значение не удаётся, выводится предупреждение, после чего артефакт считается подходящим для любой платформы. Не указывайте -url-prefix, чтобы сформировать относительные URL и загрузить манифест рядом с артефактами.

При любом несоответствии verify завершается с ненулевым кодом, поэтому эту команду удобно использовать как проверку CI между сборкой и публикацией. Для серверов, которые самостоятельно формируют манифесты, wails3 updater sign -key updater.key <files...> выводит поля digest/signature каждого файла в формате JSON, готовом для включения в ваш документ.

Аутентификация

Аутентификацией занимается сервер; протокол лишь передаёт заголовки. Клиент повторно отправляет настроенные заголовки с каждым запросом манифеста. При загрузке артефактов заголовок Authorization отправляется только в том случае, если URL артефакта находится на том же хосте, что и манифест, и протокол не понижается с https до http. При любом перенаправлении на другой источник или с понижением протокола этот заголовок удаляется, поэтому учётные данные никогда не передаются в CDN или объектное хранилище и никогда не отправляются в открытом виде.

Пример с доступом по лицензии, естественным образом сочетающийся с размещёнными службами лицензирования:

go
ep, _ := endpoint.New(endpoint.Config{
    URL:     "https://updates.example.com/check",
    Headers: map[string]string{"Authorization": "License " + licenseKey},
})

Настройка клиента

Полное справочное описание endpoint.Config и сведения о включении провайдера в цепочки резервирования наряду с провайдерами GitHub, keygen.sh и AppCast см. в руководстве по средству обновления.

Редактировать страницу

Последнее обновление: