8 Distribution and Secure Updates
| (require rivet/distribution) | package: rivet |
The updater treats HTTPS as transport protection, not as its root of trust. Manifests are independently signed with Ed25519 and bind the application ID, version, channel, rollout, rollback policy, artifact byte size, SHA-256 digest, platform, and architecture.
struct
(struct update-artifact ( platform architecture url sha256 size installer arguments component)) platform : symbol? architecture : symbol? url : string? sha256 : string? size : exact-nonnegative-integer? installer : symbol? arguments : (listof string?) component : (or/c #f symbol?)
struct
(struct update-manifest ( application-id version build channel published-at minimum-version previous-version rollback-allowed? rollout artifacts)) application-id : string? version : string? build : exact-positive-integer? channel : symbol? published-at : string? minimum-version : string? previous-version : (or/c #f string?) rollback-allowed? : boolean? rollout : exact-nonnegative-integer? artifacts : (listof update-artifact?)
struct
(struct updater-config ( application-id current-version channel platform architecture public-key expected-key-id rollout-bucket maximum-download-bytes component)) application-id : string? current-version : string? channel : symbol? platform : symbol? architecture : symbol? public-key : any/c expected-key-id : (or/c #f string?) rollout-bucket : exact-nonnegative-integer? maximum-download-bytes : exact-positive-integer? component : (or/c #f symbol?)
struct
(struct update-candidate (manifest artifact))
manifest : update-manifest? artifact : update-artifact?
struct
(struct install-plan ( candidate downloaded-path backup-path install restart rollback)) candidate : update-candidate? downloaded-path : path-string? backup-path : (or/c #f path-string?) install : procedure? restart : procedure? rollback : procedure?
struct
(struct updater-settings-adapter (get set!))
get : procedure? set! : procedure?
struct
(struct updater-runtime ( config destination-dir settings fetch download box silent-interval)) config : updater-config? destination-dir : path-string? settings : updater-settings-adapter? fetch : procedure? download : procedure? box : box? silent-interval : real?
procedure
(make-updater-runtime #:config config #:destination-dir destination-dir #:settings settings [ #:fetch fetch #:download download #:silent-interval-seconds silent-interval]) → updater-runtime? config : updater-config? destination-dir : path-string? settings : updater-settings-adapter? fetch : procedure? = fetch-update-manifest download : procedure? = download-update silent-interval : real? = silent-check-interval-seconds
procedure
(updater-runtime-state runtime) → hash?
runtime : updater-runtime?
procedure
(updater-runtime-check! runtime) → (or/c #f update-candidate?)
runtime : updater-runtime?
procedure
(updater-runtime-download! runtime candidate) → path? runtime : updater-runtime? candidate : update-candidate?
procedure
(silent-check-due? runtime now-seconds) → boolean?
runtime : updater-runtime? now-seconds : exact-positive-integer?
procedure
(mark-silent-checked! runtime now-seconds) → void?
runtime : updater-runtime? now-seconds : exact-positive-integer?
procedure
(draw-rollout-bucket) → (integer-in 0 99)
procedure
(resolve-rollout-bucket! settings [key]) → (integer-in 0 99)
settings : updater-settings-adapter? key : (or/c string? symbol?) = "update.rollout-bucket"
procedure
(platform-installation? value) → boolean?
value : any/c
8.1 Versions and Channels
procedure
(version-compare left right) → (or/c -1 0 1)
left : string? right : string?
procedure
(version<=? left right) → boolean?
left : string? right : string?
procedure
(version>=? left right) → boolean?
left : string? right : string?
procedure
(valid-channel? value) → boolean?
value : any/c
procedure
(channel-accepts-version? channel version) → boolean?
channel : symbol? version : string?
8.2 Release Target Metadata
These helpers live in the leaf module rivet/distribution/identity and are also re-exported by rivet/distribution. Requiring the leaf module does not initialize cryptographic factories or platform adapters.
procedure
(platform-symbol [os]) → (or/c 'windows 'macos 'linux)
os : symbol? = (system-type 'os)
procedure
(architecture-symbol [machine]) → (or/c 'x64 'arm64)
machine : symbol? = (system-type 'arch)
procedure
(installer-extension installer) → string?
installer : symbol?
procedure
(rfc3339-now [seconds]) → string?
seconds : real? = (current-seconds)
8.3 Signed Manifests
update-artifact values describe one platform artifact using the fields platform, architecture, url, sha256, size, installer, and arguments. update-manifest values contain the signed application and release policy plus a non-empty list of artifacts. The minimum updatable version and optional rollback predecessor must both be older than the release version. Artifact sizes are positive, and a manifest cannot repeat the same platform, architecture, and installer identity. Payloads and signed wrappers contain exactly one JSON value and reject fields outside their versioned schema, so a publisher typo cannot be silently ignored.
procedure
(write-signed-manifest manifest private-key key-id [ out]) → void? manifest : update-manifest? private-key : any/c key-id : string? out : output-port? = (current-output-port)
procedure
(read-signed-manifest input)
→
update-manifest? bytes? string? bytes? input : input-port?
procedure
(verify-signed-manifest input public-key [ #:key-id expected-key-id]) → update-manifest? input : input-port? public-key : any/c expected-key-id : (or/c #f string?) = #f
value
procedure
(manifest->payload-bytes manifest) → bytes?
manifest : update-manifest?
procedure
(payload-bytes->manifest payload) → update-manifest?
payload : bytes?
8.4 Key and Digest Primitives
The same primitives the updater uses, exposed for release tooling and tests. Keys are Racket crypto key values; readers accept the standard DER encodings (SubjectPublicKeyInfo, OneAsymmetricKey, PrivateKeyInfo) and Rivet’s textual forms.
procedure
(read-ed25519-public-key path) → any/c
path : path-string?
procedure
(read-ed25519-private-key path) → any/c
path : path-string?
procedure
(bytes->ed25519-public-key raw) → any/c
raw : bytes?
procedure
(ed25519-sign private-key message) → bytes?
private-key : any/c message : bytes?
procedure
(ed25519-verify public-key message signature) → boolean? public-key : any/c message : bytes? signature : bytes?
procedure
(sha256-bytes input) → bytes?
input : bytes?
procedure
(sha256-file path) → bytes?
path : path-string?
procedure
(sha256-file/hex path) → string?
path : path-string?
procedure
(bytes->base64-string value) → string?
value : bytes?
procedure
(base64-string->bytes value) → bytes?
value : string?
8.5 Selecting and Installing an Update
An updater-config records the application ID, current version, channel, platform, architecture, public key, expected key ID, deterministic rollout bucket, and maximum download size.
procedure
(fetch-update-manifest manifest-url public-key [ #:key-id key-id #:maximum-bytes maximum-bytes #:connect-timeout-seconds connect-timeout-seconds #:idle-timeout-seconds idle-timeout-seconds]) → update-manifest? manifest-url : string? public-key : any/c key-id : (or/c #f string?) = #f maximum-bytes : exact-positive-integer? = (* 1024 1024) connect-timeout-seconds : (real-in 0.001 3600) = 30 idle-timeout-seconds : (real-in 0.001 3600) = 60
procedure
(fetch-sibling-asset url destination [ #:sha256 expected-sha256 #:maximum-bytes maximum-bytes #:on-progress on-progress #:connect-timeout-seconds connect-timeout-seconds #:idle-timeout-seconds idle-timeout-seconds]) → path? url : string? destination : path-string? expected-sha256 : (or/c #f string?) = #f maximum-bytes : exact-positive-integer? = (* 1024 1024 1024) on-progress : procedure? = void connect-timeout-seconds : (real-in 0.001 3600) = 30 idle-timeout-seconds : (real-in 0.001 3600) = 60
procedure
(select-update config manifest) → (or/c #f update-candidate?)
config : updater-config? manifest : update-manifest?
procedure
(download-update config candidate destination [ #:on-progress on-progress #:progress-every progress-every #:stall-timeout-seconds stall-timeout-seconds #:total-timeout-seconds total-timeout-seconds]) → path? config : updater-config? candidate : update-candidate? destination : path-string? on-progress : procedure? = void progress-every : (or/c #f exact-positive-integer?) = #f stall-timeout-seconds : (real-in 0.001 3600) = 60 total-timeout-seconds : (or/c #f (real-in 0.001 86400)) = #f
procedure
(verify-update-artifact! candidate path) → path-string?
candidate : update-candidate? path : path-string?
procedure
(make-install-plan candidate downloaded-path [ #:backup-path backup-path] #:install install [ #:restart restart #:rollback rollback]) → install-plan? candidate : update-candidate? downloaded-path : path-string? backup-path : (or/c #f path-string?) = #f install : procedure? restart : procedure? = void rollback : procedure? = void
8.6 Managed Check and Download Lifecycle
procedure
(current-update-architecture) → (or/c 'x64 'arm64)
procedure
(make-updater #:application-id application-id #:current-version current-version [ #:channel channel #:platform platform #:architecture architecture #:install-kind install-kind] #:public-key public-key #:key-id key-id #:manifest-url manifest-url #:data-directory data-directory #:rollout-bucket rollout-bucket [ #:maximum-manifest-bytes maximum-manifest-bytes #:maximum-download-bytes maximum-download-bytes #:check-timeout check-timeout #:stall-timeout stall-timeout #:total-timeout total-timeout]) → updater? application-id : string? current-version : string? channel : (or/c 'stable 'beta 'dev) = 'stable
platform : (or/c 'windows 'macos 'linux) = (current-update-platform)
architecture : (or/c 'x64 'arm64) = (current-update-architecture)
install-kind : (or/c #f 'portable 'msi 'appimage 'package-manager) = #f public-key : any/c key-id : string? manifest-url : string? data-directory : path-string? rollout-bucket : (integer-in 0 99)
maximum-manifest-bytes : exact-positive-integer? = (* 1024 1024)
maximum-download-bytes : exact-positive-integer? = (* 512 1024 1024) check-timeout : (and/c real? positive?) = 30.0 stall-timeout : (and/c real? positive?) = 120.0 total-timeout : (and/c real? positive?) = (* 30.0 60.0)
On Windows, pass 'msi for an MSI-installed copy; release manifests carry a signed MSI entry alongside the portable ZIP, and the linked host library’s rivet::update::MsiHandoff owns the bounded quit/install/relaunch step. Linux AppImage copies pass 'appimage and use the platform adapter for the atomic replacement.
procedure
(updater-check! instance) → hash?
instance : updater?
procedure
(updater-download! instance) → void?
instance : updater?
procedure
(updater-cancel! instance) → boolean?
instance : updater?
procedure
(updater-state instance) → hash?
instance : updater?
procedure
(updater-wait instance [timeout]) → (or/c #f hash?)
instance : updater? timeout : (or/c #f (and/c real? (not/c negative?))) = #f
procedure
(execute-install-plan! plan [ #:health-check health-check #:commit commit #:journal-path journal-path]) → any/c plan : install-plan? health-check : (-> any/c) = (lambda () #t) commit : (-> any/c) = void journal-path : (or/c #f path-string?) = #f
When journal-path is provided, every destructive phase is written to an atomically replaced JSON journal. The restart callback must start the replacement and return so the health check and commit can complete. Use an absolute downloaded path and make rollback idempotent when durable recovery is enabled.
procedure
(recover-install-plan! plan journal-path [ #:commit commit]) → (or/c 'committed 'rolled-back) plan : install-plan? journal-path : path-string? commit : (-> any/c) = void
8.7 First-Party Platform Replacement
procedure
(current-update-platform) → (or/c 'windows 'macos 'linux)
procedure
(current-install-kind [executable])
→ (or/c 'portable 'appimage 'package-manager)
executable : (or/c #f path-string?) = (find-system-path 'run-file)
procedure
(platform-installer-policy platform installer) → (or/c 'portable 'package-manager 'unsupported) platform : symbol? installer : symbol?
procedure
(verify-platform-payload! platform payload) → void?
platform : symbol? payload : path-string?
procedure
(prepare-platform-installation candidate downloaded-path #:target target-path #:restart restart #:health-check health-check [ #:verify-staged verify-staged #:backup-path backup-path]) → platform-installation? candidate : update-candidate? downloaded-path : path-string? target-path : complete-path? restart : procedure? health-check : procedure? verify-staged : (or/c #f procedure?) = #f backup-path : (or/c #f complete-path?) = #f
procedure
(execute-platform-installation! installation [ #:journal-path journal-path]) → any/c installation : platform-installation? journal-path : (or/c #f path-string?) = #f
procedure
(recover-platform-installation! installation journal-path) → (or/c 'committed 'rolled-back) installation : platform-installation? journal-path : path-string?