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
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,23 @@ concurrency:
cancel-in-progress: true

jobs:
racket-minimum:
name: Racket 9.0 compatibility
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up minimum supported Racket CS
uses: Bogdanp/setup-racket@2466913449df77df2bad149d1f2fc4e1ea4795dd # v1.15
with:
architecture: x64
distribution: full
variant: CS
version: '9.0'
- name: Install Rivet
run: raco pkg install --auto --no-docs --name rivet --link "$PWD"
- name: Test
run: raco test tests/

racket:
name: Racket core
runs-on: ubuntu-24.04
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

- Removed the misleading `rivet-type` runtime descriptors from the pre-1.0
public surface. Schema type identifiers are declaration grammar, while Keel
is the canonical runtime-inspection model.
- Added an exact public-export contract and a Racket 9.0 CI lane so accidental
API drift and minimum-version regressions fail before merge.

- Make Keel's validated `app-schema` the canonical in-memory input to Swift,
C++, and Kotlin client generation. Rivet now keeps only a fail-closed
Keel-to-RVT1 type adapter instead of reconstructing a parallel codegen schema
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Racket CS runs on a dedicated runtime thread. Native UI code never manipulates R

The embedding model is inspired by [Noise](https://github.com/Bogdanp/Noise), but Rivet makes the runtime contract, protocol, code generation, and lifecycle cross-platform instead of Swift-first.

See [architecture](docs/architecture.md), [agent-native development](docs/agent-native.md), [protocol](docs/protocol.md), [embedding](docs/embedding.md), [typed device communication](docs/device-communication.md), the unimplemented [network device channel design](docs/network-device-channel.md), [Android architecture](docs/android.md), [project configuration](docs/configuration.md), [system services](docs/system-services.md), [Linux display policy](docs/linux-display.md), [production roadmap](docs/production-roadmap.md), [large payloads, pagination, and progress](docs/large-payloads.md), [Windows native window interop](docs/windows-native-window.md), [release and updates](docs/release-and-updates.md), [diagnostics](docs/diagnostics.md), [package verification](docs/package-verification.md), and [production signing](docs/production-signing.md) for the details.
See [architecture](docs/architecture.md), [API stability](docs/api-stability.md), [agent-native development](docs/agent-native.md), [protocol](docs/protocol.md), [embedding](docs/embedding.md), [typed device communication](docs/device-communication.md), the unimplemented [network device channel design](docs/network-device-channel.md), [Android architecture](docs/android.md), [project configuration](docs/configuration.md), [system services](docs/system-services.md), [Linux display policy](docs/linux-display.md), [production roadmap](docs/production-roadmap.md), [large payloads, pagination, and progress](docs/large-payloads.md), [Windows native window interop](docs/windows-native-window.md), [release and updates](docs/release-and-updates.md), [diagnostics](docs/diagnostics.md), [package verification](docs/package-verification.md), and [production signing](docs/production-signing.md) for the details.

## Platform status

Expand Down Expand Up @@ -203,6 +203,10 @@ For publisher-signed output, use `raco rivet package --production` with the plat

Arguments and results are validated at the Racket boundary. Supported schema values currently include `String`, `Int64`, `Bool`, `Bytes`, `Void`, `Any`, `(List T)`, `(Optional T)`, named values declared with `define-record`, and closed cases declared with `define-enum`. Records generate Swift structs under `RivetTypes`, C++ structs, and Kotlin data classes; Enums generate Swift raw-value enums under `RivetTypes`, C++ `enum class` values, and Kotlin `enum class` values. The Swift namespace prevents domain names such as `Task` and `Result` from shadowing standard-library types across the application module.

These type names form Rivet's declaration grammar; they are not runtime Racket
values. Runtime tooling should inspect the immutable Keel schema returned by
`current-backend-app-schema` rather than depending on a second descriptor API.

### Events

```racket
Expand Down Expand Up @@ -332,6 +336,10 @@ The maintained Taskboard also has platform-native [UI end-to-end gates](docs/nat
- **Publisher credentials remain application-specific** — Rivet automates Authenticode and Developer ID/notarization flows, but certificates, PFX passwords, and Apple notary profiles are intentionally supplied by the application/CI environment rather than stored by Rivet.
- **The public API is still pre-1.0** — protocol compatibility is versioned, but higher-level APIs may still evolve.

Maintainers should follow the [API stability policy](docs/api-stability.md);
the repository snapshots every public export and tests the full Racket suite
against the minimum supported Racket release.

## Roadmap

- [x] **Phase 1** — RVT1 protocol in Racket, C++, and Swift
Expand Down
6 changes: 5 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Racket CS 运行在独立 runtime 线程。原生 UI 代码不会直接操作 Ra

嵌入模型受到 [Noise](https://github.com/Bogdanp/Noise) 的启发,但 Rivet 把 runtime contract、协议、代码生成和生命周期都做成平台无关的统一核心,而不是以 Swift 为中心。

深入设计见 [架构](docs/architecture.md)、[Agent-native 开发](docs/agent-native.md)、[协议](docs/protocol.md)、[嵌入](docs/embedding.md)、[类型安全设备通信](docs/device-communication.md)、尚未实现的[网络设备通道设计](docs/network-device-channel.md)、[Android 架构](docs/android.md)、[项目配置](docs/configuration.md)、[系统服务](docs/system-services.md)、[Linux 显示协议策略](docs/linux-display.md)、[生产晋级路线图](docs/production-roadmap.md)、[大载荷与分页](docs/large-payloads.md)、[Windows 原生窗口 interop](docs/windows-native-window.md)、[发布与更新](docs/release-and-updates.md)、[诊断](docs/diagnostics.md)、[发布物验证](docs/package-verification.md) 与 [生产签名](docs/production-signing.md)。
深入设计见 [架构](docs/architecture.md)、[API 稳定性策略](docs/api-stability.zh-CN.md)、[Agent-native 开发](docs/agent-native.md)、[协议](docs/protocol.md)、[嵌入](docs/embedding.md)、[类型安全设备通信](docs/device-communication.md)、尚未实现的[网络设备通道设计](docs/network-device-channel.md)、[Android 架构](docs/android.md)、[项目配置](docs/configuration.md)、[系统服务](docs/system-services.md)、[Linux 显示协议策略](docs/linux-display.md)、[生产晋级路线图](docs/production-roadmap.md)、[大载荷与分页](docs/large-payloads.md)、[Windows 原生窗口 interop](docs/windows-native-window.md)、[发布与更新](docs/release-and-updates.md)、[诊断](docs/diagnostics.md)、[发布物验证](docs/package-verification.md) 与 [生产签名](docs/production-signing.md)。

## 平台支持状态

Expand Down Expand Up @@ -201,6 +201,8 @@ raco rivet release

参数与返回值都会在 Racket 边界做类型校验。目前支持 `String`、`Int64`、`Bool`、`Bytes`、`Void`、`Any`、`(List T)`、`(Optional T)`、通过 `define-record` 声明的具名 Record,以及通过 `define-enum` 声明的封闭取值。Record 会生成位于 `RivetTypes` 下的 Swift struct、C++ struct 与 Kotlin data class;Enum 会生成位于 `RivetTypes` 下的 Swift raw-value enum、C++ `enum class` 与 Kotlin `enum class`。Swift 命名空间可避免 `Task`、`Result` 等领域名称在整个应用模块中遮蔽标准库类型。

这些类型名属于 Rivet 的声明语法,并不是 Racket 运行时值。运行时工具应检查 `current-backend-app-schema` 返回的不可变 Keel schema,而不是依赖第二套 descriptor API。

### Event

```racket
Expand Down Expand Up @@ -328,6 +330,8 @@ CI 会验证 Racket、C++、Swift 与 Kotlin 协议实现,在三个桌面平
- **正式发布凭据仍属于应用自身** —— Rivet 已自动化 Authenticode、Developer ID、notarization 流程,但证书、PFX 密码、Apple notary profile 会由应用/CI 环境注入,不由 Rivet 保存。
- **Public API 仍处于 pre-1.0** —— 协议有版本控制,但高层 API 仍可能继续调整。

维护者应遵循 [API 稳定性策略](docs/api-stability.zh-CN.md):仓库会快照全部公开导出,并使用最低支持的 Racket 版本运行完整 Racket 测试套件。

## 路线图

- [x] **Phase 1** —— Racket / C++ / Swift 三端 RVT1 协议
Expand Down
23 changes: 23 additions & 0 deletions docs/api-stability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Rivet API stability

Rivet is pre-1.0, but its public surface is deliberate. Every phase-0 export
from `rivet`, `rivet/app-info`, `rivet/backend`, `rivet/keel`,
`rivet/protocol`, `rivet/resources`, `rivet/system`, `rivet/distribution`, and
`rivet/testing` is covered by `tests/public-api.rkt`.

An API change must therefore be explicit:

1. change the implementation and documentation together;
2. update the public export snapshot intentionally;
3. record the migration in `CHANGELOG.md`; and
4. pass the full Racket suite on both the minimum supported Racket release and
the current development baseline.

Before 1.0, incompatible changes are allowed when they remove a design trap or
create a materially clearer long-term contract. After 1.0, removing or changing
a public binding requires a major release. Additions remain minor releases, and
compatible fixes remain patch releases.

Schema type identifiers such as `String`, `Int64`, `Bool`, and `(List T)` are
syntax accepted by Rivet declaration forms. They are not exported runtime
values. Tools inspect the canonical Keel `app-schema` instead.
21 changes: 21 additions & 0 deletions docs/api-stability.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Rivet API 稳定性

Rivet 尚未到 1.0,但它的公开接口必须是有意设计的。`rivet`、
`rivet/app-info`、`rivet/backend`、`rivet/keel`、`rivet/protocol`、
`rivet/resources`、`rivet/system`、`rivet/distribution` 与 `rivet/testing`
的全部 phase-0 导出都由 `tests/public-api.rkt` 固定。

因此,API 变更必须是显式行为:

1. 同时修改实现与文档;
2. 有意更新公开导出快照;
3. 在 `CHANGELOG.md` 记录迁移方式;
4. 使用最低支持的 Racket 版本和当前开发基线运行完整 Racket 测试套件。

在 1.0 之前,如果不兼容变更能够消除设计陷阱或建立明显更清晰的长期契约,
则可以接受。1.0 之后,删除或改变公开绑定需要发布 major 版本;新增兼容 API
属于 minor 版本,兼容修复属于 patch 版本。

`String`、`Int64`、`Bool`、`(List T)` 等 Schema 类型标识符,是 Rivet
声明形式接受的语法,并不是导出的运行时值。工具应检查规范的 Keel
`app-schema`。
6 changes: 2 additions & 4 deletions rivet/main.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
"backend.rkt"
"keel.rkt"
"protocol.rkt"
"resources.rkt"
"types.rkt")
"resources.rkt")

(provide (all-from-out "app-info.rkt")
(all-from-out "backend.rkt")
(all-from-out "keel.rkt")
(all-from-out "protocol.rkt")
(all-from-out "resources.rkt")
(all-from-out "types.rkt"))
(all-from-out "resources.rkt"))
15 changes: 6 additions & 9 deletions rivet/scribblings/rivet.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -91,20 +91,17 @@ packaged dependency-closure, and clean-machine checks.

@subsection{RPCs, Events, State, Records, and Enums}

@defstruct*[rivet-type ([name symbol?])]{Represents a public schema type descriptor.}

@defthing[Void rivet-type?]
@defthing[Bool rivet-type?]
@defthing[Int64 rivet-type?]
@defthing[String rivet-type?]
@defthing[Bytes rivet-type?]
@defthing[List rivet-type?]

State descriptors and the registry records behind RPCs, Events, Records, and
Enums are intentionally opaque. Applications declare them with the forms in
this section; code generators consume the immutable @racket[backend-schema]
snapshot instead of depending on mutable runtime representation.

The type names in declarations are schema grammar, not Racket variables or
runtime descriptor values. They are interpreted only inside the declaration
forms below. This keeps application code honest: use ordinary Racket
predicates and contracts for runtime values, and use the generated Keel schema
when a tool needs to inspect the application contract.

@defform[(define-rpc (name [arg : type] ... : result-type) body ...)]{
Defines a Racket procedure named @racket[name] and registers it as an RPC for
generated Swift, C++, and Kotlin clients. Supported schema types are @racket[String],
Expand Down
21 changes: 0 additions & 21 deletions rivet/types.rkt

This file was deleted.

123 changes: 123 additions & 0 deletions tests/public-api.rkt
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#lang racket/base

(require racket/list
rackunit)

;; This is an intentional compatibility boundary, not a documentation index.
;; Every phase-0 export is listed so that adding an accidental helper is caught
;; just as reliably as removing or renaming an existing binding.
(define expected-public-api
'((rivet
(app-build app-display-name app-identifier app-info app-info-build
app-info-display-name app-info-identifier app-info-name
app-info-release-channel app-info-version app-info? app-name
app-release-channel app-version backend-schema
backend-schema->app-schema current-app-info current-backend-app-schema
current-resource-root current-rivet-diagnostic-sink decode-value
define-enum define-event define-record define-rpc define-state
emit-event! encode-value enum-case enum-schema event-schema frame
frame-id frame-payload frame-type frame? max-frame-payload-size
max-value-depth max-value-nodes message:cancel message:error
message:event message:hello message:request message:response
message:shutdown protocol-version read-frame record-ref record-schema
resource-path resource-root rpc-schema serve serve-fds state-ref
state-schema state-set! state? struct:app-info struct:frame write-frame))
(rivet/app-info
(app-build app-display-name app-identifier app-info app-info-build
app-info-display-name app-info-identifier app-info-name
app-info-release-channel app-info-version app-info? app-name
app-release-channel app-version current-app-info struct:app-info))
(rivet/backend
(backend-schema current-rivet-diagnostic-sink define-enum define-event
define-record define-rpc define-state emit-event! enum-case enum-schema
event-schema record-ref record-schema rpc-schema serve serve-fds
state-ref state-schema state-set! state?))
(rivet/keel
(backend-schema->app-schema current-backend-app-schema))
(rivet/protocol
(decode-value encode-value frame frame-id frame-payload frame-type frame?
max-frame-payload-size max-value-depth max-value-nodes message:cancel
message:error message:event message:hello message:request
message:response message:shutdown protocol-version read-frame
struct:frame write-frame))
(rivet/resources
(current-resource-root resource-path resource-root))
(rivet/system
(acquire-single-instance! autostart-enabled? call-with-crash-reporting
current-rivet-crash-reporter current-rivet-log-sink
current-system-adapter install-crash-hook! make-settings-store
register-activation-handler! rivet-log secure-store-ref
secure-store-remove! secure-store-set! set-autostart!
set-settings-store-data! set-settings-store-lock!
set-settings-store-path! set-tray-menu! settings-ref settings-remove!
settings-set! settings-snapshot settings-store settings-store-data
settings-store-lock settings-store-path settings-store?
show-system-notification! struct:settings-store struct:system-adapter
system-adapter system-adapter-acquire-single-instance
system-adapter-autostart-enabled system-adapter-capabilities
system-adapter-install-crash-hook system-adapter-name
system-adapter-register-activation-handler
system-adapter-secure-store-ref system-adapter-secure-store-remove
system-adapter-secure-store-set system-adapter-set-autostart
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
channel-accepts-version? current-install-kind current-update-platform
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
manifest->payload-bytes manifest-schema-version payload-bytes->manifest
platform-installation platform-installation-commit
platform-installation-health-check platform-installation-plan
platform-installation? platform-installer-policy
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 struct:platform-installation
struct:update-artifact struct:update-candidate struct:update-manifest
struct:updater-config update-artifact update-artifact-architecture
update-artifact-arguments update-artifact-installer
update-artifact-platform update-artifact-sha256 update-artifact-size
update-artifact-url update-artifact? update-candidate
update-candidate-artifact update-candidate-manifest update-candidate?
update-manifest update-manifest-application-id update-manifest-artifacts
update-manifest-build update-manifest-channel
update-manifest-minimum-version update-manifest-previous-version
update-manifest-published-at update-manifest-rollback-allowed?
update-manifest-rollout update-manifest-version update-manifest?
updater-config updater-config-application-id
updater-config-architecture updater-config-channel
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?
valid-channel? verify-platform-payload! verify-signed-manifest
verify-update-artifact! version-compare version<=? version<? version=?
version>=? version>? version? write-signed-manifest))
(rivet/testing
(ephemeral-http-listener fake-request fake-request-body
fake-request-headers fake-request-method fake-request-path fake-request?
serve-http! struct:fake-request))))

(define (phase-zero-export-names exports)
(for*/list ([phase-entry (in-list exports)]
#:when (equal? (car phase-entry) 0)
[export-entry (in-list (cdr phase-entry))])
(car export-entry)))

(define (public-export-names module-path)
(dynamic-require module-path #f)
(define-values (variable-exports syntax-exports)
(module->exports module-path))
(sort (remove-duplicates
(append (phase-zero-export-names variable-exports)
(phase-zero-export-names syntax-exports)))
symbol<?))

(for ([entry (in-list expected-public-api)])
(define module-path (car entry))
(define expected (sort (cadr entry) symbol<?))
(test-case (format "public exports: ~a" module-path)
(check-equal? (public-export-names module-path) expected)))
Loading
Loading