diff --git a/agents/rap-node-agent/cmd/mesh-live-smoke/main.go b/agents/rap-node-agent/cmd/mesh-live-smoke/main.go index b7f5e07..76a84aa 100644 --- a/agents/rap-node-agent/cmd/mesh-live-smoke/main.go +++ b/agents/rap-node-agent/cmd/mesh-live-smoke/main.go @@ -10,6 +10,7 @@ import ( "path/filepath" "time" + "github.com/example/remote-access-platform/agents/rap-node-agent/internal/fabricproto" "github.com/example/remote-access-platform/agents/rap-node-agent/internal/mesh" ) @@ -30,6 +31,9 @@ type smokeReport struct { RelayPath []string `json:"relay_path"` TestServiceAccepted bool `json:"test_service_accepted"` TestServiceEchoPayload string `json:"test_service_echo_payload"` + FabricSessionAccepted bool `json:"fabric_session_accepted"` + FabricSessionLatencyMS int64 `json:"fabric_session_latency_ms"` + FabricSessionEndpoint string `json:"fabric_session_endpoint"` PeerEndpoints map[string]any `json:"peer_endpoints"` } @@ -94,6 +98,19 @@ func run(ctx context.Context) (smokeReport, error) { if err != nil { return smokeReport{}, fmt.Errorf("test service: %w", err) } + fabricSessionStartedAt := time.Now() + fabricSessionResponse, err := mesh.NewClient(nodeB.URL).SendFabricSessionFrame(ctx, mesh.FabricSessionDialOptions{ + Token: "rap_fsn_mesh_live_smoke", + Timeout: 3 * time.Second, + }, fabricproto.Frame{ + Type: fabricproto.FramePing, + Sequence: uint64(fabricSessionStartedAt.UnixNano()), + Payload: []byte("mesh-live-smoke-fabric-session"), + }) + if err != nil { + return smokeReport{}, fmt.Errorf("fabric session smoke: %w", err) + } + fabricSessionLatency := time.Since(fabricSessionStartedAt) return smokeReport{ Stage: "C17F scoped synthetic config plus live HTTP transport", @@ -105,6 +122,9 @@ func run(ctx context.Context) (smokeReport, error) { RelayPath: decodeProbePath(relayAck), TestServiceAccepted: testService.Ack.MessageType == mesh.SyntheticMessageTestServiceAck, TestServiceEchoPayload: testService.Response.EchoPayload, + FabricSessionAccepted: fabricSessionResponse.Type == fabricproto.FramePong && string(fabricSessionResponse.Payload) == "mesh-live-smoke-fabric-session", + FabricSessionLatencyMS: fabricSessionLatency.Milliseconds(), + FabricSessionEndpoint: nodeB.URL + "/mesh/v1/fabric/session/ws", PeerEndpoints: map[string]any{ "node-a": nodeA.URL, "node-r": nodeR.URL, @@ -137,7 +157,7 @@ func writeSmokeScopedConfig(local mesh.PeerIdentity, peers map[string]string, ro func newSmokeNode(local mesh.PeerIdentity) *smokeNode { node := &smokeNode{Local: local} node.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - mesh.Server{Local: node.Local, SyntheticRuntime: node.Runtime}.Handler().ServeHTTP(w, r) + mesh.Server{Local: node.Local, SyntheticRuntime: node.Runtime, FabricSessionEnabled: true}.Handler().ServeHTTP(w, r) })) node.URL = node.server.URL return node diff --git a/docs/architecture/DISTRIBUTED_FABRIC_NODE_PROTOCOL_PLAN.md b/docs/architecture/DISTRIBUTED_FABRIC_NODE_PROTOCOL_PLAN.md index 48682ea..3d3d3cf 100644 --- a/docs/architecture/DISTRIBUTED_FABRIC_NODE_PROTOCOL_PLAN.md +++ b/docs/architecture/DISTRIBUTED_FABRIC_NODE_PROTOCOL_PLAN.md @@ -265,6 +265,8 @@ authority-pinned nodes. Node-agent exposes the endpoint only when `RAP_MESH_FABRIC_SESSION_ENABLED` / `-mesh-fabric-session-enabled` is set, and reports the enabled endpoint in heartbeat metadata. +`mesh-live-smoke` includes a fabric-session `PING`/`PONG` check alongside the +existing route and test-service probes. Deliverables: