Enrich fleet site and building telemetry tables#472
Conversation
🔐 Codex Security Review
Review SummaryOverall Risk: MEDIUM Findings[MEDIUM] Building device counts bypass the new stats authorization boundary
[MEDIUM] Building device count query lacks membership org scoping
[MEDIUM] List stats can materialize unbounded fleet-wide telemetry work
NotesNo cryptostealing, pool hijack, command injection, raw frontend HTML rendering, or protobuf wire-format break was evident in the reviewed diff. This was a static review of Generated by Codex Security Review | |
There was a problem hiding this comment.
Pull request overview
This PR upgrades the Fleet “Sites” and “Buildings” tabs from placeholder rows into telemetry-aware rollups by extending proto contracts, refactoring shared backend rollup helpers, and wiring the client tables to render telemetry/issue/health columns consistently with the existing racks pattern.
Changes:
- Extended Sites/Buildings stats protos and handlers to include temperature min/max + reporting counts and per-component issue counts; added building list
device_count. - Generalized component-error aggregation to support multiple scopes (collections/sites/buildings) and centralized telemetry aggregation in
devicerollup. - Added ProtoFleet list-state hooks and column configs to render linked container counts, issue counts, telemetry metrics, and health bars on Sites/Buildings tables.
Reviewed changes
Copilot reviewed 32 out of 38 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| server/sqlc/queries/building.sql | Add device_count to building list query. |
| server/internal/handlers/sites/handler.go | Plumb new site stats fields into RPC response. |
| server/internal/handlers/sites/handler_stats_test.go | Update handler test wiring (incl. rack count dependency). |
| server/internal/handlers/buildings/translate.go | Include device_count in list response translation. |
| server/internal/handlers/buildings/handler.go | Plumb new building stats fields into RPC response. |
| server/internal/handlers/buildings/handler_stats_test.go | Update stats handler test fake to satisfy new interface. |
| server/internal/domain/stores/sqlstores/device.go | Replace per-collection component error counts with scoped implementation. |
| server/internal/domain/stores/sqlstores/building.go | Map SQL building rows to domain model including DeviceCount. |
| server/internal/domain/stores/interfaces/mocks/mock_device_store.go | Add mock method for GetComponentErrorCounts. |
| server/internal/domain/stores/interfaces/device.go | Add ComponentErrorScope + rename CollectionID→ScopeID and new store method. |
| server/internal/domain/sites/service.go | Add rack count + component issue counts + temperature rollup to site stats. |
| server/internal/domain/sites/service_stats_test.go | Expand service stats tests for rack count + new fields. |
| server/internal/domain/sites/models/models.go | Extend SiteStats model with rack count, temperature, issue counts. |
| server/internal/domain/devicerollup/devicerollup.go | Centralize temperature rollup + component issue aggregation helper. |
| server/internal/domain/collection/service.go | Migrate collection telemetry aggregation to devicerollup.AggregateLatestMetrics. |
| server/internal/domain/collection/service_test.go | Update test deviceQueryer interface method name/signature. |
| server/internal/domain/buildings/service.go | Add component issue counts + temperature rollup to building stats. |
| server/internal/domain/buildings/service_stats_test.go | Update building stats test fake to satisfy new interface. |
| server/internal/domain/buildings/models/models.go | Extend building models (with counts + stats fields). |
| server/generated/sqlc/building.sql.go | Generated sqlc output for building list changes. |
| server/generated/grpc/sites/v1/sites.pb.go | Generated proto output for site stats changes. |
| proto/sites/v1/sites.proto | Add temperature + issue counts + rack count to GetSiteStatsResponse. |
| proto/buildings/v1/buildings.proto | Add device_count to BuildingWithCounts and telemetry/issue fields to stats. |
| docs/plans/2026-06-15-372-fleet-tab-telemetry-columns-tdd.md | Add TDD describing intended design/rollout. |
| client/src/protoFleet/hooks/useSiteListState.ts | New hook to fetch and cache site stats per visible row. |
| client/src/protoFleet/hooks/useBuildingListState.ts | New hook to fetch and cache building stats per visible row. |
| client/src/protoFleet/features/fleetManagement/utils/fleetTabLinks.ts | Centralize Fleet tab deep-link construction. |
| client/src/protoFleet/features/fleetManagement/pages/FleetSitesPage.tsx | Wire sites page to stats hook + error callout + pass statsMap. |
| client/src/protoFleet/features/fleetManagement/pages/FleetBuildingsPage.tsx | Wire buildings page to stats hook + error callout + pass statsMap. |
| client/src/protoFleet/features/fleetManagement/components/SiteList/SiteList.tsx | Add telemetry-aware columns via shared col config. |
| client/src/protoFleet/features/fleetManagement/components/SiteList/SiteList.test.tsx | Update route expectations for new deep-links. |
| client/src/protoFleet/features/fleetManagement/components/SiteList/siteColConfig.tsx | New column config to render metrics/links/composition bar for sites. |
| client/src/protoFleet/features/fleetManagement/components/BuildingList/BuildingList.tsx | Add telemetry-aware columns via shared col config. |
| client/src/protoFleet/features/fleetManagement/components/BuildingList/BuildingList.test.tsx | Update route expectations for new deep-links. |
| client/src/protoFleet/features/fleetManagement/components/BuildingList/buildingColConfig.tsx | New column config to render metrics/links/composition bar for buildings. |
| client/src/protoFleet/api/generated/sites/v1/sites_pb.ts | Generated TS proto output for sites changes. |
| client/src/protoFleet/api/generated/buildings/v1/buildings_pb.ts | Generated TS proto output for buildings changes. |
Files not reviewed (1)
- server/internal/domain/stores/interfaces/mocks/mock_device_store.go: Generated file
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1c233b61cd
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
🤖 Addressed the security review findings in 65ae93c: row-level site/building stats requests are now concurrency-capped, GetSiteStats uses a targeted rack-count query instead of listing all sites, and building-scoped component issue counts now exclude soft-deleted device sets while scoping rack rows by org. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 65ae93c862
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8aaedef43e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
8aaedef to
7f8c377
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7f8c377c91
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1cb2c603ae
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
1cb2c60 to
c817085
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a4e113603a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
a4e1136 to
bac36ee
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bac36eec53
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9a23408a9b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Summary
This PR enriches the Fleet Sites and Buildings tabs with telemetry-aware table rows: miners, issues, hashrate, efficiency, power, temperature, and health composition now come back with each list row instead of being fetched by one stats RPC per row. The API gains a lightweight
common.v1.FleetListStatsshape attached toSiteWithCountsandBuildingWithCounts, with inclusion gated byfleet:readso telemetry only appears for callers authorized to read fleet data.The heavier detail stats RPCs remain in place for pages/cards that need detail-only payloads such as device identifiers, per-rack health, floor-plan data, or slot statuses. This change also creates the API shape needed for future server-side sorting/filtering on telemetry columns.
How it works
The Fleet Sites and Buildings React tables still request their normal list data, but now read
listStatsfrom each row and render telemetry columns directly from that payload. Count cells link into the child Fleet tabs, issue counts sum component-level actionable errors, temperature respects the user's unit preference, and health bars show hashing, broken, and offline/sleeping device composition.On the server,
ListSitesandListBuildingsfirst enforce their normal list permissions, then opportunistically checkfleet:read. Callers with that permission get row-scopedFleetListStats; callers without it receive the same structural list rows without stats. The domain services populate row stats from structural counts, paired/auth-needed miner identifiers, miner state counts, latest telemetry rollups, and component issue counts.Telemetry math and component issue aggregation are centralized in
server/internal/domain/devicerollupso Sites, Buildings, and existing collection stats use the same conversions, invalid-value handling, temperature range behavior, and component type mapping.Diagrams
sequenceDiagram participant UI as Fleet table participant API as ListSites/ListBuildings participant Auth as RBAC participant Domain as Domain service participant Stores as SQL/device/telemetry stores UI->>API: Request list rows API->>Auth: Require base list access API->>Auth: Check fleet:read for telemetry API->>Domain: List with IncludeStats flag Domain->>Stores: Load structural rows and counts alt caller has fleet:read Domain->>Stores: Resolve paired/auth-needed devices by row scope Domain->>Stores: Load miner state counts Domain->>Stores: Load component issue counts Domain->>Stores: Load latest telemetry for unique devices Domain-->>API: Rows with FleetListStats else caller lacks fleet:read Domain-->>API: Rows without list_stats end API-->>UI: Render telemetry columns from listStats when presentAreas of the code involved
proto/common/v1/fleet_list_stats.protoFleetListStatsfor lightweight table-row rollups.proto/sites/v1/sites.proto,proto/buildings/v1/buildings.protolist_statstoSiteWithCounts/BuildingWithCounts; extends detail stats with temperature, component issues, and rack count where needed.client/src/protoFleet/api/generated/**,server/generated/grpc/**server/sqlc/queries/site.sql,server/sqlc/queries/building.sql,server/generated/sqlc/**server/internal/handlers/sites,server/internal/handlers/buildingsList*handlers gate list stats behindfleet:read; translators map domain stats into protobuf responses.server/internal/domain/sites,server/internal/domain/buildingsserver/internal/domain/devicerollupserver/internal/domain/collectiondevicerollupand generalized component-error scoping for existing collection stats.server/internal/domain/stores/interfaces,server/internal/domain/stores/sqlstores/device.goclient/src/protoFleet/features/fleetManagement/components/SiteListsite.listStats, adds telemetry/count columns, and removes placeholder cells.client/src/protoFleet/features/fleetManagement/components/BuildingListbuilding.listStats, adds rack/miner/count/telemetry columns, and removes placeholder cells.client/src/protoFleet/features/fleetManagement/utils/fleetTabLinks.tsGetSiteStats/GetBuildingStatslist-state fetching and concurrency helper.API and data shape
ListSitesSiteWithCounts.list_statsfleet:read.device_identifiers, rack floor-plan health, slot statuses.ListBuildingsBuildingWithCounts.device_count,BuildingWithCounts.list_statsfleet:read.device_identifiers, per-rack health, rack floor-plan data, slot statuses.GetSiteStatsGetBuildingStatsKey technical decisions and trade-offs
ListSites/ListBuildingsrows.Get*Statsrequests.FleetListStatsproto.GetSiteStatsResponse/GetBuildingStatsResponsedirectly.list_statswithfleet:read.devicerollup.Security and performance notes
ListSitesandListBuildingsonly setIncludeStatsafter a successfulfleet:readcheck. Without that permission, rows omitlist_stats.Testing and validation
go test ./server/internal/domain/devicerollup ./server/internal/domain/sites ./server/internal/domain/buildings ./server/internal/domain/collection ./server/internal/handlers/sites ./server/internal/handlers/buildings ./server/internal/domain/stores/sqlstores -run '^$'npm run lintnpm test -- --run src/protoFleet/features/fleetManagement/components/SiteList/SiteList.test.tsx src/protoFleet/features/fleetManagement/components/BuildingList/BuildingList.test.tsxnpm run build:protoFleetFollow-ups intentionally out of scope
list_statscontract to Rack and Group list endpoints.