@@ -54,7 +54,7 @@ func newDebugHandler(n *SamNode) http.Handler {
5454 writeDebugJSON (w , n .tokenInfo ())
5555 })
5656 mux .HandleFunc ("GET /debug/logs" , func (w http.ResponseWriter , r * http.Request ) {
57- writeDebugJSON (w , map [ string ] any { "logs" : GetRecentLogs ()})
57+ writeDebugJSON (w , logsResponse { Logs : GetRecentLogs ()})
5858 })
5959 mux .HandleFunc ("POST /debug/connect-peer" , func (w http.ResponseWriter , r * http.Request ) {
6060 var req struct {
@@ -109,9 +109,47 @@ func writeDebugJSON(w http.ResponseWriter, v any) {
109109 }
110110}
111111
112+ // The types below are the /debug payloads. They are unexported on purpose:
113+ // these endpoints are unversioned operator diagnostics, not part of the
114+ // api/sam.proto mesh contract.
115+
116+ type meshInfoResponse struct {
117+ PeerID string `json:"peer_id"`
118+ ConnectedPeers []string `json:"connected_peers"`
119+ DHTSize int `json:"dht_size"`
120+ RouterPeerID string `json:"router_peer_id"`
121+ LocalAPISocket string `json:"local_api_socket,omitempty"`
122+ }
123+
124+ type connectivityResponse struct {
125+ ConnectedPeers int `json:"connected_peers"`
126+ TotalKnownPeers int `json:"total_known_peers"`
127+ PingLatencyMS * int64 `json:"ping_latency_ms,omitempty"`
128+ PingError * bool `json:"ping_error,omitempty"`
129+ PingErrorMsg string `json:"ping_error_msg,omitempty"`
130+ RouterLatencyMS * int64 `json:"router_latency_ms,omitempty"`
131+ RouterError * bool `json:"router_error,omitempty"`
132+ RouterErrorMsg string `json:"router_error_msg,omitempty"`
133+ }
134+
135+ type tokenInfoResponse struct {
136+ HasToken bool `json:"has_token"`
137+ ExpiresInSeconds * float64 `json:"expires_in_seconds,omitempty"`
138+ IsExpired * bool `json:"is_expired,omitempty"`
139+ }
140+
141+ type networkInfoResponse struct {
142+ ListenAddresses []string `json:"listen_addresses"`
143+ ObservedAddresses []string `json:"observed_addresses"`
144+ }
145+
146+ type logsResponse struct {
147+ Logs []string `json:"logs"`
148+ }
149+
112150// meshInfo backs both the get_mesh_info MCP tool and GET /debug/mesh-info.
113151// The MCP path skips the /debug boundary guard, so it re-checks here.
114- func (n * SamNode ) meshInfo () (map [ string ] any , error ) {
152+ func (n * SamNode ) meshInfo () (* meshInfoResponse , error ) {
115153 if err := n .debugReady (); err != nil {
116154 return nil , err
117155 }
@@ -122,29 +160,25 @@ func (n *SamNode) meshInfo() (map[string]any, error) {
122160 for _ , p := range peers {
123161 connectedPeers = append (connectedPeers , p .String ())
124162 }
125- dhtSize := n .DHT .RoutingTable ().Size ()
126163
127- resData := map [string ]any {
128- "peer_id" : n .Host .ID ().String (),
129- "connected_peers" : connectedPeers ,
130- "dht_size" : dhtSize ,
131- "router_peer_id" : n .RouterPeerID .String (),
132- }
133- if n .BoundSocketPath != "" {
134- resData ["local_api_socket" ] = n .BoundSocketPath
135- }
136- return resData , nil
164+ return & meshInfoResponse {
165+ PeerID : n .Host .ID ().String (),
166+ ConnectedPeers : connectedPeers ,
167+ DHTSize : n .DHT .RoutingTable ().Size (),
168+ RouterPeerID : n .RouterPeerID .String (),
169+ LocalAPISocket : n .BoundSocketPath ,
170+ }, nil
137171}
138172
139173// connectivityStats backs GET /debug/connectivity: with a peer ID it pings that
140174// peer, otherwise it pings the SAM router.
141- func (n * SamNode ) connectivityStats (ctx context.Context , peerIDStr string ) map [ string ] any {
175+ func (n * SamNode ) connectivityStats (ctx context.Context , peerIDStr string ) connectivityResponse {
142176 ctx , cancel := context .WithTimeout (ctx , connectivityPingTimeout )
143177 defer cancel ()
144178
145- stats := map [ string ] any {
146- "connected_peers" : len (n .Host .Network ().Peers ()),
147- "total_known_peers" : len (n .Host .Peerstore ().Peers ()),
179+ stats := connectivityResponse {
180+ ConnectedPeers : len (n .Host .Network ().Peers ()),
181+ TotalKnownPeers : len (n .Host .Peerstore ().Peers ()),
148182 }
149183
150184 if peerIDStr != "" {
@@ -153,48 +187,52 @@ func (n *SamNode) connectivityStats(ctx context.Context, peerIDStr string) map[s
153187 n .preparePeerAddrs (ctx , pid )
154188 start := time .Now ()
155189 err := n .Host .Connect (ctx , peer.AddrInfo {ID : pid })
156- stats ["ping_latency_ms" ] = time .Since (start ).Milliseconds ()
157- stats ["ping_error" ] = err != nil
190+ latency := time .Since (start ).Milliseconds ()
191+ failed := err != nil
192+ stats .PingLatencyMS = & latency
193+ stats .PingError = & failed
158194 if err != nil {
159- stats [ "ping_error_msg" ] = err .Error ()
195+ stats . PingErrorMsg = err .Error ()
160196 }
161197 } else {
162- stats ["ping_error" ] = true
163- stats ["ping_error_msg" ] = "invalid peer id"
198+ failed := true
199+ stats .PingError = & failed
200+ stats .PingErrorMsg = "invalid peer id"
164201 }
165202 } else if n .RouterPeerID != "" {
166203 start := time .Now ()
167204 err := n .Host .Connect (ctx , peer.AddrInfo {ID : n .RouterPeerID })
168- stats ["router_latency_ms" ] = time .Since (start ).Milliseconds ()
169- stats ["router_error" ] = err != nil
205+ latency := time .Since (start ).Milliseconds ()
206+ failed := err != nil
207+ stats .RouterLatencyMS = & latency
208+ stats .RouterError = & failed
170209 if err != nil {
171- stats [ "router_error_msg" ] = err .Error ()
210+ stats . RouterErrorMsg = err .Error ()
172211 }
173212 }
174213
175214 return stats
176215}
177216
178217// tokenInfo backs GET /debug/token-info.
179- func (n * SamNode ) tokenInfo () map [string ]any {
180- info := map [string ]any {
181- "has_token" : false ,
182- }
183-
218+ func (n * SamNode ) tokenInfo () tokenInfoResponse {
219+ var info tokenInfoResponse
184220 token , err := n .Store .LoadIdentity ()
185221 if err == nil && len (token ) > 0 {
186- info [ "has_token" ] = true
222+ info . HasToken = true
187223 exp , err := n .Store .LoadIdentityExpiration ()
188224 if err == nil {
189- info ["expires_in_seconds" ] = time .Until (time .Unix (exp , 0 )).Seconds ()
190- info ["is_expired" ] = time .Now ().Unix () > exp
225+ expiresIn := time .Until (time .Unix (exp , 0 )).Seconds ()
226+ expired := time .Now ().Unix () > exp
227+ info .ExpiresInSeconds = & expiresIn
228+ info .IsExpired = & expired
191229 }
192230 }
193231 return info
194232}
195233
196234// networkInfo backs GET /debug/network-info.
197- func (n * SamNode ) networkInfo () map [ string ] any {
235+ func (n * SamNode ) networkInfo () networkInfoResponse {
198236 listenAddrs := []string {}
199237 for _ , a := range n .Host .Network ().ListenAddresses () {
200238 listenAddrs = append (listenAddrs , a .String ())
@@ -205,9 +243,9 @@ func (n *SamNode) networkInfo() map[string]any {
205243 observedAddrs = append (observedAddrs , a .String ())
206244 }
207245
208- return map [ string ] any {
209- "listen_addresses" : listenAddrs ,
210- "observed_addresses" : observedAddrs ,
246+ return networkInfoResponse {
247+ ListenAddresses : listenAddrs ,
248+ ObservedAddresses : observedAddrs ,
211249 }
212250}
213251
0 commit comments