Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/server/chunked_load.go
Original file line number Diff line number Diff line change
Expand Up @@ -663,8 +663,8 @@ func (s *PacketStore) scanAndMergeChunk(rows *sql.Rows, relayPM *prefixMap, cold
// scanned to keep scanArgs aligned with the o.raw_hex column,
// and its header route feeds wireRoute (#76).
Timestamp: normalizeTimestamp(nullStrVal(obsTimestamp)),
wireRoute: wireRouteFromRawHex(obsRawHex.String),
}
cacheObservationWireEvidence(obs, obsRawHex.String)

// Same relay-hop indexing as Load and live ingest (see
// indexObservationRelayHops). relayPM is passed in from
Expand Down
4 changes: 4 additions & 0 deletions cmd/server/openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func routeDescriptions() map[string]routeMeta {
Errors: []routeError{{Status: "400", Description: "Invalid window."}, {Status: "500", Description: "Current identity or node metadata could not be read."}, {Status: "503", Description: "Relay snapshot is unavailable or warming up."}}},
"GET /api/regions/history": {Summary: "Hourly scoped relay history", Description: "One bounded bulk snapshot for Regions timeline and equal-window comparison. Includes 504 UTC clock-hour buckets over 21 days, excluding the unfinished current hour. Counts distinct transmissions per confirmed node and scope, assigned by transmission FirstSeen; summed node counts are relay appearances. Metadata is current, or archived only when no current row exists. Current catalog identity resolution and live visibility policies apply. Coverage and truncation must be checked before interpreting changes.", Tag: "analytics", Response: schemaRef("RegionsHistoryResponse"),
Errors: []routeError{{Status: "500", Description: "Identity or node metadata could not be read."}, {Status: "503", Description: "Relay history snapshot is unavailable or warming up."}}},
"GET /api/regions/routes": {Summary: "Observed directed scoped relay steps", Description: "Paired Regions history plus bounded directed adjacent relay steps from each explicitly observed scoped flood path. The observation transport codes must match the canonical transmission frame underlying its existing scope name. Counts deduplicate transmission IDs per directed step and scope and use FirstSeen hourly buckets. Scope handoffs, RF links, complete end-to-end paths and observer/origin edges are not inferred. Independent stepNodes carry current or archived endpoint metadata. Snapshot-wide diagnostics expose skipped or incomplete evidence.", Tag: "analytics", Response: schemaRef("RegionsRoutesResponse"), Errors: []routeError{{Status: "500", Description: "Identity or endpoint metadata could not be read."}, {Status: "503", Description: "Paired relay routes snapshot is unavailable or warming up."}}},
"GET /api/config/theme": {Summary: "Get theme configuration", Description: "Returns color maps, CSS variables, and theme defaults.", Tag: "config"},
"GET /api/config/map": {Summary: "Get map configuration", Tag: "config"},
"GET /api/config/geo-filter": {Summary: "Get geo-filter configuration", Tag: "config"},
Expand Down Expand Up @@ -1018,6 +1019,9 @@ func componentSchemas() map[string]interface{} {
for name, schema := range regionsHistorySchemas() {
schemas[name] = schema
}
for name, schema := range regionsRoutesSchemas() {
schemas[name] = schema
}
for name, schema := range heardBySchemas() {
schemas[name] = schema
}
Expand Down
40 changes: 30 additions & 10 deletions cmd/server/regions_history.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"log"
"net/http"
"sort"
Expand Down Expand Up @@ -215,6 +216,22 @@ func (s *Server) handleRegionsHistory(w http.ResponseWriter, r *http.Request) {
writeError(w, http.StatusServiceUnavailable, "observed relay history is warming up")
return
}
response, err := s.regionsHistoryResponse(r.Context(), snapshot)
if err != nil {
log.Printf("[regions-history] node metadata: %v", err)
writeError(w, http.StatusInternalServerError, "could not read region node metadata")
return
}
w.Header().Set("Cache-Control", "no-store")
writeJSON(w, response)
}

func (s *Server) regionsHistoryResponse(ctx context.Context, snapshot *repeaterRelaySnapshot) (RegionsHistoryResponse, error) {
response, _, err := s.regionsHistoryAndStepNodes(ctx, snapshot, nil)
return response, err
}

func (s *Server) regionsHistoryAndStepNodes(ctx context.Context, snapshot *repeaterRelaySnapshot, routeKeys map[string]bool) (RegionsHistoryResponse, []RegionsRouteNode, error) {
h := snapshot.history
response := RegionsHistoryResponse{GeneratedAt: snapshot.generatedAt.Format(time.RFC3339Nano),
HistoryStart: h.start.Format(time.RFC3339Nano), HistoryEnd: h.end.Format(time.RFC3339Nano),
Expand All @@ -225,18 +242,27 @@ func (s *Server) handleRegionsHistory(w http.ResponseWriter, r *http.Request) {
start := snapshot.coverageStart.Format(time.RFC3339Nano)
response.Coverage.Start = &start
}
keys := make([]string, 0, len(h.nodes))
keys := make([]string, 0, len(h.nodes)+len(routeKeys))
for key := range h.nodes {
keys = append(keys, key)
}
err := s.forEachVisibleRegionNode(r.Context(), keys, true, func(n observedRegionNodeMetadata) bool {
for key := range routeKeys {
if _, exists := h.nodes[key]; !exists {
keys = append(keys, key)
}
}
stepNodes := []RegionsRouteNode{}
err := s.forEachVisibleRegionNode(ctx, keys, true, func(n observedRegionNodeMetadata) bool {
if routeKeys[n.publicKey] {
stepNodes = append(stepNodes, regionRouteNodeMetadata(n))
}
scopes := h.nodes[n.publicKey]
if len(scopes) == 0 {
return true
}
if len(response.Nodes) >= maxObservedRegionNodes {
response.Truncated = true
return false
return len(routeKeys) > 0
}
node := RegionsHistoryNode{PublicKey: n.publicKey, Name: n.name, Role: n.role,
PositionSource: "none", MetadataSource: n.source, Scopes: scopes}
Expand All @@ -250,13 +276,7 @@ func (s *Server) handleRegionsHistory(w http.ResponseWriter, r *http.Request) {
response.Nodes = append(response.Nodes, node)
return true
})
if err != nil {
log.Printf("[regions-history] node metadata: %v", err)
writeError(w, http.StatusInternalServerError, "could not read region node metadata")
return
}
w.Header().Set("Cache-Control", "no-store")
writeJSON(w, response)
return response, stepNodes, err
}

func regionsHistorySchemas() map[string]*openAPISchema {
Expand Down
1 change: 1 addition & 0 deletions cmd/server/regions_observed.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type regionScopeRelay struct {
type repeaterRelaySnapshot struct {
info map[string]RepeaterRelayInfo
history *regionsRelayHistory
routes *regionsRouteSnapshot
generatedAt time.Time
coverageStart time.Time
loading bool
Expand Down
Loading
Loading