Skip to content

Isolate Connect libp2p runtime from server Netty#37

Merged
robinbraemer merged 4 commits into
connectfrom
codex/connect-libp2p-native-endpoint
Jun 29, 2026
Merged

Isolate Connect libp2p runtime from server Netty#37
robinbraemer merged 4 commits into
connectfrom
codex/connect-libp2p-native-endpoint

Conversation

@robinbraemer

@robinbraemer robinbraemer commented Jun 27, 2026

Copy link
Copy Markdown
Member

Summary

  • load the jvm-libp2p endpoint and tunnel implementation through a Connect-owned child-first runtime boundary
  • keep server-facing Netty/platform integration on the normal plugin classpath so older Paper runtimes do not collide with libp2p's Netty 4.2 stack
  • bootstrap managed libp2p from WatchService response headers so users do not need CONNECT_LIBP2P_* env vars
  • authorize every configured staging/edge peer from bootstrap/register and relay address lists, so anycast-routed proxies can dial the endpoint
  • include endpoint auth type in libp2p registration records and preserve WatchService/WebSocket fallback when libp2p is unavailable
  • fix local-channel address handling for Spigot/Paper libp2p sessions

Why

Paper 1.21.6 exposed a runtime ABI failure while starting endpoint libp2p:
NoSuchMethodError: io.netty.channel.SingleThreadEventLoop.<init>(...).
Catching LinkageError protected plugin startup, but only gave fallback. This PR lets the same older Paper server run endpoint libp2p by isolating libp2p dependencies from the server/plugin classpath.

The staging validation also found an anycast correctness issue: the endpoint authorized only the first configured edge peer. When the endpoint registered through one Fly machine and a player landed on another, the second proxy's libp2p session could be rejected before session_ack. The endpoint now accepts all managed edge/relay peers supplied by the server bootstrap.

Verification

  • ./gradlew build on head b792544a4e2e43e12272378831aabb09f9fa1ac5
  • prior focused checks on this branch:
    • git diff --check
    • ./gradlew -q :core:test --tests 'com.minekube.connect.tunnel.p2p.Libp2pEndpointConfigTest'
    • ./gradlew -q :core:test --tests 'com.minekube.connect.tunnel.p2p.*'
    • ./gradlew -q :core:test --tests '*Libp2pRuntimeBoundaryTest' --tests '*Libp2pRuntimeLoaderTest'
    • ./gradlew -q :core:test --tests '*PeerRegistration*' --tests '*SameStream*' --tests '*TunnelHandlerTest' --tests '*Libp2pTunnelTransportTest' --tests '*EndpointLibp2pHostTest' --tests '*Libp2pEndpointTest' --tests '*Libp2pRuntimeBoundaryTest' --tests '*Libp2pRuntimeLoaderTest'
    • ./gradlew -q :spigot:shadowJar :bungee:shadowJar :velocity:shadowJar :spigot:verifyLibp2pRuntimeIsolation :bungee:verifyLibp2pRuntimeIsolation :velocity:verifyLibp2pRuntimeIsolation

Local E2E evidence

  • Local stack: NATS JetStream, Moxy watch/proxy, Paper 1.21.6 build 48, and Craftless Fabric client against localhost:25566.
  • Paper 1.21.6 with the isolated connect-spigot.jar registered endpoint craftless-local with peer 12D3KooWKGABHVz3McHdC3dbVFw9v9VYyd1azUsfQxUz6vah8rXa and no longer failed on the old SingleThreadEventLoop Netty constructor.
  • Craftless real-client smoke exited successfully. Moxy accepted join session d8vsemehs1b4ja7qhlcg for Player26 with selectedTransport=direct, selectedAddr=/ip4/127.0.0.1/tcp/49238, warmPath=warm, and connectionStatus=success after 298.4095ms.
  • Paper logged Connect tunneled player logged in as Player26, Player26 joined the game, and accepted chat hello from Craftless Fabric smoke. The later disconnect was the smoke client exiting intentionally.

Fly staging evidence

Validated previous fixed head a6e0011763b352c56af91d5a5cae6975dc80cc20 against Fly staging with an offline-mode Paper 1.21.6 endpoint. The later head b792544a4e2e43e12272378831aabb09f9fa1ac5 adds managed bootstrap/no-env-var wiring and passed ./gradlew build locally.

  • Repeated public anycast: four Craftless public joins reached Paper and exited intentionally (Player432, Player198, Player51, Player59). Public routing hit machine 287e6d1ae36e48; Moxy logged libp2p session accepted with selectedTransport=direct, warmPath=warm, and connectionStatus=success.
  • Explicit machine 6832 check: Player141, session d90coiri940s21t52kq0, machine 6832e0da270568, selectedTransport=direct, warmPath=warm, connectionStatus=success, duration 1.34243085s.
  • Explicit machine 287e check: Player213, session d90cov2cinjc207c1en0, machine 287e6d1ae36e48, selectedTransport=direct, warmPath=warm, connectionStatus=success, duration 926.957364ms.
  • 31-minute hold: Craftless controlled Fabric smoke joined public staging as Player327 at 2026-06-28T08:09:16Z, sent marker chat craftless public 31min supervised smoke 080854, held until 2026-06-28T08:40:18Z, and Gradle exited 0 with BUILD SUCCESSFUL in 31m 14s.
  • Final hold Moxy evidence: session d90ddari940s21t52kug on machine 6832e0da270568; libp2p session accepted logged selectedTransport=direct, selected address /ip6/fdaa:1:71d4:a7b:35b:4321:3084:7100/tcp/62140, warmPath=warm, request kind join, protocol java; player joined endpoint completed with connectionStatus=success in 974.490113ms.
  • Final hold Paper evidence: Connect tunneled player logged in as Player327, Player327 joined the game, marker chat at 10:09:17 Europe/Berlin, then the only disconnect at 10:40:18 after the configured hold ended. No Timed out, Read timed out, Internal server connection error, or unexpected Moxy failed before accept occurred for that final session.

Artifacts are recorded in the Moxy readiness plan at docs/superpowers/plans/2026-06-18-connect-libp2p-pr265-readiness.md.

Related

Review follow-up on head c64ada287c952ef3572dbaac2a3560b9068efd44:

  • Expanded child-first runtime loading to all com.minekube.connect.tunnel.p2p implementation classes while keeping parent-facing API wrappers on the plugin classpath.
  • Wired verifyLibp2pRuntimeIsolation into check and made it skip artifacts that intentionally do not contain the libp2p runtime wrappers.
  • Verified with ./gradlew -q :core:test --tests 'com.minekube.connect.tunnel.p2p.Libp2pRuntimeLoaderTest' :spigot:verifyLibp2pRuntimeIsolation :bungee:verifyLibp2pRuntimeIsolation :velocity:verifyLibp2pRuntimeIsolation and ./gradlew build.

@robinbraemer robinbraemer force-pushed the codex/connect-libp2p-native-endpoint branch from 1cabdf0 to 00da2ce Compare June 27, 2026 17:09
@robinbraemer robinbraemer merged commit eb11139 into connect Jun 29, 2026
1 check passed
@robinbraemer robinbraemer deleted the codex/connect-libp2p-native-endpoint branch June 29, 2026 15:02
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