From 6c62c14e2cff9873ddb818e94f06c0ae95570736 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Sat, 16 May 2026 10:34:28 +0300 Subject: [PATCH] Wire gated QUIC fabric listener --- .../rap-node-agent/cmd/rap-node-agent/main.go | 118 +++++++++++++++++- .../cmd/rap-node-agent/main_test.go | 8 ++ .../rap-node-agent/internal/config/config.go | 5 + .../internal/config/config_test.go | 5 + .../internal/hostagent/config.go | 3 + .../internal/hostagent/docker.go | 4 + .../internal/hostagent/linux.go | 2 + .../internal/hostagent/profile.go | 8 ++ .../internal/hostagent/update.go | 2 + .../internal/hostagent/windows.go | 2 + .../DISTRIBUTED_FABRIC_NODE_PROTOCOL_PLAN.md | 4 + 11 files changed, 160 insertions(+), 1 deletion(-) diff --git a/agents/rap-node-agent/cmd/rap-node-agent/main.go b/agents/rap-node-agent/cmd/rap-node-agent/main.go index b4fcbe1..0e3f93b 100644 --- a/agents/rap-node-agent/cmd/rap-node-agent/main.go +++ b/agents/rap-node-agent/cmd/rap-node-agent/main.go @@ -2,9 +2,15 @@ package main import ( "context" + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" "encoding/json" "fmt" "log" + "math/big" "net" "net/http" "os" @@ -364,6 +370,9 @@ type syntheticMeshState struct { ListenerRuntimeConfig config.Config ListenerHandler *dynamicHTTPHandler StopListener func() + QUICFabricServer *mesh.QUICFabricServer + QUICFabricListenAddr string + QUICFabricError string ConfigLoadError string } @@ -778,6 +787,7 @@ func startSyntheticMeshEndpoint(ctx context.Context, _ context.CancelFunc, cfg c listenerCfg := meshListenerRuntimeConfig(cfg, loadedConfig.MeshListener) listenerReport, stopListener := startSyntheticMeshHTTPServer(ctx, listenerCfg, identity, dynamicListenerHandler, len(peerEndpoints), len(routes), gateEnabled, runtimeEnabled) vpnFabricSessionPeers := mesh.NewFabricSessionPeerManager() + quicFabricServer, quicFabricAddr, quicFabricErr := startQUICFabricEndpoint(ctx, cfg, identity) return &syntheticMeshState{ Runtime: runtime, Routes: routes, @@ -813,6 +823,9 @@ func startSyntheticMeshEndpoint(ctx context.Context, _ context.CancelFunc, cfg c ListenerRuntimeConfig: listenerCfg, ListenerHandler: dynamicListenerHandler, StopListener: stopListener, + QUICFabricServer: quicFabricServer, + QUICFabricListenAddr: quicFabricAddr, + QUICFabricError: errorString(quicFabricErr), ConfigLoadError: errorString(err), }, stopListener, nil } @@ -1124,6 +1137,8 @@ func meshListenerConfigKey(cfg config.Config) string { strings.TrimSpace(cfg.MeshRegion), fmt.Sprintf("%t", cfg.MeshProductionForwardingEnabled), fmt.Sprintf("%t", cfg.VPNFabricSessionTransportEnabled), + fmt.Sprintf("%t", cfg.MeshQUICFabricEnabled), + strings.TrimSpace(cfg.MeshQUICFabricListenAddr), }, "|") } @@ -1149,6 +1164,68 @@ func bindSyntheticMeshListener(cfg config.Config) (net.Listener, string, bool, e return nil, "", false, err } +func startQUICFabricEndpoint(ctx context.Context, cfg config.Config, identity state.Identity) (*mesh.QUICFabricServer, string, error) { + if !cfg.MeshQUICFabricEnabled { + return nil, "", nil + } + if strings.TrimSpace(cfg.MeshQUICFabricListenAddr) == "" { + return nil, "", fmt.Errorf("quic fabric enabled but listen addr is empty") + } + tlsConfig, err := quicFabricTLSConfig(identity) + if err != nil { + return nil, "", err + } + server, err := mesh.StartQUICFabricServer(ctx, mesh.QUICFabricServerConfig{ + ListenAddr: cfg.MeshQUICFabricListenAddr, + TLSConfig: tlsConfig, + Logger: func(entry mesh.FabricSessionEventLogEntry) { + payload, err := json.Marshal(entry) + if err != nil { + log.Printf("fabric quic event marshal failed: %v", err) + return + } + log.Printf("fabric_quic_event=%s", string(payload)) + }, + }) + if err != nil { + return nil, "", err + } + addr := "" + if server.Addr() != nil { + addr = server.Addr().String() + } + log.Printf("quic fabric endpoint enabled: listen_addr=%s effective_addr=%s node_id=%s cluster_id=%s", cfg.MeshQUICFabricListenAddr, addr, identity.NodeID, identity.ClusterID) + return server, addr, nil +} + +func quicFabricTLSConfig(identity state.Identity) (*tls.Config, error) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, err + } + commonName := firstNonEmpty(identity.NodeID, "rap-fabric-node") + template := x509.Certificate{ + SerialNumber: big.NewInt(time.Now().UnixNano()), + Subject: pkix.Name{CommonName: commonName}, + NotBefore: time.Now().Add(-time.Minute), + NotAfter: time.Now().Add(24 * time.Hour), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + DNSNames: []string{commonName, "localhost"}, + } + certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key) + if err != nil { + return nil, err + } + return &tls.Config{ + Certificates: []tls.Certificate{{ + Certificate: [][]byte{certDER}, + PrivateKey: key, + }}, + NextProtos: []string{"rap-fabric-data-session-v1"}, + }, nil +} + func isAddressInUse(err error) bool { if err == nil { return false @@ -1689,6 +1766,7 @@ func applyRefreshedSyntheticMeshConfig(ctx context.Context, cfg config.Config, i } else { meshState.ListenerHandler.Update(nextListenerHandler) } + applyQUICFabricConfigIfChanged(ctx, cfg, identity, meshState) applyMeshListenerConfigIfChanged(ctx, cfg, identity, meshState, loadedConfig, observedAt) meshState.Routes = loadedConfig.Routes meshState.RouteHealthRoutes = routeHealthRoutes @@ -1735,6 +1813,32 @@ func applyMeshListenerConfigIfChanged(ctx context.Context, base config.Config, i ) } +func applyQUICFabricConfigIfChanged(ctx context.Context, cfg config.Config, identity state.Identity, meshState *syntheticMeshState) { + if meshState == nil { + return + } + desiredAddr := strings.TrimSpace(cfg.MeshQUICFabricListenAddr) + if meshState.QUICFabricServer != nil && (!cfg.MeshQUICFabricEnabled || meshState.QUICFabricListenAddr != desiredAddr) { + _ = meshState.QUICFabricServer.Close() + meshState.QUICFabricServer = nil + meshState.QUICFabricListenAddr = "" + } + if !cfg.MeshQUICFabricEnabled { + meshState.QUICFabricError = "" + return + } + if meshState.QUICFabricServer != nil { + return + } + server, addr, err := startQUICFabricEndpoint(ctx, cfg, identity) + meshState.QUICFabricServer = server + meshState.QUICFabricListenAddr = addr + meshState.QUICFabricError = errorString(err) + if err != nil { + log.Printf("quic fabric endpoint unavailable: listen_addr=%s node_id=%s cluster_id=%s err=%v", cfg.MeshQUICFabricListenAddr, identity.NodeID, identity.ClusterID, err) + } +} + func meshRendezvousLeasePostureForState(meshState *syntheticMeshState, identity state.Identity, observedAt time.Time) meshRendezvousLeasePosture { posture := meshRendezvousLeasePosture{} if meshState == nil { @@ -2487,7 +2591,7 @@ func heartbeatPayload(cfg config.Config, identity state.Identity, meshState *syn payload.Capabilities["mesh_production_forwarding"] = true } if cfg.MeshFabricSessionEnabled { - payload.Metadata["fabric_session_endpoint_report"] = map[string]any{ + report := map[string]any{ "schema_version": "rap.fabric_session_endpoint_report.v1", "enabled": true, "transport": "websocket_binary_frames", @@ -2498,8 +2602,20 @@ func heartbeatPayload(cfg config.Config, identity state.Identity, meshState *syn "traffic_isolation": "logical_streams", "observed_at": observedAt.UTC().Format(time.RFC3339Nano), } + if meshState != nil && cfg.MeshQUICFabricEnabled { + report["quic"] = map[string]any{ + "enabled": meshState.QUICFabricServer != nil, + "listen_addr": cfg.MeshQUICFabricListenAddr, + "effective_listen_addr": meshState.QUICFabricListenAddr, + "error": meshState.QUICFabricError, + } + } + payload.Metadata["fabric_session_endpoint_report"] = report payload.Capabilities["fabric_session_websocket_endpoint"] = true payload.Capabilities["fabric_data_session_v1"] = true + if cfg.MeshQUICFabricEnabled { + payload.Capabilities["fabric_quic_endpoint"] = true + } } if cfg.VPNFabricSessionTransportEnabled { report := map[string]any{ diff --git a/agents/rap-node-agent/cmd/rap-node-agent/main_test.go b/agents/rap-node-agent/cmd/rap-node-agent/main_test.go index 544e516..1d27bfc 100644 --- a/agents/rap-node-agent/cmd/rap-node-agent/main_test.go +++ b/agents/rap-node-agent/cmd/rap-node-agent/main_test.go @@ -706,11 +706,14 @@ func TestHeartbeatPayloadIncludesMeshEndpointReport(t *testing.T) { MeshProductionForwardingEnabled: true, MeshFabricSessionEnabled: true, VPNFabricSessionTransportEnabled: true, + MeshQUICFabricEnabled: true, + MeshQUICFabricListenAddr: ":19443", }, state.Identity{ ClusterID: "cluster-1", NodeID: "node-a", }, &syntheticMeshState{ VPNFabricSessionPeers: mesh.NewFabricSessionPeerManager(), + QUICFabricListenAddr: "127.0.0.1:19443", }, time.Date(2026, 4, 28, 12, 0, 0, 0, time.UTC)) report, ok := payload.Metadata["mesh_endpoint_report"].(map[string]any) @@ -731,6 +734,11 @@ func TestHeartbeatPayloadIncludesMeshEndpointReport(t *testing.T) { } if report, ok := payload.Metadata["fabric_session_endpoint_report"].(map[string]any); !ok || report["path"] != "/mesh/v1/fabric/session/ws" { t.Fatalf("fabric session endpoint report missing: %+v", payload.Metadata) + } else if quic, ok := report["quic"].(map[string]any); !ok || quic["listen_addr"] != ":19443" || quic["effective_listen_addr"] != "127.0.0.1:19443" { + t.Fatalf("fabric quic endpoint report missing: %+v", report) + } + if payload.Capabilities["fabric_quic_endpoint"] != true { + t.Fatalf("fabric quic capability missing: %+v", payload.Capabilities) } if payload.Capabilities["vpn_fabric_session_transport"] != true || payload.Capabilities["vpn_packet_batch_binary_frames"] != true { t.Fatalf("vpn fabric session capabilities missing: %+v", payload.Capabilities) diff --git a/agents/rap-node-agent/internal/config/config.go b/agents/rap-node-agent/internal/config/config.go index 4b1a06e..9906b8b 100644 --- a/agents/rap-node-agent/internal/config/config.go +++ b/agents/rap-node-agent/internal/config/config.go @@ -28,6 +28,8 @@ type Config struct { MeshProductionForwardingEnabled bool MeshFabricSessionEnabled bool VPNFabricSessionTransportEnabled bool + MeshQUICFabricEnabled bool + MeshQUICFabricListenAddr string MeshProductionObservationSinkCapacity int MeshListenAddr string MeshListenPortMode string @@ -67,6 +69,8 @@ func Load(args []string, env map[string]string) (Config, error) { fs.BoolVar(&cfg.MeshProductionForwardingEnabled, "mesh-production-forwarding-enabled", getEnvBool(env, "RAP_MESH_PRODUCTION_FORWARDING_ENABLED", false), "Enable production fabric-control direct next-hop forwarding gate. Disabled by default.") fs.BoolVar(&cfg.MeshFabricSessionEnabled, "mesh-fabric-session-enabled", getEnvBool(env, "RAP_MESH_FABRIC_SESSION_ENABLED", false), "Enable authenticated fabric session WebSocket endpoint. Disabled by default.") fs.BoolVar(&cfg.VPNFabricSessionTransportEnabled, "vpn-fabric-session-transport-enabled", getEnvBool(env, "RAP_VPN_FABRIC_SESSION_TRANSPORT_ENABLED", false), "Route VPN packet transport over persistent fabric session when explicitly enabled. Disabled by default.") + fs.BoolVar(&cfg.MeshQUICFabricEnabled, "mesh-quic-fabric-enabled", getEnvBool(env, "RAP_MESH_QUIC_FABRIC_ENABLED", false), "Enable QUIC/UDP fabric listener. Disabled by default.") + fs.StringVar(&cfg.MeshQUICFabricListenAddr, "mesh-quic-fabric-listen-addr", getEnv(env, "RAP_MESH_QUIC_FABRIC_LISTEN_ADDR", ""), "Listen address for QUIC/UDP fabric endpoint, for example :19443.") fs.IntVar(&cfg.MeshProductionObservationSinkCapacity, "mesh-production-observation-sink-capacity", getEnvSignedInt(env, "RAP_MESH_PRODUCTION_OBSERVATION_SINK_CAPACITY", 0), "Bounded local metadata-only production envelope observation sink capacity. Disabled when 0.") fs.StringVar(&cfg.MeshListenAddr, "mesh-listen-addr", getEnv(env, "RAP_MESH_LISTEN_ADDR", ""), "Listen address for disabled-by-default C17E synthetic mesh HTTP endpoint.") fs.StringVar(&cfg.MeshListenPortMode, "mesh-listen-port-mode", getEnv(env, "RAP_MESH_LISTEN_PORT_MODE", "manual"), "Mesh listen port behavior: manual, auto, or disabled.") @@ -102,6 +106,7 @@ func Load(args []string, env map[string]string) (Config, error) { cfg.NodeName = strings.TrimSpace(cfg.NodeName) cfg.StateDir = strings.TrimSpace(cfg.StateDir) cfg.MeshListenAddr = strings.TrimSpace(cfg.MeshListenAddr) + cfg.MeshQUICFabricListenAddr = strings.TrimSpace(cfg.MeshQUICFabricListenAddr) cfg.MeshListenPortMode = strings.ToLower(strings.TrimSpace(cfg.MeshListenPortMode)) cfg.MeshAdvertiseEndpoint = strings.TrimRight(strings.TrimSpace(cfg.MeshAdvertiseEndpoint), "/") cfg.MeshAdvertiseEndpointsJSON = strings.TrimSpace(cfg.MeshAdvertiseEndpointsJSON) diff --git a/agents/rap-node-agent/internal/config/config_test.go b/agents/rap-node-agent/internal/config/config_test.go index dfd6a72..de69a9b 100644 --- a/agents/rap-node-agent/internal/config/config_test.go +++ b/agents/rap-node-agent/internal/config/config_test.go @@ -22,6 +22,8 @@ func TestLoadConfigFromEnvAndArgs(t *testing.T) { "RAP_MESH_PRODUCTION_FORWARDING_ENABLED": "true", "RAP_MESH_FABRIC_SESSION_ENABLED": "true", "RAP_VPN_FABRIC_SESSION_TRANSPORT_ENABLED": "true", + "RAP_MESH_QUIC_FABRIC_ENABLED": "true", + "RAP_MESH_QUIC_FABRIC_LISTEN_ADDR": ":19443", "RAP_MESH_PRODUCTION_OBSERVATION_SINK_CAPACITY": "5", "RAP_MESH_LISTEN_ADDR": "127.0.0.1:19001", "RAP_MESH_LISTEN_PORT_MODE": "auto", @@ -74,6 +76,9 @@ func TestLoadConfigFromEnvAndArgs(t *testing.T) { if !cfg.VPNFabricSessionTransportEnabled { t.Fatal("VPNFabricSessionTransportEnabled = false, want true") } + if !cfg.MeshQUICFabricEnabled || cfg.MeshQUICFabricListenAddr != ":19443" { + t.Fatalf("unexpected QUIC fabric config: %+v", cfg) + } if cfg.MeshProductionObservationSinkCapacity != 5 { t.Fatalf("MeshProductionObservationSinkCapacity = %d, want 5", cfg.MeshProductionObservationSinkCapacity) } diff --git a/agents/rap-node-agent/internal/hostagent/config.go b/agents/rap-node-agent/internal/hostagent/config.go index 2c5e5f6..c544712 100644 --- a/agents/rap-node-agent/internal/hostagent/config.go +++ b/agents/rap-node-agent/internal/hostagent/config.go @@ -31,6 +31,8 @@ type RuntimeConfig struct { MeshProductionForwardingEnabled bool MeshFabricSessionEnabled bool VPNFabricSessionTransportEnabled bool + MeshQUICFabricEnabled bool + MeshQUICFabricListenAddr string MeshListenAddr string MeshListenPortMode string MeshListenAutoPortStart int @@ -63,6 +65,7 @@ func (cfg RuntimeConfig) Normalize() RuntimeConfig { cfg.Network = firstNonEmpty(cfg.Network, DefaultNetwork) cfg.RestartPolicy = firstNonEmpty(cfg.RestartPolicy, "unless-stopped") cfg.MeshListenAddr = strings.TrimSpace(cfg.MeshListenAddr) + cfg.MeshQUICFabricListenAddr = strings.TrimSpace(cfg.MeshQUICFabricListenAddr) cfg.MeshListenPortMode = strings.ToLower(strings.TrimSpace(cfg.MeshListenPortMode)) cfg.MeshAdvertiseEndpoint = strings.TrimRight(strings.TrimSpace(cfg.MeshAdvertiseEndpoint), "/") cfg.MeshAdvertiseEndpointsJSON = strings.TrimSpace(cfg.MeshAdvertiseEndpointsJSON) diff --git a/agents/rap-node-agent/internal/hostagent/docker.go b/agents/rap-node-agent/internal/hostagent/docker.go index 0141559..ca5697b 100644 --- a/agents/rap-node-agent/internal/hostagent/docker.go +++ b/agents/rap-node-agent/internal/hostagent/docker.go @@ -266,6 +266,7 @@ func NodeAgentEnvWithStateDir(cfg RuntimeConfig, stateDir string) []string { "RAP_MESH_PRODUCTION_FORWARDING_ENABLED=" + boolString(cfg.MeshProductionForwardingEnabled), "RAP_MESH_FABRIC_SESSION_ENABLED=" + boolString(cfg.MeshFabricSessionEnabled), "RAP_VPN_FABRIC_SESSION_TRANSPORT_ENABLED=" + boolString(cfg.VPNFabricSessionTransportEnabled), + "RAP_MESH_QUIC_FABRIC_ENABLED=" + boolString(cfg.MeshQUICFabricEnabled), } if cfg.JoinToken != "" { env = append(env, "RAP_JOIN_TOKEN="+cfg.JoinToken) @@ -273,6 +274,9 @@ func NodeAgentEnvWithStateDir(cfg RuntimeConfig, stateDir string) []string { if cfg.MeshListenAddr != "" { env = append(env, "RAP_MESH_LISTEN_ADDR="+cfg.MeshListenAddr) } + if cfg.MeshQUICFabricListenAddr != "" { + env = append(env, "RAP_MESH_QUIC_FABRIC_LISTEN_ADDR="+cfg.MeshQUICFabricListenAddr) + } if cfg.MeshListenPortMode != "" { env = append(env, "RAP_MESH_LISTEN_PORT_MODE="+cfg.MeshListenPortMode) } diff --git a/agents/rap-node-agent/internal/hostagent/linux.go b/agents/rap-node-agent/internal/hostagent/linux.go index ce3fb47..774e961 100644 --- a/agents/rap-node-agent/internal/hostagent/linux.go +++ b/agents/rap-node-agent/internal/hostagent/linux.go @@ -74,6 +74,8 @@ func LinuxInstallConfigFromProfile(profile LinuxInstallProfile) LinuxInstallConf MeshProductionForwardingEnabled: profile.MeshProductionForwardingEnabled, MeshFabricSessionEnabled: profile.MeshFabricSessionEnabled, VPNFabricSessionTransportEnabled: profile.VPNFabricSessionTransportEnabled, + MeshQUICFabricEnabled: profile.MeshQUICFabricEnabled, + MeshQUICFabricListenAddr: profile.MeshQUICFabricListenAddr, MeshListenAddr: profile.MeshListenAddr, MeshListenPortMode: profile.MeshListenPortMode, MeshListenAutoPortStart: profile.MeshListenAutoPortStart, diff --git a/agents/rap-node-agent/internal/hostagent/profile.go b/agents/rap-node-agent/internal/hostagent/profile.go index c6ffd19..f5d5667 100644 --- a/agents/rap-node-agent/internal/hostagent/profile.go +++ b/agents/rap-node-agent/internal/hostagent/profile.go @@ -32,6 +32,8 @@ type DockerInstallProfile struct { MeshProductionForwardingEnabled bool `json:"mesh_production_forwarding_enabled"` MeshFabricSessionEnabled bool `json:"mesh_fabric_session_enabled"` VPNFabricSessionTransportEnabled bool `json:"vpn_fabric_session_transport_enabled"` + MeshQUICFabricEnabled bool `json:"mesh_quic_fabric_enabled"` + MeshQUICFabricListenAddr string `json:"mesh_quic_fabric_listen_addr"` MeshListenAddr string `json:"mesh_listen_addr"` MeshListenPortMode string `json:"mesh_listen_port_mode"` MeshListenAutoPortStart int `json:"mesh_listen_auto_port_start"` @@ -76,6 +78,8 @@ type WindowsInstallProfile struct { MeshProductionForwardingEnabled bool `json:"mesh_production_forwarding_enabled"` MeshFabricSessionEnabled bool `json:"mesh_fabric_session_enabled"` VPNFabricSessionTransportEnabled bool `json:"vpn_fabric_session_transport_enabled"` + MeshQUICFabricEnabled bool `json:"mesh_quic_fabric_enabled"` + MeshQUICFabricListenAddr string `json:"mesh_quic_fabric_listen_addr"` MeshListenAddr string `json:"mesh_listen_addr"` MeshListenPortMode string `json:"mesh_listen_port_mode"` MeshListenAutoPortStart int `json:"mesh_listen_auto_port_start"` @@ -110,6 +114,8 @@ type LinuxInstallProfile struct { MeshProductionForwardingEnabled bool `json:"mesh_production_forwarding_enabled"` MeshFabricSessionEnabled bool `json:"mesh_fabric_session_enabled"` VPNFabricSessionTransportEnabled bool `json:"vpn_fabric_session_transport_enabled"` + MeshQUICFabricEnabled bool `json:"mesh_quic_fabric_enabled"` + MeshQUICFabricListenAddr string `json:"mesh_quic_fabric_listen_addr"` MeshListenAddr string `json:"mesh_listen_addr"` MeshListenPortMode string `json:"mesh_listen_port_mode"` MeshListenAutoPortStart int `json:"mesh_listen_auto_port_start"` @@ -289,6 +295,8 @@ func RuntimeConfigFromProfile(profile DockerInstallProfile) RuntimeConfig { MeshProductionForwardingEnabled: profile.MeshProductionForwardingEnabled, MeshFabricSessionEnabled: profile.MeshFabricSessionEnabled, VPNFabricSessionTransportEnabled: profile.VPNFabricSessionTransportEnabled, + MeshQUICFabricEnabled: profile.MeshQUICFabricEnabled, + MeshQUICFabricListenAddr: profile.MeshQUICFabricListenAddr, MeshListenAddr: profile.MeshListenAddr, MeshListenPortMode: profile.MeshListenPortMode, MeshListenAutoPortStart: profile.MeshListenAutoPortStart, diff --git a/agents/rap-node-agent/internal/hostagent/update.go b/agents/rap-node-agent/internal/hostagent/update.go index 152e4f0..4224bc7 100644 --- a/agents/rap-node-agent/internal/hostagent/update.go +++ b/agents/rap-node-agent/internal/hostagent/update.go @@ -596,6 +596,8 @@ func (m DockerManager) runtimeConfigFromContainer(ctx context.Context, runner Co MeshProductionForwardingEnabled: parseBool(env["RAP_MESH_PRODUCTION_FORWARDING_ENABLED"]), MeshFabricSessionEnabled: parseBool(env["RAP_MESH_FABRIC_SESSION_ENABLED"]), VPNFabricSessionTransportEnabled: parseBool(env["RAP_VPN_FABRIC_SESSION_TRANSPORT_ENABLED"]), + MeshQUICFabricEnabled: parseBool(env["RAP_MESH_QUIC_FABRIC_ENABLED"]), + MeshQUICFabricListenAddr: env["RAP_MESH_QUIC_FABRIC_LISTEN_ADDR"], MeshListenAddr: env["RAP_MESH_LISTEN_ADDR"], MeshListenPortMode: env["RAP_MESH_LISTEN_PORT_MODE"], MeshListenAutoPortStart: parseInt(env["RAP_MESH_LISTEN_AUTO_PORT_START"]), diff --git a/agents/rap-node-agent/internal/hostagent/windows.go b/agents/rap-node-agent/internal/hostagent/windows.go index 582ccd4..445b50f 100644 --- a/agents/rap-node-agent/internal/hostagent/windows.go +++ b/agents/rap-node-agent/internal/hostagent/windows.go @@ -68,6 +68,8 @@ func WindowsInstallConfigFromProfile(profile WindowsInstallProfile) WindowsInsta MeshProductionForwardingEnabled: profile.MeshProductionForwardingEnabled, MeshFabricSessionEnabled: profile.MeshFabricSessionEnabled, VPNFabricSessionTransportEnabled: profile.VPNFabricSessionTransportEnabled, + MeshQUICFabricEnabled: profile.MeshQUICFabricEnabled, + MeshQUICFabricListenAddr: profile.MeshQUICFabricListenAddr, MeshListenAddr: profile.MeshListenAddr, MeshListenPortMode: profile.MeshListenPortMode, MeshListenAutoPortStart: profile.MeshListenAutoPortStart, diff --git a/docs/architecture/DISTRIBUTED_FABRIC_NODE_PROTOCOL_PLAN.md b/docs/architecture/DISTRIBUTED_FABRIC_NODE_PROTOCOL_PLAN.md index f10f5a5..8577b18 100644 --- a/docs/architecture/DISTRIBUTED_FABRIC_NODE_PROTOCOL_PLAN.md +++ b/docs/architecture/DISTRIBUTED_FABRIC_NODE_PROTOCOL_PLAN.md @@ -301,6 +301,10 @@ Carrier selection understands QUIC transport labels and `quic://host:port` endpoints while preserving WebSocket as the default fallback. `QUICFabricServer` provides the matching node-side QUIC listener for accepting fabric streams and running the same session frame handler as other carriers. +Node-agent can now gate the QUIC listener with +`RAP_MESH_QUIC_FABRIC_ENABLED` / `RAP_MESH_QUIC_FABRIC_LISTEN_ADDR`, report it +in heartbeat metadata, and pass the setting through host-agent install/update +profiles. Deliverables: