Skip to content

feat(skynet): hold+reuse multihop routes via a yamux skyfwd mux (fix route-death)#3368

Merged
0pcom merged 4 commits into
skycoin:developfrom
0pcom:feat/skynet-route-reuse
Jul 3, 2026
Merged

feat(skynet): hold+reuse multihop routes via a yamux skyfwd mux (fix route-death)#3368
0pcom merged 4 commits into
skycoin:developfrom
0pcom:feat/skynet-route-reuse

Conversation

@0pcom

@0pcom 0pcom commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Problem

The skynet resolving proxy "only worked well for direct routes." Over a multihop route the routing rules expire and the whole route re-sets-up on every reconnect (a multi-second stall), making multihop skynet browsing unusable through a browser-configured SOCKS5 proxy.

Root cause isn't the route type — it's reuse. A rule's 10-min TTL is refreshed only by an open RouteGroup's keepalive, and the native .skynet proxy dialed a fresh route per SOCKS5 connection with no caching (embedded_skynetweb.go), closing it when the connection ended. Direct re-setup is instant; multihop isn't. skysocks-client-lite never had this because it caches + yamux-muxes its route and holds it open — so its multihop rules stay warm.

Fix

Generalize that pattern into a shared primitive:

  • pkg/skyroute.Pool — holds ONE route group per destination PK and yamux-muxes logical connections over it, so the held route's keepalive keeps every hop warm and reconnects reuse it with zero setup. Idle groups are reclaimed after ~10 min (DefaultIdleTTL); a holder that's done (a closing iframe window) can Release one eagerly. Because the route lives on the visor, this needs no browser "page-open" signal — it fixes the external-browser SOCKS5 case and the in-tab iframe case identically.
  • yamux-aware skyfwd server (skyenv.SkyForwardingMuxPort = 59) — one accepted route group carries a yamux session; each stream runs the same ready-byte + ClientMsg handshake + forward as the 1:1 SkyForwardingServerPort, reusing handleServerConn. The route group's peer PK is carried onto each stream (muxPeerConn) so the per-port PK whitelist still works. Additive + version-negotiated: a caller dials the mux port and falls back to the 1:1 port (ErrNoMux, negative-cached) against older visors.
  • routerSkynetDialer dials through the pool for the route path; the direct-transport fast path and explicit source routes are unchanged, and a real route-setup failure is surfaced (only ErrNoMux falls through to the legacy 1:1 dial).

Tests

pkg/skyroute unit tests: route-group reuse (one dial, many streams), ErrNoMux fallback + negative cache, idle reap, eager Release. Full build + vet clean.

Scope / follow-ups

This is Phase 1 of docs/skynet-routing-control-rfc.md (included). Phases 2–3 unify skysocks-lite and skychat's skynet:1 path onto a shared RoutingPolicy and add the iframe routing-control surface. Live validation on a real 2-visor no-direct-transport (forced multihop) path is the recommended acceptance test before/after merge — the unit tests cover the pool↔yamux logic but not a live route group.

0pcom added 4 commits July 3, 2026 17:03
…route-death)

The skynet resolving proxy "only worked well for direct routes": over a multihop
route the routing rules expire and the whole route re-sets-up on every reconnect
(seconds). Root cause — a rule's 10-min TTL is refreshed only by an OPEN
RouteGroup's keepalive, and the native `.skynet` proxy dialed a FRESH route per
SOCKS5 connection with no caching (embedded_skynetweb.go), closing it when the
connection ended. Direct re-setup is instant; multihop isn't. skysocks-lite never
had this because it caches+yamux-muxes its route and holds it open.

Generalize that pattern:

- **pkg/skyroute.Pool** — holds ONE route group per destination PK and yamux-muxes
  logical connections over it, so the held route's keepalive keeps every hop warm
  and reconnects reuse it with zero setup. Idle groups are reclaimed after ~10 min
  (DefaultIdleTTL); a holder that's done (e.g. a closing iframe window) can Release
  one eagerly. Because the route lives on the visor, this needs no browser
  "page-open" signal — it fixes the external-browser SOCKS5 case and the in-tab
  iframe case identically.
- **skyfwd mux server** (skyenv.SkyForwardingMuxPort = 59) — one accepted route
  group carries a yamux session; each stream runs the SAME ready-byte + ClientMsg
  handshake + forward as the 1:1 SkyForwardingServerPort, reusing handleServerConn.
  The route group's peer PK is carried onto each stream (muxPeerConn) so the
  per-port PK whitelist still works. Additive + version-negotiated: a caller dials
  the mux port and falls back to the 1:1 port (ErrNoMux, negative-cached) against
  older visors.
- **routerSkynetDialer** dials through the pool for the route path (direct
  transport + explicit source routes are unchanged); a real route-setup failure is
  surfaced, only ErrNoMux falls through to the legacy 1:1 dial.

Unit-tested (pkg/skyroute): route-group reuse, ErrNoMux fallback + negative cache,
idle reap, eager Release. Full build + vet clean.

Design + the wider unified routing-control plan (skysocks-lite + skychat as the
other two consumers of RoutingPolicy, the iframe control surface, and a voice-chat
assessment) in docs/skynet-routing-control-rfc.md. This is Phase 1.
…ing server

Wires the REAL yamux mux forwarding server (serveSkyForwardingMuxSession) to the
REAL skyroute.Pool over one in-memory conn and asserts the core property: 5 logical
connections reuse ONE route group (dial count == 1), each reaching a registered
service through the real handleServerConn ready-byte + ClientMsg handshake +
dispatch. A RouteGroup already satisfies net.Conn and runs under yamux in production
(skysocks-lite), so this covers the mux-server↔pool integration the skyroute unit
tests (which mock the far end) don't — without standing up the full transport/
route-setup stack. Also asserts muxPeerConn carries the route group's peer PK
(keeps the per-port whitelist working over the muxed path).
@0pcom 0pcom merged commit 7ecd1d8 into skycoin:develop Jul 3, 2026
3 of 5 checks passed
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