---
id: w6/m2/t004
title: "ownerId scoping parity: field on every resource object + ownerId filter on list endpoints"
worker: worker6
status: done
estimate: 25m
depends_on: [w6/m2/t002]
---

## Objective

Render's scoping contract (finding 10): every resource object the API returns carries an `ownerId` identifying its owning workspace, and list endpoints accept an `ownerId` filter. Sweep bex's REST resources (services, deploys, databases, env-groups equivalents) and close the gaps so a Render-targeting client can scope by workspace the way Render docs prescribe.

## Context

- Render's services list already filters by `ownerId` in its real API; bex's service endpoints live in `lego/backend/internal/api/` (+ `internal/apps/`, `internal/postgres/` features).
- Multi-workspace (m1) makes the filter meaningful; w1/m9 t003 scoped list/get to the caller's workspace — this generalizes to "caller's workspaces ∩ requested ownerId".
- Field naming: Render uses `ownerId` in JSON (the docs' `owner_id` mention is prose; confirm casing in t001's pinned spec).

## Steps

1. Inventory bex REST resource responses; add `ownerId` where missing.
2. Add `ownerId` query-param filtering to list endpoints, validated against the caller's memberships.
3. Mirror in GraphQL/MCP list verbs where those adapters expose the same lists.

## Files

- `lego/backend/internal/api/`
- `lego/backend/internal/{apps,postgres,logs,metrics}/` as touched

## Acceptance criteria

- [x] Every REST resource object carries `ownerId` with the pinned casing (services fully populated from `core.LabelTenant` — reconciled with w1/m9's tenant onboarding, which landed the same label mid-milestone and now stamps it on every App CR; databases carry the field but it's honestly always empty today — Database CRs aren't tenant-labeled yet, filed as `w6/001.md`)
- [x] `GET /v1/services?ownerId=tea-…` returns only that workspace's services; an ownerId the caller can't access is 403
- [x] GraphQL/MCP lists accept the same scoping (`services(ownerId:)`, `databases(ownerId:)`, `list_services`/`list_postgres_instances` ownerId arg + session-selection fallback)

## Out of scope

- New resource types.
