Skip to content

chore: rivetkit core/napi/typescript follow up review#4702

Draft
NathanFlurry wants to merge 1 commit into04-22-chore_fix_remaining_issues_with_rivetkit-corefrom
04-22-chore_rivetkit_core_napi_typescript_follow_up_review
Draft

chore: rivetkit core/napi/typescript follow up review#4702
NathanFlurry wants to merge 1 commit into04-22-chore_fix_remaining_issues_with_rivetkit-corefrom
04-22-chore_rivetkit_core_napi_typescript_follow_up_review

Conversation

@NathanFlurry
Copy link
Copy Markdown
Member

Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

@railway-app
Copy link
Copy Markdown

railway-app Bot commented Apr 23, 2026

🚅 Deployed to the rivet-pr-4702 environment in rivet-frontend

Service Status Web Updated (UTC)
website 😴 Sleeping (View Logs) Web Apr 23, 2026 at 10:11 am
frontend-cloud ❌ Build Failed (View Logs) Web Apr 23, 2026 at 9:55 am
ladle ✅ Success (View Logs) Web Apr 23, 2026 at 9:55 am
frontend-inspector ❌ Build Failed (View Logs) Web Apr 23, 2026 at 9:54 am
kitchen-sink ❌ Build Failed (View Logs) Web Apr 23, 2026 at 9:53 am
mcp-hub ✅ Success (View Logs) Web Apr 23, 2026 at 3:55 am

Copy link
Copy Markdown
Member Author

NathanFlurry commented Apr 23, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@claude
Copy link
Copy Markdown

claude Bot commented Apr 23, 2026

PR 4702 Code Review — Adversarial Review Synthesis

The full adversarial review findings (F1-F42) with challenger verdicts are in this comment. The per-diff code review is in the comment below.

Key findings verified as REAL in this PR:

  • F3: clean run exit fix (task.rs) - FIXED in this PR
  • F12: Registry.handler()/serve() throw - spec written (handler-serve-restoration.md), fix pending
  • F14: package exports subpaths removed - ./db and ./db/drizzle restored in this PR
  • F18: actor-lifecycle state in napi not core - tracked, not yet fixed
  • F19: inspector logic duplicated in TypeScript - tracked, not yet fixed
  • F21: 50ms polling loop in native.ts - tracked, not yet fixed
  • F32: module-level actor-keyed maps in native.ts - tracked, not yet fixed
  • F34: ActorContext.key type widened - tracked, not yet fixed
  • F35: sql on ctx but ./db dropped - ./db/drizzle restored in this PR
  • F36: ContextOf type helpers removed - tracked for restoration
  • F38: inline use in test - tracked
  • F41: dead BARE code audit - pending audit
  • F42: inline mod tests in src/ - policy added to CLAUDE.md

@NathanFlurry NathanFlurry changed the base branch from 04-22-chore_fix_remaining_issues_with_rivetkit-core to graphite-base/4702 April 23, 2026 04:02
@NathanFlurry NathanFlurry force-pushed the 04-22-chore_rivetkit_core_napi_typescript_follow_up_review branch from 689feea to 527f1d2 Compare April 23, 2026 04:02
@NathanFlurry NathanFlurry changed the base branch from graphite-base/4702 to 04-22-chore_fix_remaining_issues_with_rivetkit-core April 23, 2026 04:02
@NathanFlurry NathanFlurry force-pushed the 04-22-chore_rivetkit_core_napi_typescript_follow_up_review branch from 527f1d2 to ef109dd Compare April 23, 2026 08:02
@NathanFlurry NathanFlurry force-pushed the 04-22-chore_fix_remaining_issues_with_rivetkit-core branch from a639b2e to d7cd40d Compare April 23, 2026 08:02
@claude
Copy link
Copy Markdown

claude Bot commented Apr 23, 2026

PR #4702 Code Review

This PR ships several meaningful improvements: a new CoreServerlessRuntime in Rust, hibernatable WebSocket restoration via the envoy, a Drizzle-ORM SQLite adapter, the lifecycle fix for clean run() exits, and a collection of smaller fixes. The overall direction is sound. Issues below range from blockers to nits.


Bugs / Correctness

1. Clean-exit path skips channel/deadline cleanup

When clean_exit && lifecycle == Started, the new early-return skips reset_sleep_timer, state_save_deadline, inspector_serialize_state_deadline, and close_actor_event_channel. Those four lines now only execute on the panicking/erroring path. If the actor can sleep between run() returning and the engine Stop arriving, the live sleep timer could trigger an unintended transition. Confirm this is intentional.

2. Registry.handler() has a CancellationToken / event-listener leak if request.arrayBuffer() rejects

If arrayBuffer() rejects (upstream disconnect), the function throws before removeEventListener / cancelToken.cancel() are reached. Wrap in try/finally.

3. configureServerlessPool silently swallows errors

Catches all errors and logs without re-throwing. The caller fire-and-forgets via configureServerlessPoolPromise, so a registration failure is invisible. Surface a clear operator-visible signal that the process is misconfigured.

4. Token security: fail-open when token is unset (serverless.rs, validate_start_headers)

When configured_token is None the token check is skipped -- any caller can hit /start. The spec requires fail-closed by default with an explicit serverless.unauthenticated: true opt-out. This is a security gap for deployments that omit the token field expecting downstream enforcement.

5. CORS headers emitted on all routes including /start -- contradicts spec

serverless.rs:172-175 applies cors_headers to every response. The spec explicitly says /api/rivet/* is server-to-server only, Access-Control-Allow-* headers must NOT be emitted, and OPTIONS should return 405. The current implementation does the opposite: it emits full CORS headers on all routes including OPTIONS (which returns 204 with CORS rather than 405).

6. ensure_serverless_runtime has a TOCTOU race between concurrent first requests

registry.rs:211-244 acquires parking_lot::Mutex to check serverless_runtime, drops it, then in a separate lock acquisition takes from inner. Two concurrent requests arriving before initialization both pass the if let Some(runtime) = check as None, both try guard.take() in sequence, and the second gets registry_already_serving_error(). Use tokio::sync::OnceCell for atomic one-shot initialization.

7. DEFAULT_MAX_INCOMING_MESSAGE_SIZE client-side 64 KiB cap mismatches the 1 MiB server-side default

actor-conn.ts sets a 64 KiB incoming message cap while the server default is 1 MiB. Causes client-side incoming_too_long errors below the server's actual limit. Document why these two limits differ, or align them.


CLAUDE.md Violations

8. anyhow::bail! in new code (runner_config.rs, serverless.rs)

CLAUDE.md: "prefer .context() over the anyhow! macro." The bail! calls at runner_config.rs:61-66 and serverless.rs:379 format error details into the message string. Use .with_context() chains. The bail! in ensure_envoy especially warrants a typed RivetError since it crosses the actor dispatch path.

9. Inline #[cfg(test)] mod tests in serverless.rs violates Rust test layout rule

Per the testing reference, Rust tests for rivetkit-core must live under tests/, not inline in src/*.rs. Move the inline tests at serverless.rs:617-758 to rivetkit-rust/packages/rivetkit-core/tests/modules/serverless.rs.

10. Unused Promise import in rivetkit-napi/src/registry.rs

use napi::bindgen_prelude::{Buffer, Promise}; -- Promise does not appear in any new function signature in this file and will produce a dead-code warning.

11. Structured logging violations in runner_config.rs

CLAUDE.md: "Do not format parameters into the main message. Use structured fields." The bail! calls at runner_config.rs:61-66 and 90-95 format pool_name, status, and response_body into the message string. Use tracing::error! with named fields instead.


Performance / Design

12. Split lock-then-await pattern in ensure_serverless_runtime

registry.rs:207-245 releases the sync lock before into_serverless_runtime(...).await, but this split is the root of the TOCTOU race in issue 6. Replace with tokio::sync::OnceCell<CoreServerlessRuntime> so initialization is atomic and awaitable without a sync lock.

13. TokioMutex<Option<EnvoyHandle>> held across start_envoy network I/O

ensure_envoy (serverless.rs:372-406) holds a TokioMutex across start_envoy(...).await, which performs a network handshake. Every concurrent /start before the envoy is initialized queues behind this lock during a full network round-trip. Use tokio::sync::OnceCell<EnvoyHandle> or a watch channel so initialization happens once and all waiters unblock atomically.


Security

14. /metadata exposes clientToken to unauthenticated callers

serverless.rs:332-335 includes clientToken in the /metadata response without any auth guard on that route. If clientToken grants any privileged access, either require authentication or exclude the token from the unauthenticated metadata endpoint.

15. x-rivet-pool-name not validated against DNS subdomain rules

CLAUDE.md "Naming + Data Conventions" requires pool names to match DNS subdomain format. The current code passes any non-empty pool_name directly to start_envoy, producing a confusing engine error rather than an actionable 400. Add explicit validation in parse_start_headers.

16. cors_headers reflects the request Origin header verbatim with credentials

serverless.rs:452-458 sets access-control-allow-origin to whatever origin the request sends, combined with access-control-allow-credentials: true. Reflecting an arbitrary Origin back is an open CORS reflection that allows a browser to send credentialed cross-origin requests from any origin. Given that the spec already says these headers should not be emitted on server-to-server routes, remove the CORS block entirely from the serverless handler.


Test Coverage Gaps

17. Clean-run-exit test covers Sleep but not Destroy

Add a test case that sends StopReason::Destroy after a clean run() return and asserts onDestroy fires exactly once.

18. No TypeScript-level integration test for the /start SSE stream

Rust-level tests cover routing but there are no TS-level tests for Registry.handler() covering the full SSE stream, backpressure, cancellation, or CORS header propagation.

19. startCount assertion loosened from toBe(2) to toBeGreaterThanOrEqual(2) without a root-cause fix

CLAUDE.md: driver test flakes must be root-caused and fixed, not papered over with loosened assertions. Restore the precise assertion after fixing the underlying sequencing issue.

20. Hibernatable WebSocket ack state failure unresolved

The driver test records: hibernatable-websocket-protocol: FAIL - replays only unacked indexed websocket messages after sleep and wake. Ack state was undefined instead of index 1. Per CLAUDE.md, unannotated test failures must be root-caused and fixed or annotated with a tracking ticket.


Minor / Nits

21. register.ts drops CJS compatibility detection

Confirm the new ESM-only path works under all targeted runtimes (Cloudflare Workers, etc.), or document the limitation.

22. isServerlessStartRequest path normalization may diverge from Rust route_path

TypeScript strips all leading/trailing slashes; Rust preserves the leading slash. If basePath is /, TypeScript produces an empty string and may route incorrectly. Add an explicit test for this edge case.

23. Deprecated workflow loop aliases have no runtime warning

historyEvery, commitInterval, historyKeep are silently read. Emit a console.warn when a deprecated field is detected so users know to migrate.


Summary

Severity Count Top items
Bug / correctness 7 CancellationToken leak, fail-open auth, CORS/OPTIONS spec mismatch, TOCTOU in runtime init
CLAUDE.md violation 4 anyhow::bail!, inline test module, unused import, logging format
Performance / design 2 TOCTOU in serverless init, mutex held across network I/O
Security 4 Fail-open auth, CORS reflection with credentials, unauthenticated metadata token, pool-name bypass
Test coverage 4 Destroy path, TS handler SSE, assertion loosening, hibernatable WS ack failure
Minor / nits 3 CJS loader, path normalization, deprecated field warning

Highest-priority before merging: Issue 4 (fail-open auth when token unset), Issue 5 (CORS emitted on server-to-server endpoint), Issue 6 (TOCTOU race in ensure_serverless_runtime), Issue 16 (open CORS reflection with credentials), Issue 19 (assertion loosening masking a race), Issue 20 (hibernatable WS ack failure unresolved).

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 23, 2026

Preview packages published to npm

Install with:

npm install rivetkit@pr-4702

All packages published as 0.0.0-pr.4702.a179a52 with tag pr-4702.

Engine binary is shipped via @rivetkit/engine-cli on linux-x64-musl, linux-arm64-musl, darwin-x64, and darwin-arm64. Windows users should use the release installer or set RIVET_ENGINE_BINARY.

Docker images:

docker pull rivetdev/engine:slim-a179a52
docker pull rivetdev/engine:full-a179a52
Individual packages
npm install rivetkit@pr-4702
npm install @rivetkit/react@pr-4702
npm install @rivetkit/rivetkit-napi@pr-4702
npm install @rivetkit/workflow-engine@pr-4702

@github-actions
Copy link
Copy Markdown
Contributor

Preview packages published to npm

Install with:

npm install rivetkit@pr-4702

All packages published as 0.0.0-pr.4702.d108c6a with tag pr-4702.

Engine binary is shipped via @rivetkit/engine-cli on linux-x64-musl, linux-arm64-musl, darwin-x64, and darwin-arm64. Windows users should use the release installer or set RIVET_ENGINE_BINARY.

Docker images:

docker pull rivetdev/engine:slim-d108c6a
docker pull rivetdev/engine:full-d108c6a
Individual packages
npm install rivetkit@pr-4702
npm install @rivetkit/react@pr-4702
npm install @rivetkit/rivetkit-napi@pr-4702
npm install @rivetkit/workflow-engine@pr-4702

@NathanFlurry NathanFlurry force-pushed the 04-22-chore_rivetkit_core_napi_typescript_follow_up_review branch from 9284ef8 to f7dae0a Compare April 23, 2026 09:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant