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
661 changes: 661 additions & 0 deletions .agent/specs/rivetkit-core-wasm-support.md

Large diffs are not rendered by default.

392 changes: 392 additions & 0 deletions .agent/specs/rivetkit-runtime-boundary-cleanup.md

Large diffs are not rendered by default.

456 changes: 456 additions & 0 deletions .agent/supabase-functions-kitchen-sink-serverless-e2e.ts

Large diffs are not rendered by default.

420 changes: 420 additions & 0 deletions .agent/workerd-kitchen-sink-serverless-e2e.ts

Large diffs are not rendered by default.

58 changes: 55 additions & 3 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ jobs:
fi

# ---------------------------------------------------------------------------
# build — matrix of 10 native/engine artifacts (11 on release for Windows)
# build — matrix of native/engine artifacts
# ---------------------------------------------------------------------------
build:
needs: [context]
Expand Down Expand Up @@ -232,6 +232,45 @@ jobs:
path: artifacts/${{ matrix.artifact }}
if-no-files-found: error

# ---------------------------------------------------------------------------
# build-wasm — wasm package artifact built in parallel with native artifacts
# ---------------------------------------------------------------------------
build-wasm:
needs: [context]
name: "Build rivetkit-wasm"
if: needs.context.outputs.is_fork != 'true'
runs-on: depot-ubuntu-24.04-8
permissions:
contents: read
steps:
- uses: actions/checkout@v4
with:
lfs: ${{ needs.context.outputs.trigger == 'release' }}
- run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: pnpm
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
target: wasm32-unknown-unknown
rustflags: ""
- uses: Swatinem/rust-cache@v2
with:
shared-key: "rivetkit-wasm-publish"
cache-on-failure: true
- name: Install wasm package dependencies
run: pnpm install --frozen-lockfile --filter=@rivetkit/rivetkit-wasm
- name: Build wasm package
run: pnpm --filter=@rivetkit/rivetkit-wasm build
- name: Upload wasm package artifact
uses: actions/upload-artifact@v4
with:
name: wasm-package
path: rivetkit-typescript/packages/rivetkit-wasm/pkg
if-no-files-found: error

# ---------------------------------------------------------------------------
# docker-images — per-arch runtime images pushed to Docker Hub
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -298,12 +337,13 @@ jobs:
# publish — npm publish + R2 upload + Docker manifest + release tail
# ---------------------------------------------------------------------------
publish:
needs: [context, build, docker-images]
needs: [context, build, build-wasm, docker-images]
name: "Publish"
if: |
!cancelled() &&
needs.context.outputs.is_fork != 'true' &&
needs.build.result == 'success' &&
needs.build-wasm.result == 'success' &&
needs.docker-images.result == 'success'
runs-on: depot-ubuntu-24.04-8
permissions:
Expand Down Expand Up @@ -343,6 +383,16 @@ jobs:
path: engine-artifacts
pattern: engine-*
merge-multiple: true
- name: Download wasm package artifact
uses: actions/download-artifact@v4
with:
name: wasm-package
path: rivetkit-typescript/packages/rivetkit-wasm/pkg
- name: Validate wasm package artifact
run: |
test -f rivetkit-typescript/packages/rivetkit-wasm/pkg/rivetkit_wasm.js
test -f rivetkit-typescript/packages/rivetkit-wasm/pkg/rivetkit_wasm.d.ts
test -f rivetkit-typescript/packages/rivetkit-wasm/pkg/rivetkit_wasm_bg.wasm

- name: Place native binaries in platform packages
run: |
Expand Down Expand Up @@ -397,7 +447,9 @@ jobs:

# ---- build TypeScript packages (turbo dep graph picks up native) ----
- name: Build TypeScript packages
run: pnpm build -F rivetkit -F '@rivetkit/*' -F '!@rivetkit/shared-data' -F '!@rivetkit/engine-frontend' -F '!@rivetkit/mcp-hub' -F '!@rivetkit/rivetkit-napi'
env:
SKIP_WASM_BUILD: "1"
run: pnpm build -F rivetkit -F '@rivetkit/*' -F '!@rivetkit/shared-data' -F '!@rivetkit/engine-frontend' -F '!@rivetkit/mcp-hub' -F '!@rivetkit/rivetkit-napi' -F '!@rivetkit/rivetkit-wasm'

# ---- shared publish (runs for all triggers) ----
- name: Finalize package versions for publish
Expand Down
15 changes: 15 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Design constraints, invariants, and reference commands for the Rivet monorepo. F

**Always use versioned BARE (`vbare`) instead of raw `serde_bare` for any persisted or wire-format encoding unless explicitly told otherwise.** Raw `serde_bare::to_vec` / `from_slice` has no version header, so any future schema change forces hand-rolled `LegacyXxx` fallback structs. `vbare::OwnedVersionedData` plus a versioned `*.bare` schema is the standard pattern. Acceptable raw-bare exceptions: ephemeral in-memory encodings that never cross a process boundary or hit disk, and wire formats whose protocol version is coordinated out-of-band (e.g. an HTTP path like `/v{PROTOCOL_VERSION}/...` or another channel that pins both peers to one schema per call).

- Avoid raw `f64` fields in vbare protocol schemas that use hashable maps; generated Rust derives `Eq`/`Hash`, so encode floats as fixed bytes or an ordered wrapper.

When talking about "Rivet Actors" make sure to capitalize "Rivet Actor" as a proper noun and lowercase "actor" as a generic noun.

## Commands
Expand Down Expand Up @@ -90,6 +92,13 @@ docker-compose up -d
## Dependency Management

- Prefer the Tokio-shaped APIs from `antiox` (`antiox/sync/mpsc`, `antiox/task`, etc.) over ad hoc Promise queues, custom channel wrappers, or event-emitter coordination.
- `rivet-envoy-client` transport features are mutually exclusive; native builds use the default `native-transport`, while wasm builds must set `default-features = false` and enable `wasm-transport`.
- `rivet-envoy-client` wasm WebSocket code lives behind `target_arch = "wasm32"` with a native-host `wasm-transport` stub so feature checks do not compile browser APIs on developer machines.
- `rivetkit-core` wasm builds use `--no-default-features --features wasm-runtime,sqlite-remote`; keep native process and runner-config HTTP code behind `native-runtime`.
- Core-owned lifecycle tasks in `rivetkit-core` should spawn through `RuntimeSpawner` so native builds use Send-capable tasks and wasm builds use local tasks.
- `rivet-envoy-client::async_counter::AsyncCounter` is the shared HTTP request counter type consumed by core sleep logic; do not pull `rivet-util` into core for that counter.
- For `wasm32-unknown-unknown` Rust checks, use target-specific minimal Tokio plus `getrandom/js` and `uuid/js`; scan production dependencies with `cargo tree -e normal` so dev-dependencies do not create false native-dependency hits.
- Use `scripts/cargo/check-rivetkit-core-wasm.sh` as the canonical wasm gate for `rivetkit-core`; it checks the wasm build, scans native dependency leaks, and verifies native transport/runtime features fail on wasm.
- The high-level `rivetkit` crate stays a thin typed wrapper over `rivetkit-core` and re-exports shared transport/config types instead of redefining them.
- When `rivetkit` needs ergonomic helpers on a `rivetkit-core` type it re-exports, prefer an extension trait plus `prelude` re-export instead of wrapping and replacing the core type.
- `engine/sdks/*/api-*` are auto-generated SDK outputs; update the source API schema and regenerate them instead of editing them by hand.
Expand All @@ -98,6 +107,7 @@ docker-compose up -d

- Core tests that touch the `_RIVET_TEST_INSPECTOR_TOKEN` env override must share a process-wide lock with startup tests that assert inspector-token initialization side effects; otherwise parallel `cargo test` runs can flip `init_inspector_token(...)` between the env-override no-op path and the KV-backed path.
- For the fast static/http/bare driver verifier, pass only the files listed under `## Fast Tests` in `.agent/notes/driver-test-progress.md`; `tests/driver/*.test.ts` also pulls in slow-suite files and gives bogus gate failures.
- Wasm host smoke tests can drive `buildNativeFactory` through `WasmCoreRuntime` fake bindings to cover actor callbacks, KV, state serialization, remote SQLite routing, and NAPI import boundaries without checked-in wasm-pack output.
- When moving Rust inline tests out of `src/`, keep a tiny source-owned `#[cfg(test)] #[path = "..."] mod tests;` shim so the moved file still has private module access without widening runtime visibility. Prefer a dedicated moved-test file per source module; reusing stale shared `tests/modules/*.rs` files can silently rot against private APIs and explode once you wire them back in.
- Tracing assertions on spawned Rust futures should bind an explicit `tracing::Dispatch` with `.with_subscriber(...)` on the spawned future; thread-local `set_default(...)` can miss the real logs in full async suite runs.

Expand All @@ -108,6 +118,9 @@ docker-compose up -d
- Native SQLite VFS recent-page preload hints are actor-side Rust state surfaced by `NativeDatabase::snapshot_preload_hints()`; persist and consume them through runtime/envoy wiring, not JS APIs.
- SQLite VFS file handles must enforce their reader or writer role; reader-owned handles fail closed on mutating callbacks.
- Native SQLite single-statement work should route through the native execute path; keep `exec` as the multi-statement compatibility path.
- Pegboard-envoy remote SQL execution should use `rivetkit-sqlite::database::open_database_from_engine` instead of direct `rusqlite` calls so native routing policy stays shared.
- Pegboard-envoy remote SQL executor caches should use `Arc<OnceCell<NativeDatabaseHandle>>` values so first-use initialization stays lazy and single-flight per `(actor_id, sqlite_generation)`.
- Sent remote SQL requests must fail with `sqlite.remote_indeterminate_result` on WebSocket disconnect; only unsent remote SQL may be sent after reconnect.
- Native SQLite manual transactions keep an idle writer open until autocommit returns; route subsequent work through the writer instead of reader classification.
- Native SQLite read mode may hold multiple read-only connections, while write mode must hold exactly one writable connection and no readers; TypeScript must not be the routing policy boundary.
- For NAPI bridge wiring (TSF callback layout, cancellation tokens, `#[napi(object)]` rules), see `docs-internal/engine/napi-bridge.md`.
Expand Down Expand Up @@ -139,6 +152,7 @@ When the user asks to track something in a note, store it in `.agent/notes/` by
- rivetkit-napi must be pure bindings. If code would be duplicated by a future V8 runtime, it belongs in rivetkit-core instead.
- rivetkit-napi serves through `CoreRegistry` + `NapiActorFactory`; do not reintroduce the deleted `BridgeCallbacks` JSON-envelope envoy path or `startEnvoy*Js` exports.
- NAPI `ActorContext.sql()` returns `JsNativeDatabase` directly; do not reintroduce a standalone `SqliteDb` wrapper export.
- TypeScript runtime adapters expose `CoreRuntime` from `rivetkit/src/registry/runtime.ts`; keep raw `@rivetkit/rivetkit-napi` and future `@rivetkit/rivetkit-wasm` imports inside `src/registry/*-runtime.ts`.
- rivetkit (Rust) is a thin typed wrapper. If it does more than deserialize, delegate to core, and serialize, the logic should move to rivetkit-core.
- rivetkit (TypeScript) owns only: workflow engine, agent-os, client library, Zod schema validation for user-defined types, and actor definition types.
- Errors use universal `RivetError` (group/code/message/metadata) at all boundaries. No custom error classes in TS.
Expand Down Expand Up @@ -277,6 +291,7 @@ When the user asks to track something in a note, store it in `.agent/notes/` by
- Only add design constraints, invariants, and non-obvious rules that shape how new code should be written. Do not add general trivia, current implementation wiring, KV-key layouts, module organization, API signatures, ephemeral migration state, or anything a reader can learn by reading the code. That content belongs in module doc-comments, `docs-internal/`, or `.claude/reference/`.
- When the user asks to update any `CLAUDE.md`, add one-line bullet points only, or add a new section containing one-line bullet points.
- Architectural internals and runtime wiring belong in `docs-internal/engine/`. Agent-procedural guides (test-harness gotchas, build troubleshooting, docs-sync tables) belong in `.claude/reference/`. Link them from the [Reference Docs](#reference-docs) index below instead of inlining.
- Every directory that has a `CLAUDE.md` must also have an `AGENTS.md` symlink pointing to it (`ln -s CLAUDE.md AGENTS.md` from the same directory). When creating a new `CLAUDE.md`, create the symlink in the same change.

## Reference Docs

Expand Down
60 changes: 58 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ members = [
"rivetkit-rust/packages/actor-persist",
"rivetkit-rust/packages/rivetkit-core",
"rivetkit-rust/packages/shared-types",
"rivetkit-typescript/packages/rivetkit-napi"
"rivetkit-rust/packages/rivetkit-sqlite",
"rivetkit-rust/packages/rivetkit-sqlite-types",
"rivetkit-typescript/packages/rivetkit-napi",
"rivetkit-typescript/packages/rivetkit-wasm"
]

[workspace.package]
Expand Down Expand Up @@ -122,7 +125,6 @@ members = [
regex = "1.4"
replace_with = "0.1.8"
rstest = "0.26.1"
rusqlite = { version = "0.32.1", features = [ "bundled" ] }
rustls-pemfile = "2.2.0"
rustyline = "15.0.0"
scc = "3.6.12"
Expand Down Expand Up @@ -539,6 +541,7 @@ members = [

[workspace.dependencies.rivet-envoy-client]
path = "engine/sdks/rust/envoy-client"
default-features = false

[workspace.dependencies.rivet-envoy-protocol]
path = "engine/sdks/rust/envoy-protocol"
Expand All @@ -558,6 +561,9 @@ members = [
[workspace.dependencies.rivetkit-sqlite]
path = "rivetkit-rust/packages/rivetkit-sqlite"

[workspace.dependencies.rivetkit-sqlite-types]
path = "rivetkit-rust/packages/rivetkit-sqlite-types"

[workspace.dependencies.rivetkit-core]
path = "rivetkit-rust/packages/rivetkit-core"

Expand Down
3 changes: 2 additions & 1 deletion docker/build/darwin-arm64.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ COPY . .
RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \
export NODE_OPTIONS="--max-old-space-size=8192" && \
export SKIP_NAPI_BUILD=1 && \
export SKIP_WASM_BUILD=1 && \
pnpm install --ignore-scripts && \
VITE_APP_API_URL="${VITE_APP_API_URL}" VITE_FEATURE_FLAGS="${VITE_FEATURE_FLAGS}" npx turbo build -F @rivetkit/engine-frontend; \
fi
Expand Down Expand Up @@ -62,7 +63,7 @@ RUN --mount=type=cache,id=cargo-registry-darwin-arm64,target=/usr/local/cargo/re
fi && \
mkdir -p /artifacts && \
if [ "$BUILD_TARGET" = "engine" ]; then \
cargo build --bin rivet-engine $CARGO_FLAG --target aarch64-apple-darwin && \
cargo build -p rivet-engine --bin rivet-engine $CARGO_FLAG --target aarch64-apple-darwin && \
cp target/aarch64-apple-darwin/$PROFILE_DIR/rivet-engine /artifacts/rivet-engine-aarch64-apple-darwin; \
elif [ "$BUILD_TARGET" = "rivetkit-napi" ]; then \
cd rivetkit-typescript/packages/rivetkit-napi && \
Expand Down
3 changes: 2 additions & 1 deletion docker/build/darwin-x64.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ COPY . .
RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \
export NODE_OPTIONS="--max-old-space-size=8192" && \
export SKIP_NAPI_BUILD=1 && \
export SKIP_WASM_BUILD=1 && \
pnpm install --ignore-scripts && \
VITE_APP_API_URL="${VITE_APP_API_URL}" VITE_FEATURE_FLAGS="${VITE_FEATURE_FLAGS}" npx turbo build -F @rivetkit/engine-frontend; \
fi
Expand Down Expand Up @@ -62,7 +63,7 @@ RUN --mount=type=cache,id=cargo-registry-darwin-x64,target=/usr/local/cargo/regi
fi && \
mkdir -p /artifacts && \
if [ "$BUILD_TARGET" = "engine" ]; then \
cargo build --bin rivet-engine $CARGO_FLAG --target x86_64-apple-darwin && \
cargo build -p rivet-engine --bin rivet-engine $CARGO_FLAG --target x86_64-apple-darwin && \
cp target/x86_64-apple-darwin/$PROFILE_DIR/rivet-engine /artifacts/rivet-engine-x86_64-apple-darwin; \
elif [ "$BUILD_TARGET" = "rivetkit-napi" ]; then \
cd rivetkit-typescript/packages/rivetkit-napi && \
Expand Down
3 changes: 2 additions & 1 deletion docker/build/linux-arm64-gnu.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ COPY . .
RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \
export NODE_OPTIONS="--max-old-space-size=8192" && \
export SKIP_NAPI_BUILD=1 && \
export SKIP_WASM_BUILD=1 && \
pnpm install --ignore-scripts && \
VITE_APP_API_URL="${VITE_APP_API_URL}" VITE_FEATURE_FLAGS="${VITE_FEATURE_FLAGS}" npx turbo build -F @rivetkit/engine-frontend; \
fi
Expand Down Expand Up @@ -49,7 +50,7 @@ RUN --mount=type=cache,id=cargo-registry-linux-arm64-gnu,target=/usr/local/cargo
fi && \
mkdir -p /artifacts && \
if [ "$BUILD_TARGET" = "engine" ]; then \
cargo build --bin rivet-engine $CARGO_FLAG --target aarch64-unknown-linux-gnu && \
cargo build -p rivet-engine --bin rivet-engine $CARGO_FLAG --target aarch64-unknown-linux-gnu && \
cp target/aarch64-unknown-linux-gnu/$PROFILE_DIR/rivet-engine /artifacts/rivet-engine-aarch64-unknown-linux-gnu; \
elif [ "$BUILD_TARGET" = "rivetkit-napi" ]; then \
cd rivetkit-typescript/packages/rivetkit-napi && \
Expand Down
Loading
Loading