Repository navigation
-
Make cross-platform verification first-class.
raco rivet matrix <report.json>... [--json]folds the per-platformacceptance --json
reports for one commit into a single machine-readable verdict: a required
kind/platform cell is proven only by a passing suite on that platform,
missing platforms are never silently green, and the exit code is green only
when every required cell is proven — so an agent editing on one OS can
still be judged by the other platforms' evidence.docs/product-acceptance.md
now defines the guaranteed / conditional / platform-specific capability
classification, and the maintainer contract states that the local
operating system does not reduce the required verification scope. -
Ship the Windows runtime source list as a machine-consumable property
sheet.platform/windows/rivet-runtime-sources.propsis now the single
author of the host library's<ClCompile>set —rivet-host.vcxproj
imports it, and hosts that still compile Rivet sources directly (the
transitional path until #238 ships prebuilt static libraries) import the
same file instead of hand-copying entries that rot: adding a runtime
translation unit previously broke every downstream Windows host at link
time (the podlensvalid_utf8LNK2001). A guard test keeps the props in
step withruntime/CMakeLists.txt, andplatform/windows/README.md
documents both consumption contracts. -
Fix generated C++ bounded-wait RPCs so each promise yields exactly one
future, reused for both the timeout wait and the result read. Void RPCs now
also fulfill their promise after decoding a successful response; previously
non-Void calls threwfuture_already_retrievedand Void calls always timed
out even when the backend had completed. -
Add executable product acceptance contracts.
raco rivet acceptance --jsonvalidatesrivet-acceptance.rktd, rejects missing kind/platform
coverage, launches each current-platform suite as an executable plus argv
without a shell, enforces bounded time and output, and fails on nonzero exits
or missing evidence.inspect --json, generated projects, agent guidance,
and the native UI extension contract now expose the same definition of done,
so a build-only or unrun platform can no longer be described as accepted. -
Set the Windows title-bar icon from the embedded exe resource. WinUI shows a generic placeholder in the title bar unless the window is handed an icon explicitly, so every shipped host ran with a foreign default glyph while taskbar and shortcuts showed the real brand icon — the icon was compiled into the exe (resource ID 1 since #283) but nothing ever loaded it. The host template now loads it with
LoadIconWand hands it toAppWindow.SetIconat construction; projects without awindows-icondeclaration keep the placeholder. Existing products carry the same lines in their ownMainWindowconstructor (podlens landed it first). -
Add the release gate.
raco rivet generate --checkregenerates every client file in memory and fails when the tree differs — the writer and the gate share one regeneration, so a hand-edited version stamp or a forgotten regeneration (four shipped-repo incidents in one week) cannot merge.raco rivet doctor --releasevalidates in one command and one exit code: generated freshness, the committed schema baseline, hand-written host sources callingfetch-update-manifest/select-update/download-update/fetch-sibling-assetonly with keywords the installed rivet accepts (read viaprocedure-keywordsfrom the live functions, closing the payback#:redirectionsclass), and everyrivet.git#...pin being non-empty and consistent with the installed release (the autarx empty-ref incident). The update API keyword contract is a documented, test-guarded stable surface, anddocs/ci.mdwarns about the emptyURL#footgun. -
Own the Linux runtime layout discovery and ship the GTK main-thread bridge.
rivet::linux_runtime::discover_layout()locatesruntime/*.bootplusres/core.zobeside the executable (an explicit-root overload serves tests),layout_error_hint()is the remediation message hosts display, and the host template consumes the library instead of carrying a private copy — the staged-versus-packaged layout is Rivet's contract, so the code that knows it lives with it. The template also shipsrpc_bridge.hpp:unpackflattens a generatedResult<T>(includingResult<void>) into a value-or-error decided on the reader thread, andpost_to_mainruns the closure on the GTK main loop, freeing it there. A new GTK host starts with the correct pattern instead of rediscovering thread affinity the hard way. -
Single-source user-visible strings.
raco rivet i18nverifies the parity contract ofshared/i18n/{zh,en}.json(flat string-to-string maps with identical key sets; violations name the file and key) and regenerates the embedded host tables (GeneratedI18n.hppon Windows and Linux,GeneratedI18n.swifton macOS). The shipped loaders consult the active language, then the other language, then the key, identically across hosts, so a key can never exist in one host's fallback and not another's. The release gate fails when the parity contract breaks or the embedded tables drift from the shared source; projects without ashared/i18nsource skip the check. -
Generate bounded-wait RPC companions for the Windows and Linux C++ clients. Every RPC gains
name_within(arguments, std::chrono::milliseconds): the completion resolves a promise, the call waits up to the timeout, and a miss cancels the pending request through the existing request table — so long-lived polling UIs stop spawning a detached thread per tick. Void RPCs returnbool; other results returnstd::optional<T>; backend errors still throw from the final get. The naming collision check treats_withinas a generated companion, and the fix removes a latent generator defect the new Void fixture exposed: the Void async completion handler assigned intoResult<void>, which has no value member and made every generated header with a Void RPC uncompilable. -
Add
macos-localizationspackaging support. A(macos-localizations "zh-Hans" "en")declaration (BCP 47 identifiers, validated at project load) emitsCFBundleLocalizationsin Info.plist and stages markerContents/Resources/<lang>.lproj/InfoPlist.stringsfiles beforesign-macos!runs, so AppKit renders the system-provided chrome (About/Quit menus, standard alerts) in the user's language instead of the development region's — replacing per-app plutil injection plus manual re-signing. Projects without the declaration build byte-identical bundles to before. -
Add
raco rivet measurefor host apps.measure sizelists everydist/artifact with its byte count and, with--budget <file>(glob rules, first match wins), exits nonzero when any artifact exceeds its budget so a bundle-size regression fails the build instead of shipping.measure startupruns the launch-survival smoke over the packaged artifact and reports the elapsed window. Per-RPC latency deliberately stays with the integration--benchmarkmode, which measures the real embedded runtime end to end;docs/benchmarks.mddocuments the budget format and that boundary. -
Add the updater scaffolding to
rivet/distribution. A runtime binds the
updater config, destination directory, settings bridge, and injectable
transport hooks;updater-runtime-check!fetches, selects, and drives the
download while a phase box (idle/checking/available/downloading/ downloaded/errorwith percent, message, and downloaded path) feeds the
product'supdate-stateevent. The family silent-check throttle stamp is
now owned here: a stamp is written only when a check settles — an answer
or a definitive failure — andsilent-check-due?reads it through the
product's settings adapter, ending the three hand-rolled timestamp
semantics where one offline launch could silence four hours.
The sticky 0-99 rollout bucket resolves through the same adapter
(resolve-rollout-bucket!): drawn once from OS entropy, recovered on later
runs, redrawn on corrupt stored values, degrading to an unpersisted draw when
the store cannot write. -
Let one update feed carry multiple installables per platform. Artifacts
gain an optionalcomponentdiscriminator (app,cli, ortool) that
participates in the manifest's duplicate-identity rule, writers emit the
legacy shape when no component is set, andupdater-configselects by
component; a config without one keeps matching component-less entries
only.raco rivet release --update-payload <installer>:<glob>[:<component>]
(repeatable) expands project-relative globs into component-tagged feed
entries, so products shipping an app and a CLI — or a portable tar.gz
instead of the zip — stop splitting into two feeds. The
updater-configstruct gains the component field as its last argument. -
Add
fetch-sibling-assettorivet/distributionand the
raco rivet sign-manifestsubcommand. The sibling helper checks the HTTP
status before saving, enforces the declared Content-Length against the
byte limit, verifies an optional expected SHA-256, and streams under the
same connect and idle deadlines as the manifest fetch — replacing the
private per-product sidecar downloads that silently accepted 404 pages.
The subcommand builds a manifest from explicitly named artifacts, signs
it with the distribution signer, verifies the written bytes with the
public half before publishing (deleting the output on failure), and
optionally emits.sha256sidecars next to each artifact, so products
keep only their asset-naming conventions. -
Extend the family boundary to Commerce: online payment orchestration and
credential delivery remain optional product/service concerns and never
become Rivet dependencies. -
Bound the updater's network legs.
fetch-update-manifestruns its
blocking connect inside a custodian-owned worker with configurable
connect and idle deadlines, anddownload-updateenforces a stall
deadline (no bytes for N seconds) plus an optional total cap on the copy
loop, so a black-holed peer raises a retryable error instead of parking
the backend serve thread forever. -
Add the Kotlin device-session client for Android companions. The RVD1
records, the HMAC authentication proof and six-digit pairing-code
transcripts, and the client state machine with its credential-persistence
gate live beside the existing RVT1 runtime, pinned by the shared
device-session-golden fixture now shipped into the Kotlin test resources.
A blocking runner drives the state machine over any byte stream with
injectable clocks, enforces the post-TLS deadline, surfaces pairing codes
without auto-confirming them, and accepts the desktop user's decision from
another thread. Discovery binds the canonical advertisement codec behind
a driver interface so applications supply their own NSD stack while the
browser keeps the canonical-only, bounded, vanished-result semantics.
TLS transport and product integration remain platform work. -
Document the product-level Permit composition boundary: Rivet remains a
framework with general secure storage and update integrity, not a built-in
entitlement, trial, activation, payment, or account system. -
Give the installers a commercial first-run experience. The macOS DMG
switches to the branded drag-to-install layout when the project declares
dmg-background: the volume stages the bundle next to an/Applications
symlink, a read/write image is attached, Finder is scripted to size the
660x400 window, set the background, and place both icons, and the styled
volume converts back to the compressed image. The Windows MSI now authors
the stock install-dir wizard sequence (welcome, license agreement,
install directory, progress, exit) through the UI extension, with a
launch-app checkbox on the finish page driven by the Util extension's
WixShellExec; the license page shows the configuredwindows-license
RTF or renders the repository license text automatically, and optional
windows-installer-bitmapsreplace the wizard artwork. The deb ships
executable postinst/postrm scripts that refresh the desktop database.
All behavior is opt-in per project key; projects without the keys build
byte-identical installers to before. -
Recreate the Windows package destination before copying release payloads, so
build/package/release retries over a dirtydist/tree are idempotent and
cannot retain stale files or fail withcopy-file: destination exists. -
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 therivet-device/1ALPN requirement, the
fail-closedrivet-device._tcpadvertisement through DNS-SD, and a
bounded single-connection listener come together in
rivet::windows::DeviceSessionService, which feeds accepted sockets to
the portablerivet::DeviceSessionHost. X.509 SubjectPublicKeyInfo
extraction joined the portable runtime so companion pins are
byte-identical across platforms. -
Add the portable C++ device-session host driver. Over any
rivet::Transport,rivet::DeviceSessionHostruns the RVD1 preface for
one accepted desktop connection, enforces the post-TLS deadline, surfaces
pairing codes without auto-approving, accepts the desktop user's
confirm/reject decision from another thread with transport writes
serialized against the run loop, waits on the pairing-window clock, and
hands the accepted stream to an application handler only after
acceptance. Deterministic pipe, clock, and multi-thread tests pin the
reconnect, pairing, rejection, timeout, cancellation, and malformed-preface
behavior; the Windows and Linux gateways become thin transports over it. -
Add the portable C++ device-session preface for the Windows and Linux
gateways.rivet/device_session.hppimplements the RVD1 v1 records, the
HMAC authentication proof and six-digit pairing-code transcripts, and the
server-side state machine: reconnect authentication with an
indistinguishable unknown-device challenge and constant-time proof
comparison, dual-confirmation pairing over injected window/attempt and
secure-storage boundaries, canonical scope enforcement, stable reject
codes, and the post-TLS deadline. The shared
device-session-golden.txtvectors plus deterministic codec, transcript,
and state-machine tests pin the contract; TLS transport, listeners, and
discovery remain platform work. -
Move the test suite under the
rivetcollection (rivet/tests/). A
top-leveltests/directory inside a multi-collection package publishes a
generictestscollection that cannot coexist with any other package
doing the same, which the package catalog reports as a conflict. The suite
now follows the family layout used by the other Rivet libraries, and
raco test rivet/tests/is the documented entry point. -
Keep the package build farm green on hosts without desktop conveniences.
Console executables are now resolved from the running installation's bin
directory before PATH, so the build, launch-smoke, and AppImage process
tests run on build farms that invokebin/racketby a relative path.
Digest- and signing-dependent tests skip with a notice where the pinned
libcrypto factory is unavailable, the manifest composer skips without an
Ed25519 provider instead of raising, and CI-wiring checks tolerate packaged
copies that omit dot-directories. -
Add the framework-only Apple session client for iOS companions.
RivetDeviceAppleSessionClientconnects one explicitly selected endpoint
through a pinned reconnect or an explicit provisional-pairing TLS context,
drives theRVD1session client state machine, surfaces the pairing code
without auto-confirming it, persists each new credential through an injected
secure-storage boundary before accepting RVT1, enforces the post-TLS
deadline, and closes without a pairing fallback when authentication is
rejected. Connection, session-client, persistence, and deadline boundaries
are injectable for deterministic reconnect, pairing, persistence-failure,
timeout, cancellation, and malformed-preface tests; connection policy,
background lifecycle, and permission strings stay product-owned. -
Add the framework-only Apple Bonjour browser for iOS companions. Browsing is
explicitly started and stopped, accepts only the canonical bounded Rivet TXT
record, bounds candidates and pending events, reports add/change/remove
lifecycle events, and clears candidates on failure or cancellation.
Discovery remains unauthenticated and never opens a connection; local-network
usage strings and Bonjour declarations stay owned by the consuming
application. -
Connect accepted macOS device transports to a fresh
RivetDeviceSessionServer
throughRivetDeviceAppleSessionHost. The host enforces post-TLS deadlines,
reports pairing codes without auto-approval, switches to RVT1 only after
authenticated acceptance, and performs idempotent disconnect cleanup. -
Repair generated-project package smoke tests after the schema compatibility
gate became automatic. Workflows that intentionally replace the scaffold
backend now explicitly regenerate and verify their reviewed schema baseline
before build/package checks, preserving the gate instead of bypassing it. -
Add the first Apple companion-session security layers. Service-scoped,
device-local Keychain storage now persists pairing credentials on macOS and
iOS, whileRivetDeviceSessionsupplies the boundedRVD1preface codec,
HMAC reconnect authentication, dual-confirmation pairing state machines, and
a credential-persistence gate. Language-neutral golden vectors and
deterministic malformed-input, ordering, timeout, and authentication tests
pin the contract before Network.framework integration. -
Add
RivetDeviceAppleTransportfor the next Apple companion layer. It
configures Network.framework for TLS 1.2+, forward-secret AEAD suites, and
therivet-device/1ALPN,
authenticates either an exact server SPKI SHA-256 pin or an explicit
provisional-pairing identity, extracts canonical SPKI DER from X.509, bounds
connection setup to 30 seconds, and preserves the one-wayRVD1to RVT1
stream transition without exposing a listener or discovery service. -
Add the disabled-by-default macOS device listener and per-install P-256 TLS
identity lifecycle. Private keys and their self-signed certificates stay in
the application Keychain namespace; explicit rotation invalidates the public
pin. Listener admission is capped before transport/session allocation, and
disable, failure, and termination close every accepted connection. Product
enablement remains a separate future step. -
Add privacy-bounded Bonjour advertising to the macOS device listener. The
_rivet-device._tcpservice exists only while the listener is ready, carries
a deterministic 128-byte-bounded TXT record with the session version, a
per-listener ephemeral identifier, and the pairing-window bit, and is removed
before shutdown. No API names, account/device identifiers, filenames, or user
content enter discovery; iOS browsing and product UI remain separate work.