Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

- Add leaf-loadable distribution identity helpers for the canonical manifest
platform, architecture, installer-extension, and UTC RFC 3339 vocabulary.
Release tooling and the managed updater now consume the same implementation
instead of maintaining private mappings.
- Add the Windows device-gateway transport. A CNG-persisted P-256
self-signed identity (rotatable by deletion), an SChannel TLS server over
any accepted socket with the `rivet-device/1` ALPN requirement, the
Expand Down
12 changes: 4 additions & 8 deletions rivet-cli/release-layout.rkt
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
#lang racket/base

(require "project.rkt")
(require "../rivet/distribution/identity.rkt"
"project.rkt")

(provide release-platform
release-architecture
portable-zip-path)

(define (release-platform)
(case (system-type 'os)
[(windows) 'windows]
[(macosx) 'macos]
[(unix) 'linux]))
(platform-symbol))

(define (release-architecture)
(case (system-type 'arch)
[(aarch64 arm64) 'arm64]
[else 'x64]))
(architecture-symbol))

(define (portable-zip-path project)
(project-path
Expand Down
15 changes: 2 additions & 13 deletions rivet-cli/release.rkt
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
#lang racket/base

(require racket/date
file/zip
(require file/zip
racket/file
racket/format
racket/path
racket/string
"../rivet/distribution/crypto.rkt"
"../rivet/distribution/identity.rkt"
"../rivet/distribution/manifest.rkt"
"appimage.rkt"
"compliance.rkt"
Expand Down Expand Up @@ -37,16 +36,6 @@
(string->path (required-environment "RIVET_UPDATE_PRIVATE_KEY"))
(required-environment "RIVET_UPDATE_KEY_ID"))))

(define (rfc3339-now)
(define d (seconds->date (current-seconds) #t))
(format "~a-~a-~aT~a:~a:~aZ"
(~r (date-year d) #:min-width 4 #:pad-string "0")
(~r (date-month d) #:min-width 2 #:pad-string "0")
(~r (date-day d) #:min-width 2 #:pad-string "0")
(~r (date-hour d) #:min-width 2 #:pad-string "0")
(~r (date-minute d) #:min-width 2 #:pad-string "0")
(~r (date-second d) #:min-width 2 #:pad-string "0")))

;; Portable zip beside the installer: the family update feed consumes
;; it and it runs on locked-down machines. Deterministic input tree,
;; family naming <name>-<version>-<os>-<arch>.zip, sha256 alongside.
Expand Down
2 changes: 2 additions & 0 deletions rivet/distribution.rkt
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#lang racket/base

(require "distribution/crypto.rkt"
"distribution/identity.rkt"
"distribution/managed-updater.rkt"
"distribution/manifest.rkt"
"distribution/platform-adapter.rkt"
"distribution/updater.rkt"
"distribution/version.rkt")

(provide (all-from-out "distribution/crypto.rkt"
"distribution/identity.rkt"
"distribution/managed-updater.rkt"
"distribution/manifest.rkt"
"distribution/platform-adapter.rkt"
Expand Down
59 changes: 59 additions & 0 deletions rivet/distribution/identity.rkt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#lang racket/base

;; Release-target vocabulary shared by the publisher and update clients. Keep
;; this module leaf-loadable: callers that only report platform identity must
;; not initialize crypto factories or native update adapters.

(require racket/date
racket/format)

(provide platform-symbol
architecture-symbol
installer-extension
rfc3339-now)

(define (platform-symbol [os (system-type 'os)])
(case os
[(windows) 'windows]
[(macosx) 'macos]
[(unix) 'linux]
[else
(raise-arguments-error 'platform-symbol
"unsupported operating system"
"system-type" os)]))

(define (architecture-symbol [machine (system-type 'arch)])
(case machine
[(x86_64 amd64) 'x64]
[(aarch64 arm64) 'arm64]
[else
(raise-arguments-error 'architecture-symbol
"unsupported architecture"
"system-type" machine)]))

(define (installer-extension installer)
(case installer
[(zip) ".zip"]
[(targz) ".tar.gz"]
[(msi) ".msi"]
[(msix) ".msix"]
[(exe) ".exe"]
[(dmg) ".dmg"]
[(pkg) ".pkg"]
[(deb) ".deb"]
[(rpm) ".rpm"]
[(appimage) ".AppImage"]
[else
(raise-arguments-error 'installer-extension
"unsupported installer kind"
"installer" installer)]))

(define (rfc3339-now [seconds (current-seconds)])
(define d (seconds->date seconds #f))
(format "~a-~a-~aT~a:~a:~aZ"
(~r (date-year d) #:min-width 4 #:pad-string "0")
(~r (date-month d) #:min-width 2 #:pad-string "0")
(~r (date-day d) #:min-width 2 #:pad-string "0")
(~r (date-hour d) #:min-width 2 #:pad-string "0")
(~r (date-minute d) #:min-width 2 #:pad-string "0")
(~r (date-second d) #:min-width 2 #:pad-string "0")))
19 changes: 10 additions & 9 deletions rivet/distribution/managed-updater.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
racket/path
racket/string
"crypto.rkt"
"identity.rkt"
"manifest.rkt"
"platform-adapter.rkt"
"updater.rkt"
Expand Down Expand Up @@ -37,13 +38,13 @@
(define current-managed-download (make-parameter download-update))

(define (update-architecture-for machine)
(case machine
[(x86_64 amd64) 'x64]
[(aarch64 arm64) 'arm64]
[else
(raise-arguments-error 'current-update-architecture
"unsupported update architecture"
"architecture" machine)]))
(with-handlers ([exn:fail:contract?
(lambda (_)
(raise-arguments-error
'current-update-architecture
"unsupported update architecture"
"architecture" machine))])
(architecture-symbol machine)))

(define (current-update-architecture)
(update-architecture-for (system-type 'arch)))
Expand All @@ -70,12 +71,12 @@
(build-path
data-directory
"updates"
(format "~a-~a-~a-~a.~a"
(format "~a-~a-~a-~a~a"
(safe-component application-id)
(update-manifest-version manifest)
(update-artifact-platform artifact)
(update-artifact-architecture artifact)
(update-artifact-installer artifact))))
(installer-extension (update-artifact-installer artifact)))))

(define (initial-state current-version)
(hasheq 'phase "idle"
Expand Down
10 changes: 2 additions & 8 deletions rivet/distribution/platform-adapter.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
racket/path
racket/string
racket/system
"identity.rkt"
"manifest.rkt"
"updater.rkt")

Expand All @@ -25,14 +26,7 @@
(struct platform-installation (plan health-check commit transaction-lock))

(define (current-update-platform)
(case (system-type 'os)
[(windows) 'windows]
[(macosx) 'macos]
[(unix) 'linux]
[else
(raise-arguments-error 'current-update-platform
"unsupported operating system"
"system-type" (system-type 'os))]))
(platform-symbol))

(define (detect-install-kind platform executable appimage)
(cond
Expand Down
27 changes: 27 additions & 0 deletions rivet/scribblings/rivet.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,33 @@ Reports SemVer precedence; equivalent to a negative
@defproc[(channel-accepts-version? [channel symbol?] [version string?]) boolean?]{
Checks whether a version is allowed by a release channel.}

@subsection{Release Target Metadata}

These helpers live in the leaf module @racketmodname[rivet/distribution/identity]
and are also re-exported by @racketmodname[rivet/distribution]. Requiring the
leaf module does not initialize cryptographic factories or platform adapters.

@defproc[(platform-symbol [os symbol? (system-type 'os)])
(or/c 'windows 'macos 'linux)]{
Maps Racket's @racket['windows], @racket['macosx], and @racket['unix]
operating-system symbols to the exact platform vocabulary used by release
manifests. Unsupported values fail instead of being classified as Linux.}

@defproc[(architecture-symbol [machine symbol? (system-type 'arch)])
(or/c 'x64 'arm64)]{
Maps @racket['x86_64]/@racket['amd64] and
@racket['aarch64]/@racket['arm64] to the manifest architecture vocabulary.
Unsupported architectures fail instead of being classified as x64.}

@defproc[(installer-extension [installer symbol?]) string?]{
Returns the canonical filename extension for @racket['zip], @racket['targz],
@racket['msi], @racket['msix], @racket['exe], @racket['dmg], @racket['pkg],
@racket['deb], @racket['rpm], or @racket['appimage].}

@defproc[(rfc3339-now [seconds real? (current-seconds)]) string?]{
Formats @racket[seconds] in UTC at second precision, using the RFC 3339 form
stored in a manifest's @racket[published-at] field.}

@subsection{Signed Manifests}

@racket[update-artifact] values describe one platform artifact using the fields
Expand Down
18 changes: 18 additions & 0 deletions rivet/tests/distribution.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,24 @@
(check-true (channel-accepts-version? 'beta "1.0.0-beta.1"))
(check-true (channel-accepts-version? 'dev "1.0.0-nightly.5"))

(check-equal? (platform-symbol 'windows) 'windows)
(check-equal? (platform-symbol 'macosx) 'macos)
(check-equal? (platform-symbol 'unix) 'linux)
(check-exn #rx"unsupported operating system"
(lambda () (platform-symbol 'plan9)))
(check-equal? (architecture-symbol 'x86_64) 'x64)
(check-equal? (architecture-symbol 'amd64) 'x64)
(check-equal? (architecture-symbol 'aarch64) 'arm64)
(check-equal? (architecture-symbol 'arm64) 'arm64)
(check-exn #rx"unsupported architecture"
(lambda () (architecture-symbol 'riscv64)))
(check-equal? (installer-extension 'zip) ".zip")
(check-equal? (installer-extension 'targz) ".tar.gz")
(check-equal? (installer-extension 'appimage) ".AppImage")
(check-exn #rx"unsupported installer kind"
(lambda () (installer-extension 'unknown)))
(check-equal? (rfc3339-now 0) "1970-01-01T00:00:00Z")

(define ed25519-impl
(get-pk 'eddsa (list libcrypto-factory sodium-factory decaf-factory)))
(define private-key
Expand Down
10 changes: 6 additions & 4 deletions rivet/tests/public-api.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,17 @@
system-adapter-set-tray-menu system-adapter-show-notification
system-adapter? system-capabilities))
(rivet/distribution
(base64-string->bytes bytes->base64-string bytes->ed25519-public-key
(architecture-symbol base64-string->bytes bytes->base64-string bytes->ed25519-public-key
channel-accepts-version? current-install-kind current-update-platform
current-update-architecture download-update ed25519-sign ed25519-verify
execute-install-plan!
execute-platform-installation! fetch-update-manifest install-plan
install-plan-backup-path install-plan-candidate
install-plan-downloaded-path install-plan-install install-plan-restart
install-plan-rollback install-plan? make-install-plan
install-plan-rollback install-plan? installer-extension make-install-plan
manifest->payload-bytes manifest-schema-version payload-bytes->manifest
make-updater platform-installation? platform-installer-policy
prepare-platform-installation read-ed25519-private-key
platform-symbol prepare-platform-installation read-ed25519-private-key
read-ed25519-public-key read-signed-manifest recover-install-plan!
recover-platform-installation! select-update sha256-bytes sha256-file
sha256-file/hex struct:install-plan
Expand All @@ -91,10 +91,12 @@
updater-config-current-version updater-config-expected-key-id
updater-config-maximum-download-bytes updater-config-platform
updater-config-public-key updater-config-rollout-bucket updater-config?
updater-wait updater?
updater-wait updater? rfc3339-now
valid-channel? verify-platform-payload! verify-signed-manifest
verify-update-artifact! version-compare version<=? version<? version=?
version>=? version>? version? write-signed-manifest))
(rivet/distribution/identity
(architecture-symbol installer-extension platform-symbol rfc3339-now))
(rivet/testing
(ephemeral-http-listener fake-request fake-request-body
fake-request-headers fake-request-method fake-request-path fake-request?
Expand Down
Loading