Platform: standardize building targeting and add building-scoped RBAC
- Dominant language
- Go
- Stars
- 55
- Forks
- 16
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 87
Description
## Status
Open, but no longer a tracker for introducing building targeting from scratch.
Proto Fleet now targets buildings in several workflows. The remaining platform work is to standardize those patterns, close ownership-validation gaps, and add true building-scoped RBAC. This is still not a blocker for MQTT curtailment automation, which can use whole-org, site, device-set, or device-list response-profile scopes.
## Current state
### Building targeting and topology already exist
The codebase now has strong reusable building-aware primitives:
- `MinerListFilter` supports `building_ids`, `include_no_building`, building-scoped `zone_keys`, and `include_no_rack`.
- Rack/device-set filters support building IDs and building-scoped zone keys.
- `common.v1.ZoneKey` identifies a zone by building because zone labels are not globally unique.
- `ValidateFilterBuildings`, `BuildingBelongsToOrg`, and `BuildingsByIDs` provide reusable ownership and deleted-row checks.
- Miner membership can come from direct `device.building_id` assignment or rack membership through `device_set_rack.building_id`.
- Building CRUD, rack/device placement, stats, site reassignment, and site-map import/export enforce topology invariants at the building boundary.
- Schedules persist `SCHEDULE_TARGET_TYPE_BUILDING` and expand the building to its current paired devices at execution time.
- Bulk miner commands can target a building dynamically through `DeviceSelector.all_matching_filter` and `MinerListFilter.building_ids`.
Schedules therefore already satisfy the original outcome of targeting a building without enumerating miners. Miner commands provide the same capability for request-time filtered bulk actions, though not as a durable resource target.
### Building-scoped RBAC is still missing
Authorization remains org/site only:
- `authz.v1.ScopeType` has org and site values only.
- `authz.ResourceContext` carries only `SiteID`.
- `EffectivePermissions.Has`, `HasOrgWide`, and `SiteScopeFor` do not model building containment.
- `user_organization_role.scope_type` is constrained to `org` or `site`.
- Permission-denied payloads and list-scope projection expose site context only.
Site-scoped permission enforcement is now live across curtailment, infrastructure, miner commands, device-set reads, and selected building flows. Building role assignments and building-aware permission projection do not exist. The role-assignment API also remains incomplete, so product-managed narrowed assignments need to be considered alongside the new scope.
### Workflow gaps
- Curtailment scopes remain whole-org, site, device-set, device-list, or mixed. There is no `ScopeBuilding` and no building-scoped closed-loop admission.
- Activity supports site filters and unassigned-site filtering, but activity rows and filters have no first-class building dimension.
- Combined telemetry supports site filters, but telemetry requests have no first-class building dimension.
- Facility-fan curtailment uses explicit infrastructure device IDs plus persisted site authorization snapshots. It does not add building miner scope.
- Schedule create/update accepts syntactically valid building IDs but does not run the shared building ownership validator. A cross-org or deleted building ID can be persisted and later expand to zero devices instead of being rejected at the mutation boundary.
## Goal
Standardize building as a durable resource target where workflows need one, and add building-scoped authorization without retrofitting every API prematurely.
This work has three tracks.
## 1. Standard building target primitives
- Define when APIs should use a durable `ScopeBuilding { building_id }` shape versus the existing `MinerListFilter.building_ids` filter shape.
- Reuse `ValidateFilterBuildings` / `BuildingsByIDs` at mutation boundaries before persisting targets or applying side effects.
- Define target membership as the union of direct device building assignment and rack-derived building assignment, matching current miner filters and schedule overlap logic.
- Preserve org and deleted-row guards for all point and bulk building lookups.
- Harden schedule building targets first because schedules are the shipped durable reference implementation.
A shared proto scope should be introduced only for APIs that need a durable building resource reference. List and command filters should continue to reuse `MinerListFilter` rather than gaining parallel scope messages.
## 2. Building-scoped RBAC
- Add a building value to the authz scope enum and persistence constraint.
- Add building context to `authz.ResourceContext` and permission-denied details.
- Extend effective-permission containment so a request can be allowed by, in order:
- a matching building assignment;
- a site assignment covering the building's current parent site; or
- an unnarrowed org assignment.
- Add building-aware list projection so handlers can push readable building sets into stores instead of fetching unauthorized rows.
- Define the assignment foreign-key shape and building-move behavior.
The design must explicitly decide whether a building assignment follows the stable building ID when the building moves between sites, and how the former and new parent-site assignments affect access. Current site reassignment preserves `building.id` while cascading the new `site_id` to racks and devices.
## 3. RBAC-aware adopter
Schedules already prove operational building targeting. Select one workflow to prove building authorization end to end:
- building list/detail/stats is the narrowest platform-native adopter;
- curtailment is a strong later adopter because it already has persisted composable scopes and site-scoped authorization, but it carries higher safety and reconciler complexity; or
- schedules can adopt building permission checks after write-time ownership validation is fixed.
Do not require activity and telemetry building dimensions until their product UX needs are defined.
## Acceptance criteria
### Targeting and validation
- A canonical rule documents when to use `ScopeBuilding` versus `MinerListFilter.building_ids`.
- Schedule create/update rejects cross-org, deleted, zero, or nonexistent building IDs before persisting the target.
- Building expansion includes both directly assigned devices and devices assigned through racks, with org and pairing-status guards.
- Existing schedule and filtered-command building targeting remains compatible.
### Authorization
- Authz proto, database constraints, domain models, resolver, middleware, and error details support building scope.
- Permission tests cover matching building grants, another-building denial, parent-site fallback, org fallback, and narrowing by a site or building assignment that lacks the required permission.
- Building-aware list handlers exclude unreadable buildings at the query boundary.
- Building move semantics are documented and tested, including access through the old site, new site, and direct building assignment.
- Cross-org or deleted building assignments cannot be created.
### Adopter
- At least one workflow enforces a building-scoped permission from request through ownership validation, store query, and side effect.
- End-to-end tests cover authorization, cross-org isolation, deleted buildings, and building movement.
- Curtailment, activity, and telemetry remain unchanged unless selected as explicit adopters.
## Out of scope
- Replacing existing building-aware list filters with a universal scope abstraction.
- Retrofitting every activity, telemetry, history, or command API with building scope.
- Blocking MQTT sources, response profiles, or curtailment automation on building RBAC.
- Treating activity `scope_type` as an authz scope; it describes event target types, not permission containment.
## References
- Site-scoped curtailment foundation: #418.
- Site/building schedule targets: #552 and `docs/plans/2026-06-23-schedule-site-building-targets-tdd.md`.
- Building-aware miner/rack filters: `docs/plans/2026-05-14-229-miner-zone-building-filter-plan.md`.
- Filtered bulk miner commands: #733.
- Facility-fan curtailment stack through #770 is site-authorized and orthogonal to building miner scope.
- Authz anchors: `proto/authz/v1/authz.proto`, `server/internal/domain/authz/effective.go`, and migration `000052_create_permission_tables`.
Contributor guide
Assessment
This issue has not been assessed yet.