Skip to main content

Ownership Model

Ownership answers a question RBAC doesn't: whose is this? Access control decides who can touch a component; ownership records who is responsible for it. On DevsPortal, ownership is what makes the catalog navigable — "show me my team's components", "who owns this service?", "page the right team" — and it's set with a single annotation.

note

DevsPortal is built on the Control Plane Operator engine, and the console is its Backstage portal. Ownership is a Backstage concept layered on top of Control Plane Operator resources via the standard backstage.io/owner annotation — a plain Kubernetes annotation, no CRD or API change required.

Portal first, CLI second

Set ownership from the portal — the Create… and entity-edit forms expose the owner field — and read it back in the catalog and under Settings. The annotation/kubectl form below is for scripting, review, and GitOps.

Where ownership shows up in the portal

Once resolved, the owner is visible in two places in the console:

  • On a catalog entity — every Project and Component page shows an Owner field with the resolved team or user.
  • In Settings — your own Backstage Identity panel lists your User Entity and your Ownership Entities (the groups you own, which drive "owned by my team" filters).

The portal Settings page, with the Backstage Identity panel showing User Entity and Ownership Entities

The rest of this page is how that owner is set and resolved.

How ownership resolves

The console reads the backstage.io/owner annotation during its catalog sync and writes it to the entity's owner. It resolves an owner for each Project and Component along a fixed precedence chain:

EntityResolution order
Projectproject annotation → configured defaultOwner
Componentcomponent annotation → parent project annotation → defaultOwner

Two consequences matter:

  • A project's owner cascades to its components. Annotate the checkout project once and every component inside it — cart-api, payment, order-history — inherits that owner. This is the common case: one team owns a whole project.
  • A component can override its project. If a single component belongs to a different team (a shared library maintained by the platform team but living inside an app project), annotate that component directly; the component-level value wins.

Empty or whitespace-only values are treated as unset and fall through to the next tier. Changes are picked up on the next sync cycle (about 30 seconds) — no restart or redeploy.

The annotation format

The value is a portal entity reference: <kind>:<namespace>/<name>. The two forms you'll use:

  • group:default/checkout-team — owned by a team (the right default for most projects)
  • user:default/alice.fedshi — owned by an individual
note

The referenced group or user must exist in the portal — typically populated from the Fedshi identity provider or an org-data source — for "owned by my team" filters and ownership-based views to resolve. If it doesn't exist, the entity still shows the literal owner string, but it won't match the filters.

Set ownership on a project

Annotating a project once owns everything inside it.

In the portal: set the owner when you create the project — the Create… → Project form has an owner field that writes the backstage.io/owner annotation for you (see Orgs, projects, and tenancy). To change it later, edit the project's annotations from its catalog entity page.

Or with the occ CLI / kubectl — declaratively, in the manifest:

apiVersion: openchoreo.dev/v1alpha1
kind: Project
metadata:
name: checkout
namespace: fedshi
annotations:
backstage.io/owner: group:default/checkout-team
spec:
deploymentPipelineRef:
kind: DeploymentPipeline
name: default

Or on an existing project with kubectl:

kubectl annotate project checkout -n fedshi \
backstage.io/owner=group:default/checkout-team

This is why Onboard a team sets the annotation at project-creation time — ownership is correct from the first sync.

Override for one component

When a component belongs to a different team than its project:

apiVersion: openchoreo.dev/v1alpha1
kind: Component
metadata:
name: shared-auth-lib
namespace: fedshi
annotations:
backstage.io/owner: group:default/platform-team
spec:
owner:
projectName: checkout
componentType:
kind: ClusterComponentType
name: deployment/service
kubectl annotate component shared-auth-lib -n fedshi \
backstage.io/owner=group:default/platform-team

The checkout project still belongs to checkout-team; only shared-auth-lib is attributed to platform-team.

APIs inherit automatically

The APIs the console surfaces are derived from a component's endpoints and automatically inherit that component's resolved owner — you never annotate them separately.

Two owner fields — don't confuse them

There are two things called "owner" on these resources; they do different jobs:

  • spec.owner (on a Component/Workload) is the Control Plane Operator structural link — which Project and Component a resource belongs to. Required, immutable, not about teams.
  • metadata.annotations["backstage.io/owner"] is the catalog ownership this page is about — which team/person is responsible.

Set both: spec.owner to place the resource, the annotation to attribute it.

Verify ownership

After annotating, wait for the next sync (~30s) and check the Owner field on the Project or Component page in the Fedshi console. From the cluster side:

kubectl get project checkout -n fedshi \
-o jsonpath='{.metadata.annotations.backstage\.io/owner}'
tip

If the Owner field still shows the default after waiting, check that the key is exactly backstage.io/owner (lowercase, with the dot) and the value is a well-formed reference like group:default/checkout-team.

Default owner fallback

If neither a component nor its project carries the annotation, the portal falls back to the configured defaultOwner. That fallback also covers every non-Project/Component entity. How the default and the portal are configured is an operator concern — see the operator portal branding and configuration guides.

What's next