Skip to content

feat(engine,rivetkit): align local dev with envoy-mode design#4653

Open
brittianwarner wants to merge 1 commit into04-14-break_this_up_wip_patchesfrom
04-14-feat_envoy_mode_alignment
Open

feat(engine,rivetkit): align local dev with envoy-mode design#4653
brittianwarner wants to merge 1 commit into04-14-break_this_up_wip_patchesfrom
04-14-feat_envoy_mode_alignment

Conversation

@brittianwarner
Copy link
Copy Markdown

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 14, 2026

This PR was not deployed automatically as @brittianwarner does not have access to the Railway project.

In order to get automatic PR deploys, please add @brittianwarner to your workspace on Railway.

@brittianwarner brittianwarner mentioned this pull request Apr 14, 2026
11 tasks
Copy link
Copy Markdown
Author

brittianwarner commented Apr 14, 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 14, 2026

PR Review: feat(engine,rivetkit): align local dev with envoy-mode design

Overview

This PR renames manager* config properties to http* (e.g. serveManagerserveHttp, managerPorthttpPort) and introduces a structured runtime discriminated union (mode: "envoy" | "serverless") to centralize spawn/mode decisions. It also registers the ${poolName}-native-db runner config in both local dev and serverless paths, changes the default HTTP port from 6420 to 8080, and adds child-process signal handlers to avoid orphaned engine processes.


Breaking Changes Not Called Out

  • Default port changed from 6420 to 8080 without a deprecation notice or migration guide. Any user relying on the old default will silently break.
  • Flat config fields renamed (managerPort, serveManager, managerBasePath, managerHost) with no backward-compatibility shim at the schema level. Users with existing configs will silently lose their settings. The PR checklist box for breaking changes is unchecked.

Specific Issues

1. Broken inspector URL when httpPort is undefined (runtime/index.ts:155)

When running in envoy mode with no HTTP server, httpPort is undefined, so 0 is passed via this.httpPort ?? 0. If config.inspector.defaultEndpoint and config.endpoint are both unset, getInspectorUrl will produce http://127.0.0.1:0/ui/ which is a non-functional URL. The old code returned undefined and skipped printing the inspector URL entirely. The comment says the URL falls back through getInspectorUrl's own chain, but that fallback to config.endpoint only works when an endpoint is actually configured.

2. Duplicated mode-resolution logic

The resolvedMode / isProduction / hasEndpoint logic is computed independently in both RegistryConfigSchema.transform() (config/index.ts) and Registry.start() (registry/index.ts). If these two copies drift, the PR's own decision matrix comment will become wrong for one of them.

3. Signal-handler accumulation in ensureEngineProcess

If ensureEngineProcess is called multiple times (e.g. in test suites), process.once calls accumulate. Node.js will warn with MaxListenersExceededWarning. The exit handler is also never removed if the child exits normally before the parent does. Consider storing and removing the handlers when the child exits cleanly.

4. Duplicate error for production without endpoint

A Zod ctx.addIssue is added in config/index.ts for the production-without-endpoint case, and a throw new Error() with a nearly identical message is also added in registry/index.ts. A user will see two separate error messages for the same misconfiguration. One authoritative location should handle this.

5. configurePool set to undefined on a non-optional field (config/index.ts)

The serverless branch of the discriminated union defines configurePool: ConfigurePoolSchema without .optional(). Assigning undefined there bypasses Zod validation and may surface confusing runtime errors downstream rather than a clean config error at parse time.

6. Hardcoded -native-db suffix in two places

The suffix is hardcoded in both runtime/index.ts and serverless/configure.ts. If EngineActorDriver ever changes the pool-name convention, both sites will silently diverge. Extracting it to a shared constant would prevent this.

7. start() continues silently after serverless-mode warning

When resolvedMode is "serverless", a warning is logged but execution continues into the engine-spawn path with no explanation of what will actually happen. If calling start() in serverless mode is genuinely wrong, it should throw. If it is allowed, the warning should describe what the code will do so the caller can reason about the outcome.


Minor

  • Comment style (runtime/index.ts:151-155): the block uses parenthetical notation — per project conventions, prefer full sentences without parenthetical fragments.
  • Deno serve function (serve.ts): serveDeno uses config.httpPort directly instead of the already-resolved httpPort parameter, which means the port-collision fallback from findFreePort is not applied in Deno. The node and bun paths correctly use the parameter. Worth fixing while the rename is in progress.
  • @vite-ignore addition (websocket.ts): good fix for the Vite bundler warning.
  • Child-process cleanup in ensureEngineProcess: the intent is correct and valuable for the tsx --watch use case. The signal-handler accumulation issue above is the main concern to address.
  • Test update (tests/registry-constructor.test.ts) correctly mirrors the renamed fields.

Summary

The rename from manager to http and the new runtime discriminated union are reasonable design improvements. The main risks are: the silent breaking-change on the httpPort default, the broken inspector-URL fallback when httpPort is 0, duplicated error handling, and signal-handler accumulation. The PR checklist should be updated to indicate this is a breaking change.

@github-actions
Copy link
Copy Markdown
Contributor

Preview packages published to npm

Install with:

npm install rivetkit@pr-4653

All packages published as 0.0.0-pr.4653.7179278 with tag pr-4653.

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-7179278
docker pull rivetdev/engine:full-7179278
Individual packages
npm install rivetkit@pr-4653
npm install @rivetkit/react@pr-4653
npm install @rivetkit/rivetkit-native@pr-4653
npm install @rivetkit/workflow-engine@pr-4653

Implements Nathan's envoy-mode dev architecture in rivetkit while keeping
all engine-side changes out of scope (reverted from earlier attempt).

## Decision matrix (enforced at schema + start())

                 | Default (dev) | NODE_ENV=prod | mode=envoy override
   spawn_engine  |      y        | error if no   |         n
                 |               | RIVET_ENDPOINT|
   mode          |    envoy      |   serverless  |       envoy

Localdev always uses envoy unless the user explicitly sets
`runtime.mode`. RIVET_ENDPOINT alone does NOT flip to serverless in dev
— an envoy-mode app can still connect to a remote engine.

## Config shape: discriminated `runtime` block

Per Nathan's sketch, mode-specific config lives inside `runtime`:

  setup({
    use: { counter },
    runtime: { mode: "envoy", spawnEngine: true, poolName: "..." }
  })

  // OR
  setup({
    use: { counter },
    runtime: {
      mode: "serverless",
      configurePool: { url: "..." },
      publicEndpoint: "...",
    }
  })

TypeScript narrows available fields per mode — a serverless-only field
like `configurePool` is a type error inside `mode: "envoy"`, and vice
versa. Legacy top-level `serverless`/`envoy` blocks are kept as
`@deprecated` back-compat; the transform normalizes runtime → legacy so
every existing downstream reader of `config.serverless.*` /
`config.envoy.*` keeps working unchanged.

## manager → http rename

58 identifiers renamed across the rivetkit package per Nathan's TODO:
`serveManager` → `serveHttp`, `managerPort` → `httpPort`,
`managerHost` → `httpHost`, `managerBasePath` → `httpBasePath`,
`configuredManagerPort` → `configuredHttpPort`. Prose updated in
comments, log messages, and landing pages. Actor-internal
Manager classes (ConnectionManager, QueueManager, etc.) left untouched —
unrelated to the HTTP router concept.

## Operational fixes

* `runtime/index.ts` + `serverless/configure.ts` — `ensureLocalRunnerConfig`
  and `configureServerlessPool` now register both the main pool and the
  native-DB pool (`${poolName}-native-db`). Fixes `actor_ready_timeout`
  for any actor using `rivetkit/db` whose native envoy couldn't register
  with the engine.

* `src/engine-process/mod.ts` — install exit/SIGINT/SIGTERM/SIGHUP
  handlers that SIGTERM the spawned engine child. Prevents orphaned
  engine processes across `tsx --watch` restarts that used to hold
  port 6420 and confuse the next dev run.

* `src/common/websocket.ts` — add `/* @vite-ignore */` to the dynamic
  `ws` import so Vite stops warning about unresolvable dynamic imports
  in browser builds.

* `src/test/mod.ts` — DTS type cast for `closable` (fixes `tsup`
  DTS build: `closeIdleConnections` / `closeAllConnections` exist on
  `http.Server` but not `Http2Server`).

* `src/inspector/utils.ts` — `getInspectorUrl` prefers the engine
  endpoint so the banner's Inspector line reads 6420 (where the engine
  serves `/ui/` natively) even when a legacy manager port is reported.

* `src/registry/config/index.ts` — `httpPort` default 6420 → 8080 so it
  never collides with the engine's fixed port 6420.

* `runtime/index.ts` — `#printWelcome` computes the inspector URL even
  when no HTTP server is running (envoy-mode dev case).

## Engine-side: unchanged

Earlier experiment extending engine's `/metadata` to emit registry
info is fully reverted per Nathan's feedback. Client SDKs work against
the engine's minimal `/metadata` response because the client only
consumes `clientEndpoint` and logs `runtime`/`version` — all other
fields are optional at runtime.

## Outstanding TODOs (Nathan-owned)

* remove "runner" terminology codebase-wide
* dispatch `registry.start()` to `startServerless` when mode=serverless
  (currently warns and proceeds as envoy — requires access to user's
  HTTP framework for auto-mount)
* collapse legacy `config.serverless.*` / `config.envoy.*` reads into
  `config.runtime.*` throughout the codebase, then delete the
  `@deprecated` schema blocks
* mounting-router setup for dev & prod (TBD)
@brittianwarner brittianwarner force-pushed the 04-14-feat_envoy_mode_alignment branch from ee1c9b3 to 1bb26f5 Compare April 14, 2026 23:19
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