Skip to content

feat(tunnel-native): embedded relay-tunnel receiver via napi-rs (WIP) - #780

Draft
slinkydeveloper wants to merge 2 commits into
restatedev:mainfrom
slinkydeveloper:relay
Draft

feat(tunnel-native): embedded relay-tunnel receiver via napi-rs (WIP)#780
slinkydeveloper wants to merge 2 commits into
restatedev:mainfrom
slinkydeveloper:relay

Conversation

@slinkydeveloper

Copy link
Copy Markdown
Collaborator

WIP / draft. TypeScript/napi-rs sibling of the Java embedded relay-tunnel receiver. Reuses the same Rust engine as restatedev/sdk-shared-core#94 (branch relay, tunnel feature), consumed as a git dependency; swap to a crates.io release once that ships.

What

A new, separate package @restatedev/restate-sdk-tunnel-native that serves a Restate SDK deployment as a relay receiver using the embedded native engine (restate-sdk-shared-core, tunnel feature) via napi-rs — the same Rust engine the Java SDK drives over FFM. The engine dials the relay on its own tokio runtime and bridges each forwarded request over a loopback socket into a local node:http2 server, so SDK request dispatch — and therefore both the codegen SDK and the default promise API — is untouched. The napi boundary is control-plane only (start/status/stop); no per-request data crosses it.

This is not the pure-JS @restatedev/restate-sdk-tunnel package — the two are complementary and will be reconciled later.

Layout (packages/libs/restate-sdk-tunnel-native)

  • Cargo.toml + build.rs + src/lib.rs: a napi crate exposing a #[napi] RelayTunnel class (start(configJson) / status() / stop()) over the engine's relay::{Config, Engine, Handle}. Depends on the shared-core relay branch with features = ["tunnel"] (same git dep as the sdk-java cdylib).
  • src/index.ts: serveTunnel({ services, relay }) — wires at the shared createEndpointHandler({ services }) layer (accepts services from either programming model, exactly like serve()), boots a loopback-private node:http2 server on 127.0.0.1:0, and points the native engine at that port. Returns { localPort, status(), stop() }.
  • Dual ESM/CJS via tsdown; the napi .cjs loader + .node stay external and gitignored (built locally via napi build).

napi-rs is set up from scratch here (the repo previously only had wasm-pack for the VM).

Verification

  • napi build → a loadable host .node; RelayTunnel.start/status/stop work across the FFI (start→running, fast graceful stop, idempotent, bad-config throws).
  • The wrapper type-checks against the real SDK + binding types and builds (dual ESM/CJS).
  • Full round-trip smoke test passes: a fake relay (Node h2 client via role-flip) → native engine → serveTunnel's http2 server running a real restate.service (promise API) → forwarded /health200 "OK" → back, with the /whoami handshake (env/tunnel/api_key + auto instance_id) confirmed.

Deferred ("later" — packaging)

  • Multi-platform prebuild matrix + optionalDependencies publishing + CI cross-compile (the standard napi-rs release flow).
  • Restate Cloud /_/start-tunnel mode (the engine is /whoami-only, same as Java).
  • Reconciliation with the pure-JS restate-sdk-tunnel package.
  • Swapping the shared-core git dependency for a crates.io release.

Design/rationale: development/relay-receiver-napi-plan.md.

🤖 Generated with Claude Code

slinkydeveloper and others added 2 commits August 12, 2026 10:33
New, separate package `@restatedev/restate-sdk-tunnel-native` that serves a
Restate SDK deployment as a relay *receiver* using the embedded native engine
(restate-sdk-shared-core, `tunnel` feature) via napi-rs — the same Rust engine
the Java SDK drives over FFM. The engine dials the relay on its own tokio
runtime and bridges each forwarded request over a loopback socket into a local
node:http2 server, so SDK request dispatch — and therefore BOTH the codegen SDK
and the default promise API — is untouched. The napi boundary is control-plane
only (start/status/stop); no per-request data crosses it.

Layout (packages/libs/restate-sdk-tunnel-native):
  * Cargo.toml + build.rs + src/lib.rs: a napi crate exposing a `#[napi]`
    RelayTunnel class (start(configJson) / status() / stop()) over the engine's
    relay::{Config, Engine, Handle}. Depends on the shared-core `relay` branch
    with features=["tunnel"] (same git dep as the sdk-java cdylib).
  * src/index.ts: `serveTunnel({ services, relay })` — wires at the shared
    createEndpointHandler({ services }) layer (works with both programming
    models), boots a loopback-private node:http2 server on 127.0.0.1:0, and
    points the native engine at that port. Returns { localPort, status, stop }.
  * Dual ESM/CJS via tsdown; the napi `.cjs` loader + `.node` stay external and
    gitignored (built locally via `napi build` — the multi-platform prebuild
    matrix + publishing is deferred).

napi-rs is set up from scratch here (the repo previously only had wasm-pack for
the VM). Verified: the addon builds + loads; the wrapper type-checks against the
real SDK types and builds; a full round-trip smoke test (fake relay h2 client ->
native engine -> serveTunnel's http2 server running a real promise-API service
-> forwarded /health -> 200 -> back, /whoami confirmed) passes.

Design/rationale: development/relay-receiver-napi-plan.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The package's `_build` (tsc + tsdown) ran inside the repo-wide `turbo run
_build`, but CI never runs `napi build`, so the napi-generated `../index.cjs`
(+ its `.d.cts`) are absent and `tsc` failed with TS2307 — cascading to every
job that builds first.

An embedded native addon can't be built in CI without a native build step
(which doesn't exist yet — the prebuild matrix is deferred). So drop the turbo
task scripts (`_build`/`_check:types`/`_test`/`lint`) from this package: turbo
then skips it in the shared build/verify runs. Keep manual `build:native` +
`build:wrapper` for local/prebuild use. The package rejoins the pipeline once
its native CI lands.

Verified: `pnpm build` (turbo _build over ./packages/libs/*) and
`turbo run _check:types lint` are green with the package skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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