Skip to content

Team-Based Deployment Guide

What You'll Learn

By the end of this guide, you will understand how to:

  • Organize clusters using team-based labels for multi-tenant isolation
  • Create dashboards filtered for specific teams and environments
  • Build custom navigation menus tailored to team workflows
  • Implement REGO authorization policies for secure access control

This is an integration guide that demonstrates how SPOG's four key components work together to create secure, team-isolated views of your PowerDNS infrastructure.

The Business Scenario

You are a platform engineer at a company managing PowerDNS infrastructure for multiple application teams. Each team:

  • Manages their own set of DNS clusters across multiple environments (development, staging, production)
  • Needs isolated views showing only their clusters
  • Has different access levels based on roles (Team Lead, Developer, Operator)
  • Requires custom dashboards and navigation relevant to their workflows

Your goal is to deploy SPOG so that:

  • The DevOps team sees only their clusters with appropriate dashboards and tools
  • The Platform team sees their own separate set of clusters
  • Team members have appropriate permissions based on their roles
  • Developers cannot access production environments
  • Only operators can perform infrastructure operations like restarting instances

This guide shows you how to implement this complete team-based isolation using SPOG's integrated architecture.

Following Along

Want to deploy this example in your own Kubernetes cluster? This section provides everything you need to follow along step-by-step.

Note: The components work together as a system—you'll need to complete all four steps in this guide before everything connects and functions properly.

How Will You Access Glass UI?

How will you access Glass UI?

Prerequisites

Before starting, ensure you have:

  • Kubernetes cluster (v1.24+)
  • Ingress controller installed (e.g., nginx-ingress)
  • Helm 3.8+ installed
  • kubectl configured to access your cluster
  • Registry credentials for registry.open-xchange.com

Download Example Files

Download all configuration files for this guide:

Download Team-Based Deployment Examples

This archive contains:

Infrastructure values files:

  • controlplane-quickstart.yaml - Minimal controlplane with NATS hub
  • powerdns-quickstart.yaml - PowerDNS cluster with recursor and dnsdist

Glass configuration files:

  • Label configurations: team-based-labels-devops-prod.yaml, team-based-labels-devops-staging.yaml, team-based-labels-devops-dev.yaml, team-based-labels-platform-prod.yaml
  • Dashboard configuration: team-based-dashboard.yaml
  • Navigation configuration: team-based-navigation.yaml
  • REGO policy configuration: team-based-rego.yaml

Registry Authentication

Set up your registry credentials and log in to the OCI registry:

Bash
1
2
3
# Set your credentials
export REGISTRY_USERNAME="your-username"
export REGISTRY_PASSWORD="your-password"
Bash
export GLASS_HOSTNAME="glass.example.com"  # Replace with your domain
Bash
export NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
Bash
1
2
3
4
# Login to Helm registry
helm registry login registry.open-xchange.com \
  --username "$REGISTRY_USERNAME" \
  --password "$REGISTRY_PASSWORD"

Create Namespaces and Image Pull Secrets

Create the namespaces and image pull secrets for all components:

Bash
# Controlplane namespace
kubectl create namespace controlplane
kubectl create secret docker-registry registry-credentials \
  --docker-server=registry.open-xchange.com \
  --docker-username="$REGISTRY_USERNAME" \
  --docker-password="$REGISTRY_PASSWORD" \
  --namespace=controlplane

# Create namespaces for each team cluster
for ns in devops-prod devops-staging devops-dev platform-prod; do
  kubectl create namespace $ns
  kubectl create secret docker-registry registry-credentials \
    --docker-server=registry.open-xchange.com \
    --docker-username="$REGISTRY_USERNAME" \
    --docker-password="$REGISTRY_PASSWORD" \
    --namespace=$ns
done

Deploy Infrastructure

The following commands use the values files from the downloaded zip. Make sure you're in the directory where you extracted the files.

Install PowerDNS CRDs

Bash
1
2
3
helm install powerdns-crds \
  oci://registry.open-xchange.com/cloudcontrol/powerdns-crds \
  --version "3.1.13"

Deploy Controlplane

Bash
1
2
3
4
5
6
helm install team-controlplane \
  oci://registry.open-xchange.com/cloudcontrol/controlplane \
  --version "3.1.13" \
  --namespace controlplane \
  --set global.imagePullSecretsList[0]=registry-credentials \
  -f controlplane-quickstart.yaml

Deploy PowerDNS Clusters

Deploy a PowerDNS instance in each namespace:

Bash
# DevOps Production
helm install devops-prod \
  oci://registry.open-xchange.com/cloudcontrol/powerdns \
  --version "3.1.13" \
  --namespace devops-prod \
  --set global.imagePullSecretsList[0]=registry-credentials \
  -f powerdns-quickstart.yaml

# DevOps Staging
helm install devops-staging \
  oci://registry.open-xchange.com/cloudcontrol/powerdns \
  --version "3.1.13" \
  --namespace devops-staging \
  --set global.imagePullSecretsList[0]=registry-credentials \
  -f powerdns-quickstart.yaml

# DevOps Development
helm install devops-dev \
  oci://registry.open-xchange.com/cloudcontrol/powerdns \
  --version "3.1.13" \
  --namespace devops-dev \
  --set global.imagePullSecretsList[0]=registry-credentials \
  -f powerdns-quickstart.yaml

# Platform Production
helm install platform-prod \
  oci://registry.open-xchange.com/cloudcontrol/powerdns \
  --version "3.1.13" \
  --namespace platform-prod \
  --set global.imagePullSecretsList[0]=registry-credentials \
  -f powerdns-quickstart.yaml

Now you're ready to follow along with each step below. Deployment commands are provided after each configuration section.

Architecture Overview

This team-based deployment in SPOG creates secure, isolated views for different user types through the integration of four components: Labels, Dashboards, Navigation, and REGO authorization.

Cluster Organization by Labels

Each cluster is organized using three-dimensional labeling:

Cluster ID Team Environment Role
DevOps Team Clusters
devops-prod-auth-01 devops production authoritative
devops-staging-rec-01 devops staging recursor
devops-dev-auth-01 devops development authoritative
Platform Team Clusters
platform-prod-auth-01 platform production authoritative

Labels are the foundation that enables:

Capability Example Purpose
Dashboard filtering team = "devops" Filter widgets to show only relevant clusters
REGO authorization input.cluster.labels.team == input.user.team Control access based on team membership
Multi-dimensional organization team, environment, role Flexible taxonomy for any organizational structure
Scalability Simple team isolation → Complex multi-tenant architectures Grow from basic to sophisticated access control

User Types and Access Levels

This deployment defines four user types with different access patterns:

User Type Teams Environments Operations Dashboard Access
DevOps Team Lead DevOps All (dev, staging, prod) Read + Write (operator) All DevOps dashboards
DevOps Developer DevOps dev, staging only Read only (observer) DevOps non-prod dashboards
Platform Engineer Platform All (dev, staging, prod) Read + Write (operator) Platform dashboards only
Global Administrator All All Full admin bypass All dashboards

Access Decision Logic

Authorization follows a hierarchical decision tree:

User Cluster Operation Team Check Env Check Role Check Result
DevOps Developer devops-prod-auth-01 read_logs ✓ devops ✗ no production - DENIED
DevOps Developer devops-staging-rec-01 read_logs ✓ devops ✓ staging ✓ observer ALLOWED
DevOps Developer devops-staging-rec-01 restart_instance ✓ devops ✓ staging ✗ no operator DENIED
DevOps Team Lead devops-prod-auth-01 restart_instance ✓ devops ✓ production ✓ operator ALLOWED
Platform Engineer devops-prod-auth-01 read_logs ✗ platform≠devops - - DENIED
Global Admin any-cluster any-operation bypass bypass bypass ALLOWED

Step 1: Label Your Clusters

The first step in team-based deployment is applying consistent labels to your user plane clusters. Labels define cluster identity and enable all subsequent filtering and access control.

Labels define cluster identity through key-value pairs. For team-based deployment, we use three primary label dimensions: team (ownership), environment (lifecycle stage), and role (DNS service type).

Example Cluster Label Configurations

Here are examples showing how to label clusters for different teams and environments:

DevOps Team - Production Authoritative:

YAML
1
2
3
4
5
6
clusterId: "devops-prod-auth-01"

labels:
  team: "devops"              # Team ownership - enables team-based filtering
  environment: "production"   # Deployment stage - restricts access to authorized users
  role: "authoritative"       # DNS service type - for service-specific filtering

DevOps Team - Staging Recursor:

YAML
1
2
3
4
5
6
clusterId: "devops-staging-rec-01"

labels:
  team: "devops"              # Team ownership - enables team-based filtering
  environment: "staging"      # Deployment stage - accessible to developers
  role: "recursor"            # DNS service type - recursive resolver

DevOps Team - Development Authoritative:

YAML
1
2
3
4
5
6
clusterId: "devops-dev-auth-01"

labels:
  team: "devops"              # Team ownership - enables team-based filtering
  environment: "development"  # Deployment stage - unrestricted for team members
  role: "authoritative"       # DNS service type - authoritative nameserver

Platform Team - Production Authoritative:

YAML
1
2
3
4
5
6
clusterId: "platform-prod-auth-01"

labels:
  team: "platform"            # Team ownership - different from DevOps team
  environment: "production"   # Deployment stage - restricts access to authorized users
  role: "authoritative"       # DNS service type - for service-specific filtering

Label Strategy

The example demonstrates a three-dimensional labeling strategy:

  1. Primary filter: team - This is the main isolation boundary. Dashboards filter by team, REGO policies check team membership.

  2. Secondary filter: environment - Within a team, clusters are grouped by lifecycle stage. This enables:

  3. Restricting developers to non-production environments
  4. Creating environment-specific dashboards
  5. Implementing different policies per environment

  6. Tertiary dimension: role - DNS service type provides additional organization and filtering capability.

Deploy Step 1: Glass Instrumentation with Labels

Deploy Glass Instrumentation to each cluster with the appropriate label configuration:

Bash
# DevOps Production
helm install glass-instrumentation \
  oci://registry.open-xchange.com/cc-glass/glass-instrumentation \
  --version "1.0.0" \
  --namespace devops-prod \
  --set global.imagePullSecretsList[0]=registry-credentials \
  -f team-based-labels-devops-prod.yaml

# DevOps Staging
helm install glass-instrumentation \
  oci://registry.open-xchange.com/cc-glass/glass-instrumentation \
  --version "1.0.0" \
  --namespace devops-staging \
  --set global.imagePullSecretsList[0]=registry-credentials \
  -f team-based-labels-devops-staging.yaml

# DevOps Development
helm install glass-instrumentation \
  oci://registry.open-xchange.com/cc-glass/glass-instrumentation \
  --version "1.0.0" \
  --namespace devops-dev \
  --set global.imagePullSecretsList[0]=registry-credentials \
  -f team-based-labels-devops-dev.yaml

# Platform Production
helm install glass-instrumentation \
  oci://registry.open-xchange.com/cc-glass/glass-instrumentation \
  --version "1.0.0" \
  --namespace platform-prod \
  --set global.imagePullSecretsList[0]=registry-credentials \
  -f team-based-labels-platform-prod.yaml

Once all steps are complete, each cluster will be visible in SPOG with its configured labels.

Step 2: Create Team Dashboard

With clusters labeled, the next step is creating dashboards that automatically filter to show only team-relevant clusters. Dashboards are where users spend most of their time, so proper filtering is essential for team isolation.

Example: DevOps Team Dashboard Configuration

YAML
# Example: Team-Based Dashboard Configuration

globalConfig:
  dashboards:
    # Dashboard 1: DevOps Team Overview
    # Main dashboard showing all DevOps team clusters
    devops-overview:
      title: "DevOps Team Dashboard"
      description: "PowerDNS clusters managed by the DevOps team"
      url: /team/devops
      requires:
        - "dashboard_devops_overview"  # REGO permission flag
      graphs:
        # Widget 1: Tree table showing all DevOps clusters
        - widget: "cc-state-tree-table"
          title: "DevOps Cluster State"
          args:
            # Filter: Only show clusters with team="devops" label
            filter: 'team = "devops"'

        # Widget 2: Heatmap showing cluster readiness
        - widget: "cc-state-readiness-heatmap"
          title: "DevOps Cluster Health"
          args:
            filter: 'team = "devops"'

    # Dashboard 2: DevOps Production View
    # Filtered view showing only production clusters
    devops-production:
      title: "DevOps Production"
      description: "Production PowerDNS clusters for DevOps team"
      url: /team/devops/production
      requires:
        - "dashboard_devops_production"
      graphs:
        - widget: "cc-state-tree-table"
          title: "Production Clusters"
          args:
            # Multiple conditions: team AND environment
            filter: 'team = "devops" and environment = "production"'

        - widget: "cc-state-readiness-pie-charts"
          title: "Production Readiness Distribution"
          args:
            filter: 'team = "devops" and environment = "production"'

    # Dashboard 3: DevOps by Environment
    # Separate view for each environment (dev, staging, prod)
    devops-by-environment:
      title: "DevOps Environments"
      description: "DevOps clusters organized by environment"
      url: /team/devops/environments
      requires:
        - "dashboard_devops_environments"
      graphs:
        # Development clusters
        - widget: "cc-state-tree-table"
          title: "Development"
          args:
            filter: 'team = "devops" and environment = "development"'

        # Staging clusters
        - widget: "cc-state-tree-table"
          title: "Staging"
          args:
            filter: 'team = "devops" and environment = "staging"'

        # Production clusters
        - widget: "cc-state-tree-table"
          title: "Production"
          args:
            filter: 'team = "devops" and environment = "production"'

    # Dashboard 4: Platform Team Overview
    # Main dashboard showing all Platform team clusters
    platform-overview:
      title: "Platform Team Dashboard"
      description: "PowerDNS clusters managed by the Platform team"
      url: /team/platform
      requires:
        - "dashboard_platform_overview"  # REGO permission flag
      graphs:
        # Widget 1: Tree table showing all Platform clusters
        - widget: "cc-state-tree-table"
          title: "Platform Cluster State"
          args:
            filter: 'team = "platform"'

        # Widget 2: Heatmap showing cluster readiness
        - widget: "cc-state-readiness-heatmap"
          title: "Platform Cluster Health"
          args:
            filter: 'team = "platform"'

    # Dashboard 5: Platform Production View
    # Filtered view showing only production clusters
    platform-production:
      title: "Platform Production"
      description: "Production PowerDNS clusters for Platform team"
      url: /team/platform/production
      requires:
        - "dashboard_platform_production"
      graphs:
        - widget: "cc-state-tree-table"
          title: "Production Clusters"
          args:
            filter: 'team = "platform" and environment = "production"'

        - widget: "cc-state-readiness-pie-charts"
          title: "Production Readiness Distribution"
          args:
            filter: 'team = "platform" and environment = "production"'

    # Dashboard 6: Platform by Environment
    # Separate view for each environment (dev, staging, prod)
    platform-by-environment:
      title: "Platform Environments"
      description: "Platform clusters organized by environment"
      url: /team/platform/environments
      requires:
        - "dashboard_platform_environments"
      graphs:
        # Development clusters
        - widget: "cc-state-tree-table"
          title: "Development"
          args:
            filter: 'team = "platform" and environment = "development"'

        # Staging clusters
        - widget: "cc-state-tree-table"
          title: "Staging"
          args:
            filter: 'team = "platform" and environment = "staging"'

        # Production clusters
        - widget: "cc-state-tree-table"
          title: "Production"
          args:
            filter: 'team = "platform" and environment = "production"'

    # Dashboard 7: Global Overview (Admin Only)
    # Shows all clusters across all teams
    global-overview:
      title: "Global Cluster Overview"
      description: "All PowerDNS clusters across all teams"
      url: /global/all-clusters
      requires:
        - "dashboard_global_admin"  # Only admins can see all clusters
      graphs:
        - widget: "cc-state-tree-table"
          title: "All Clusters"
          args:
            filter: ''  # No filter - show everything

        - widget: "cc-state-readiness-heatmap"
          title: "Global Cluster Health"
          args:
            filter: ''

    # Dashboard 8: Global by Team (Admin Only)
    # Shows separate widgets for each team
    global-by-team:
      title: "Global View by Team"
      description: "All teams and their clusters"
      url: /global/by-team
      requires:
        - "dashboard_global_admin"
      graphs:
        # DevOps team clusters
        - widget: "cc-state-tree-table"
          title: "DevOps Team"
          args:
            filter: 'team = "devops"'

        # Platform team clusters
        - widget: "cc-state-tree-table"
          title: "Platform Team"
          args:
            filter: 'team = "platform"'

        # Readiness summary across all teams
        - widget: "cc-state-readiness-pie-charts"
          title: "Overall Readiness by Team"
          args:
            filter: ''

Dashboard Breakdown

The example creates eight dashboards organized by team and access level:

DevOps Team Dashboards

1. DevOps Team Overview (/team/devops)

Main dashboard showing all DevOps clusters with two widgets:

YAML
filter: 'team = "devops"'
  • Tree table: Shows cluster state for all DevOps clusters
  • Readiness heatmap: Visual health overview across all environments

Shows all clusters where team label equals "devops" - production, staging, and development.

2. DevOps Production (/team/devops/production)

Production-only view with stricter filtering:

YAML
filter: 'team = "devops" and environment = "production"'
  • Tree table: Production clusters only
  • Readiness pie charts: Distribution of production cluster health

Combines two conditions: must be DevOps team AND production environment.

3. DevOps by Environment (/team/devops/environments)

Side-by-side environment comparison with three widgets:

YAML
1
2
3
4
# Three separate widgets, each filtering one environment
filter: 'team = "devops" and environment = "development"'
filter: 'team = "devops" and environment = "staging"'
filter: 'team = "devops" and environment = "production"'

Each widget shows a tree table for its respective environment, making it easy to compare dev/staging/prod side-by-side.

Platform Team Dashboards

4. Platform Team Overview (/team/platform)

Main dashboard showing all Platform clusters:

YAML
filter: 'team = "platform"'
  • Tree table: Shows cluster state for all Platform clusters
  • Readiness heatmap: Visual health overview

Shows all clusters where team label equals "platform".

5. Platform Production (/team/platform/production)

Production-only view for Platform team:

YAML
filter: 'team = "platform" and environment = "production"'
  • Tree table: Platform production clusters
  • Readiness pie charts: Production health distribution

6. Platform by Environment (/team/platform/environments)

Environment comparison for Platform team:

YAML
1
2
3
filter: 'team = "platform" and environment = "development"'
filter: 'team = "platform" and environment = "staging"'
filter: 'team = "platform" and environment = "production"'

Three tree table widgets for side-by-side environment comparison.

Global Admin Dashboards

7. Global Overview (/global/all-clusters)

Complete system view showing all clusters across all teams:

YAML
filter: ''  # No filter - shows everything
  • Tree table: All clusters in the system
  • Readiness heatmap: System-wide health overview

Only accessible to global administrators.

8. Global by Team (/global/by-team)

Cross-team comparison view:

YAML
1
2
3
4
# Separate widgets for each team
filter: 'team = "devops"'
filter: 'team = "platform"'
filter: ''  # Overall readiness across all teams
  • Tree table (DevOps): All DevOps team clusters
  • Tree table (Platform): All Platform team clusters
  • Readiness pie charts: Overall health distribution across all teams

Provides global administrators with team-segmented visibility for cross-team operations.

Dashboard Permissions

Each dashboard includes a requires field that references REGO permission flags:

YAML
requires:
  - "dashboard_devops_overview"

This works as follows: 1. User attempts to access dashboard 2. Glass UI queries REGO policy engine: "Does this user have dashboard_devops_overview permission?" 3. REGO evaluates policies (defined in Step 4) and returns true/false 4. If true: dashboard loads. If false: user sees "Access Denied"

Permission Naming Convention: Dashboard permissions follow the pattern dashboard_{team}_{view}: - dashboard_devops_overview - DevOps team overview dashboard - dashboard_devops_production - DevOps production-only view - dashboard_platform_overview - Platform team overview dashboard - dashboard_global_admin - Global administrator dashboards

Forward Reference: These dashboard permission flags are defined in Step 4 (REGO Policies). For now, understand that this creates a security boundary - only authorized users see the dashboard.

Integration Point: Labels Enable Dashboards

Notice the connection:

  • Step 1 labeled clusters with team: "devops"
  • Step 2 creates dashboards that filter by team = "devops"
  • Result: Dashboards automatically show the right clusters

This is the first integration point where labels drive behavior. The next step adds navigation so users can easily access these dashboards.

Deployment Note

Steps 2, 3, and 4 (Dashboards, Navigation, and REGO Policies) all configure the Glass UI. These configurations are provided in separate values files (team-based-dashboard.yaml, team-based-navigation.yaml, team-based-rego.yaml) and deployed together after Step 4 using multiple -f flags.

Step 3: Configure Team Navigation

With dashboards created, you need a way for users to access them. Navigation provides the menu structure that guides users to their team-specific dashboards and tools.

Navigation is organized in three levels (Menu → Section → Item) grouped by team. Each menu contains sections that organize related dashboards and tools.

Example: DevOps & Platform Team Navigation Configuration

YAML
# Example: Team-Based Navigation Configuration
# This example shows how to create a custom navigation menu for a specific team.
#
# The DevOps team gets a tailored menu structure focused on their workflows.

globalConfig:
  navigation:
    menus:
      # Menu 1: DevOps Team Menu
      - name: "DevOps Team"
        sections:
          # Section 1: Dashboard Links
          - name: "Dashboards"
            items:
              # Link to main team dashboard
              - name: "Team Overview"
                url: "/team/devops"
                description: "All DevOps clusters"

              # Link to production-only view
              - name: "Production"
                url: "/team/devops/production"
                description: "Production clusters only"

              # Link to environment breakdown
              - name: "By Environment"
                url: "/team/devops/environments"
                description: "Dev, Staging, Prod views"

          # Section 2: External Links (EXAMPLE ONLY - these URLs are fictional)
          - name: "Resources"
            items:
              # Example: Link to team runbook
              - name: "Team Runbook"
                url: "https://docs.example.com/devops/runbook"
                description: "DevOps operational procedures"
                external: true
                requires:
                  - "navigation_devops_resources"

              # Example: Link to team documentation
              - name: "Team Wiki"
                url: "https://wiki.example.com/devops"
                description: "DevOps team wiki"
                external: true
                requires:
                  - "navigation_devops_resources"

      # Menu 2: Platform Team Menu
      - name: "Platform Team"
        sections:
          # Section 1: Dashboard Links
          - name: "Dashboards"
            items:
              # Link to main team dashboard
              - name: "Team Overview"
                url: "/team/platform"
                description: "All Platform clusters"

              # Link to production-only view
              - name: "Production"
                url: "/team/platform/production"
                description: "Production clusters only"

              # Link to environment breakdown
              - name: "By Environment"
                url: "/team/platform/environments"
                description: "Dev, Staging, Prod views"

          # Section 2: External Links (EXAMPLE ONLY - these URLs are fictional)
          - name: "Resources"
            items:
              # Example: Link to team runbook
              - name: "Team Runbook"
                url: "https://docs.example.com/platform/runbook"
                description: "Platform operational procedures"
                external: true
                requires:
                  - "navigation_platform_resources"

              # Example: Link to team documentation
              - name: "Team Wiki"
                url: "https://wiki.example.com/platform"
                description: "Platform team wiki"
                external: true
                requires:
                  - "navigation_platform_resources"

      # Menu 3: Global Admin Menu
      - name: "Global Admin"
        sections:
          # Section 1: Global Dashboard Links
          - name: "Dashboards"
            items:
              # Link to global overview dashboard
              - name: "Global Overview"
                url: "/global/all-clusters"
                description: "All clusters across all teams"

              # Link to by-team view
              - name: "View by Team"
                url: "/global/by-team"
                description: "Clusters organized by team"

          # Section 2: All Teams Quick Access
          - name: "Team Dashboards"
            items:
              - name: "DevOps Team"
                url: "/team/devops"
                description: "DevOps team dashboard"

              - name: "Platform Team"
                url: "/team/platform"
                description: "Platform team dashboard"

# Note: The default "Tools" menu (DNS Query, Cache Management) is automatically
# appended after these custom menus.

# Navigation Visibility:
# - Navigation menus can be conditionally displayed based on user permissions
# - REGO policies control which menus and items a user sees
# - Users only see navigation items they have permission to access
#
# Integration with Dashboards:
# - Menu items link to dashboard URLs defined in team-based-dashboard.yaml
# - Example: "/team/devops" links to devops-overview dashboard

The example creates three custom menus for different teams and access levels:

1. DevOps Team Menu

Primary navigation for DevOps team members:

Dashboards Section: - Links to all three DevOps team dashboards - URLs match dashboard url fields from Step 2 (/team/devops, /team/devops/production, etc.) - Descriptions help users understand what each dashboard shows

Resources Section: - Example external links (fictional URLs for demonstration) - Links to team runbook and wiki - Marked with external: true to indicate they open in new tabs

2. Platform Team Menu

Primary navigation for Platform team members:

Dashboards Section: - Links to all three Platform team dashboards - URLs match Platform dashboard url fields (/team/platform, /team/platform/production, etc.) - Same structure as DevOps menu but for Platform team resources

Resources Section: - Example external links (fictional URLs for demonstration) - Links to team runbook and wiki

3. Global Admin Menu

Administrative navigation for global administrators:

Dashboards Section: - Global Overview: All clusters across all teams (/global/all-clusters) - View by Team: Team-segmented view (/global/by-team)

Team Dashboards Section: - Quick access links to all team dashboards - Allows admins to view any team's perspective

Default Tools Menu

The default "Tools" menu is automatically appended after custom menus and provides:

  • DNS Query: Test DNS resolution against your clusters
  • Flush Cache: Clear DNS cache across clusters

This menu is included by default (globalConfig.defaults.enabled: true) so you don't need to add a custom operations menu for these common tasks.

Navigation item URLs link to dashboard URL patterns:

YAML
1
2
3
4
5
6
7
8
9
# Dashboard configuration (Step 2)
dashboards:
  devops-overview:
    url: /team/devops

# Navigation item (Step 3)
items:
  - name: "Team Overview"
    url: "/team/devops"

When a user clicks "Team Overview" in the navigation:

  1. Glass UI routes to /team/devops
  2. Finds dashboard with matching url: /team/devops
  3. Loads dashboard configuration and renders widgets
  4. Widgets apply filter team = "devops" and display data

Placeholder URLs

When using placeholder dashboards (see "Refactoring with Placeholders"), navigation items use concrete URLs (/team/devops) that match parameterized dashboard patterns (/team/:team).

Navigation items are automatically shown or hidden based on user permissions:

  • Dashboard links (e.g., url: "/team/devops") - visible only if user can access that dashboard
  • External links (e.g., team wikis) - can be controlled with navigation item requires field
  • Internal actions (e.g., /dns-query) - protected by application-level permissions

Navigation items can optionally specify their own requires field for additional control:

YAML
1
2
3
4
5
- name: "Team Runbook"
  url: "https://docs.example.com/devops/runbook"
  external: true
  requires:
    - "navigation_devops_resources"

Result: Each user sees only the menus and links they have permission to access.

Notice the connection:

  • Step 2 created dashboard with url: /team/devops
  • Step 3 creates navigation item with url: "/team/devops"
  • Result: Clicking navigation item takes user to team dashboard

This is the second integration point. The next step adds access control so only authorized users can see and use these navigation items and dashboards.

Step 4: Set REGO Policies

With dashboards and navigation in place, the final critical step is implementing authorization policies. REGO policies ensure that users can only access their team's clusters and perform operations appropriate to their roles.

REGO policies control access by evaluating user roles against cluster labels. The policy engine receives user and cluster context as input, then evaluates specific permission rules:

Rego
1
2
3
4
5
# Input structure for team-based authorization
{
  "user": {"roles": ["devops", "production", "observer"]},
  "cluster": {"labels": {"team": "devops", "environment": "production"}}
}

When checking if a user can perform an operation (e.g., read_logs), the policy engine queries the corresponding rule directly (e.g., data.pdns_permissions.read_logs) using this input context.

Note: The user data structure depends on how the OIDC server provides user data.

Example: Static Users Configuration

First, let's look at the user definitions. In production, these would come from OIDC, but for this example we use static users:

YAML
# Example: Team-Based REGO Authorization Policies
policy:
  # Static Users Configuration
  # In production, users would come from OIDC with team claims
  staticUsers:
    # DevOps Team Lead - Full access to DevOps team clusters
    devops-lead:
      id: "devops-lead"
      password: "secret"
      sub: "devops-lead"
      name: "DevOps Team Lead"
      email: "lead@devops.example.com"
      roles:
        - "devops"              # Team membership
        - "team-lead"           # Leadership role
        - "production"          # Can access production
        - "staging"             # Can access staging
        - "development"         # Can access development
        - "operator"            # Can restart instances
        - "observer"            # Can read logs

    # DevOps Developer - Limited access to non-prod
    devops-developer:
      id: "devops-developer"
      password: "secret"
      sub: "devops-developer"
      name: "DevOps Developer"
      email: "dev@devops.example.com"
      roles:
        - "devops"              # Team membership
        - "developer"           # Developer role
        - "staging"             # Can access staging
        - "development"         # Can access development
        - "observer"            # Can read logs
        # Note: No "production" role - cannot see prod clusters
        # Note: No "operator" role - cannot restart instances

    # Platform Team Member - Access to Platform team clusters only
    platform-engineer:
      id: "platform-engineer"
      password: "secret"
      sub: "platform-engineer"
      name: "Platform Engineer"
      email: "eng@platform.example.com"
      roles:
        - "platform"            # Different team
        - "production"
        - "staging"
        - "development"
        - "operator"
        - "observer"
      # Note: Cannot see DevOps team clusters

    # Global Administrator - Access to all teams
    admin:
      id: "admin"
      password: "secret"
      sub: "admin"
      name: "Global Administrator"
      email: "admin@example.com"
      roles:
        - "admin"               # Admin bypass
        - "global"              # Global access

  # REGO Authorization Policies

User Roles Explained

DevOps Team Lead: - Roles: devops, team-lead, production, staging, development, operator, observer - Can access: All DevOps team environments - Can perform: All operations (read logs, clear cache, restart instances)

DevOps Developer: - Roles: devops, developer, staging, development, observer - Can access: Staging and development only (no production role) - Can perform: Read-only operations (read logs, DNS checks - no operator role means no cache clearing or instance management)

Platform Engineer: - Roles: platform, production, staging, development, operator, observer - Can access: Platform team clusters only (different team) - Cannot see: DevOps team clusters

Global Administrator: - Roles: admin, global - Can access: All clusters (admin bypass) - Can perform: All operations

SPOG uses three REGO packages: pdns_permissions (operation permissions like read_logs, restart_instance_set), pdns_global_flags (UI access permissions for dashboards and navigation), and user (helper functions for team membership and environment access checks).

REGO Policy Breakdown

The policies define three packages:

1. pdns_permissions.rego - Operation Permissions

This package defines well-known operations that services check:

Rego
# Basic connection - all authenticated users
connect if true

# Read cluster state - requires team membership and visibility
read if user.can_see_cluster

# Read logs - requires observer role + cluster visibility
read_logs if user.can_observe_cluster

# Clear cache - requires operator role + cluster visibility (destructive operation)
clear_cache if user.can_manage_instances

# Restart instances - requires operator role + cluster visibility
restart_instance_set if user.can_manage_instances

# Delete pods - requires operator role + cluster visibility
delete_pod if user.can_manage_instances

# DNS check - requires observer role + cluster visibility
dns_check if user.can_observe_cluster

Each permission references functions from the user package that evaluate access based on roles and cluster labels. The admin bypass is built into the helper functions themselves (see user.rego below).

2. pdns_global_flags.rego - Dashboard and Navigation Permissions

This package defines custom permission flags for UI access control:

Rego
# DevOps team dashboard flags
dashboard_devops_overview if "devops" in input.user.roles
dashboard_devops_production if "devops" in input.user.roles
dashboard_devops_environments if "devops" in input.user.roles

# Platform team dashboard flags
dashboard_platform_overview if "platform" in input.user.roles
dashboard_platform_production if "platform" in input.user.roles
dashboard_platform_environments if "platform" in input.user.roles

# Global admin dashboard flags
dashboard_global_admin if "admin" in input.user.roles

# Navigation item access flags
navigation_devops_resources if "devops" in input.user.roles
navigation_platform_resources if "platform" in input.user.roles

# Admin has access to everything
dashboard_devops_overview if "admin" in input.user.roles
dashboard_platform_overview if "admin" in input.user.roles
navigation_devops_resources if "admin" in input.user.roles
navigation_platform_resources if "admin" in input.user.roles

These flags are referenced by dashboard requires fields and navigation item requires fields to control visibility. Note that this package accesses input.user.roles directly, unlike pdns_permissions.rego which uses helper functions from the user package.

3. user.rego - User Authorization Logic

This package contains the core authorization logic:

Role Convenience Flags:

Rego
roles := input.user.roles

# Role Flags
admin if "admin" in roles
team_lead if "team-lead" in roles
developer if "developer" in roles
operator if "operator" in roles
observer if "observer" in roles

# Team Membership
is_devops_team_member if "devops" in roles
is_platform_team_member if "platform" in roles

These convenience flags simplify checking if a user has specific roles. Instead of writing "observer" in input.user.roles everywhere, we can just check observer. The roles variable is set once at the top of the package from input.user.roles.

Team-Based Cluster Visibility:

Rego
1
2
3
4
5
# User must belong to the same team as the cluster
has_matching_team if {
  some team in roles
  team in input.cluster.labels.team
}

This rule checks if any of the user's roles matches the cluster's team label. For example: - User roles: ["devops", "production", "observer"] - Cluster labels: {team: "devops", environment: "production"} - Evaluation: "devops" in roles equals "devops" in cluster labels → TRUE

Environment Access:

Rego
1
2
3
4
5
# User must have role matching cluster environment
has_matching_environment if {
  some env in input.cluster.labels.environment
  env in roles
}

This rule checks if the cluster's environment is in the user's roles: - User roles: ["devops", "staging", "development", "observer"] - Cluster labels: {team: "devops", environment: "production"} - Evaluation: "production" not in roles → FALSE (user cannot access production)

Cluster Visibility Rule:

Rego
1
2
3
4
5
# User can see cluster if team AND environment match
can_see_cluster if {
  has_matching_team
  has_matching_environment
}

Both team and environment must match for visibility. This implements the dual-gating strategy: 1. Must be same team 2. Must have environment access

Observer Capabilities:

Rego
1
2
3
4
5
6
7
8
# Can read logs and check DNS if observer role + visibility
can_observe_cluster if {
  can_see_cluster
  observer
}

# Admin bypass
can_observe_cluster if admin

Observer role grants read-only access: view logs, check DNS, read cluster state. Requires cluster visibility first. Admins have observer capabilities on all clusters.

Operator Capabilities:

Rego
1
2
3
4
5
6
7
8
# Can restart instances and delete pods if operator role + visibility
can_manage_instances if {
  can_see_cluster
  operator
}

# Admin bypass
can_manage_instances if admin

Operator role grants infrastructure management: restart instances, delete pods. Requires cluster visibility first. Admins have operator capabilities on all clusters.

Complete REGO Policy Configuration

Here's the complete policy configuration with all three packages:

YAML
# Example: Team-Based REGO Authorization Policies
policy:
  # Static Users Configuration
  # In production, users would come from OIDC with team claims
  policies:
    # Operation Permission Definitions
    # These define well-known operations that services check
    pdns_permissions.rego: |
      package pdns_permissions

      import data.user

      # Basic connection - all authenticated users
      connect if true

      # Read cluster state - requires team membership and visibility
      read if user.can_see_cluster

      # Read logs - requires observer role + cluster visibility
      read_logs if user.can_observe_cluster

      # Clear DNS cache - requires operator role + cluster visibility (destructive operation)
      clear_cache if user.can_manage_instances

      # Restart instances - requires operator role + cluster visibility
      restart_instance_set if user.can_manage_instances

      # Delete pods - requires operator role + cluster visibility
      delete_pod if user.can_manage_instances

      # DNS check - requires observer role + cluster visibility
      dns_check if user.can_observe_cluster

    # Dashboard and Navigation Permission Flags
    # These are custom flags for controlling UI access
    pdns_global_flags.rego: |
      package pdns_global_flags

      # Dashboard access flags
      dashboard_devops_overview if "devops" in input.user.roles
      dashboard_devops_overview if "admin" in input.user.roles

      dashboard_devops_production if "devops" in input.user.roles
      dashboard_devops_production if "admin" in input.user.roles

      dashboard_devops_environments if "devops" in input.user.roles
      dashboard_devops_environments if "admin" in input.user.roles

      dashboard_platform_overview if "platform" in input.user.roles
      dashboard_platform_overview if "admin" in input.user.roles

      dashboard_platform_production if "platform" in input.user.roles
      dashboard_platform_production if "admin" in input.user.roles

      dashboard_platform_environments if "platform" in input.user.roles
      dashboard_platform_environments if "admin" in input.user.roles

      dashboard_global_admin if "admin" in input.user.roles

      # Navigation item access flags
      navigation_devops_resources if "devops" in input.user.roles
      navigation_devops_resources if "admin" in input.user.roles

      navigation_platform_resources if "platform" in input.user.roles
      navigation_platform_resources if "admin" in input.user.roles

    # User Authorization Logic
    # This evaluates whether a user can access specific clusters
    user.rego: |
      package user

      roles := input.user.roles

      # Role Flags
      admin if "admin" in roles
      team_lead if "team-lead" in roles
      developer if "developer" in roles
      operator if "operator" in roles
      observer if "observer" in roles

      # Team Membership
      is_devops_team_member if "devops" in roles
      is_platform_team_member if "platform" in roles

      # Team-Based Cluster Visibility
      # User must belong to the same team as the cluster
      has_matching_team if {
        some team in roles
        team in input.cluster.labels.team
      }

      # Admin bypass - admins see all clusters regardless of team
      has_matching_team if admin

      # Environment Access
      # User must have role matching cluster environment
      has_matching_environment if {
        some env in input.cluster.labels.environment
        env in roles
      }

      # Admin bypass for environment
      has_matching_environment if admin

      # Cluster Visibility Rule
      # User can see cluster if team AND environment match
      can_see_cluster if {
        has_matching_team
        has_matching_environment
      }

      # Observer Capabilities
      # Can read logs and check DNS if observer role + visibility
      can_observe_cluster if {
        can_see_cluster
        observer
      }

      can_observe_cluster if admin

      # Operator Capabilities
      # Can restart instances and delete pods if operator role + visibility
      can_manage_instances if {
        can_see_cluster
        operator
      }

      can_manage_instances if admin

Authorization Flow Example

Let's trace a complete authorization decision:

Scenario: DevOps Developer tries to read logs from production cluster

Input (passed to policy engine when querying data.pdns_permissions.read_logs):

JSON
{
  "user": {
    "roles": ["devops", "developer", "staging", "development", "observer"]
  },
  "cluster": {
    "labels": {
      "team": "devops",
      "environment": "production"
    }
  }
}

Evaluation:

  1. Check has_matching_team:
  2. User roles contain "devops"
  3. Cluster team is "devops"
  4. Result: TRUE ✓

  5. Check has_matching_environment:

  6. User roles are ["devops", "developer", "staging", "development", "observer"]
  7. Cluster environment is "production"
  8. "production" in roles? → FALSE
  9. Result: FALSE ✗

  10. Check can_see_cluster:

  11. Requires: has_matching_team AND has_matching_environment
  12. Evaluation: TRUE AND FALSE → FALSE
  13. Result: FALSE ✗

  14. Check read_logs permission:

  15. Requires: can_observe_cluster
  16. can_observe_cluster requires: can_see_cluster AND observer role
  17. Evaluation: FALSE AND TRUE → FALSE
  18. Result: DENIED ✗

Outcome: DevOps Developer CANNOT read logs from production cluster because they lack the "production" role, even though they: - Belong to the correct team - Have the observer role - Are authenticated

This demonstrates environment-based isolation within a team.

Another Example: DevOps Team Lead Accesses Production

Scenario: DevOps Team Lead tries to restart instance on production cluster

Input (passed to policy engine when querying data.pdns_permissions.restart_instance_set):

JSON
{
  "user": {
    "roles": ["devops", "team-lead", "production", "staging", "development", "operator", "observer"]
  },
  "cluster": {
    "labels": {
      "team": "devops",
      "environment": "production"
    }
  }
}

Evaluation:

  1. Check has_matching_team:
  2. User roles contain "devops"
  3. Cluster team is "devops"
  4. Result: TRUE ✓

  5. Check has_matching_environment:

  6. User roles contain "production"
  7. Cluster environment is "production"
  8. Result: TRUE ✓

  9. Check can_see_cluster:

  10. Requires: has_matching_team AND has_matching_environment
  11. Evaluation: TRUE AND TRUE → TRUE
  12. Result: TRUE ✓

  13. Check can_manage_instances:

  14. Requires: can_see_cluster AND operator role
  15. User has operator role
  16. Evaluation: TRUE AND TRUE → TRUE
  17. Result: TRUE ✓

  18. Check restart_instance_set permission:

  19. Requires: can_manage_instances OR admin
  20. Evaluation: TRUE
  21. Result: ALLOWED ✓

Outcome: DevOps Team Lead CAN restart instances on production cluster because they have: - Correct team membership - Production environment access - Operator role for infrastructure operations

Integration Point: Policies Protect Dashboards and Navigation

Notice the complete integration:

  • Step 1 labeled clusters with team: "devops"
  • Step 2 created dashboards with requires: ["dashboard_devops_overview"]
  • Step 3 created navigation linking to those dashboards
  • Step 4 implements dashboard_devops_overview REGO permission flag checking team membership

Result: Only users with devops role (or admin role) can: - See "DevOps Team" navigation menu - Access /team/devops dashboard - View DevOps clusters in widgets - Perform operations on DevOps clusters

This is the complete security model enforcing team-based isolation.

Deploy Steps 2-4: Glass UI with Complete Configuration

Deploy Glass UI with dashboards, navigation, and REGO policies:

Bash
1
2
3
4
5
6
7
8
9
helm install glass-ui \
  oci://registry.open-xchange.com/cc-glass/glass-ui \
  --version "1.0.0" \
  --namespace controlplane \
  --set ui.ingress.host="$GLASS_HOSTNAME" \
  --set global.imagePullSecretsList[0]=registry-credentials \
  -f team-based-dashboard.yaml \
  -f team-based-navigation.yaml \
  -f team-based-rego.yaml
Bash
1
2
3
4
5
6
7
8
9
helm install glass-ui \
  oci://registry.open-xchange.com/cc-glass/glass-ui \
  --version "1.0.0" \
  --namespace controlplane \
  --set global.imagePullSecretsList[0]=registry-credentials \
  --set "ui.config.nats.serverUrl=ws://localhost:8222" \
  -f team-based-dashboard.yaml \
  -f team-based-navigation.yaml \
  -f team-based-rego.yaml

Then start port-forwards:

Bash
kubectl port-forward svc/glass-ui 8080:80 -n controlplane &
kubectl port-forward svc/glass-nats 8222:8080 -n controlplane &
Bash
helm install glass-ui \
  oci://registry.open-xchange.com/cc-glass/glass-ui \
  --version "1.0.0" \
  --namespace controlplane \
  --set global.imagePullSecretsList[0]=registry-credentials \
  --set ui.service.type=NodePort \
  --set ui.service.nodePort=31080 \
  --set "ui.config.nats.serverUrl=ws://${NODE_IP}:31222" \
  -f team-based-dashboard.yaml \
  -f team-based-navigation.yaml \
  -f team-based-rego.yaml

Each values file configures one aspect of the Glass UI:

  • team-based-dashboard.yaml - Dashboard definitions with team-based filtering
  • team-based-navigation.yaml - Navigation menu structure for all teams
  • team-based-rego.yaml - REGO policies for authorization and static user definitions

After deployment, access Glass UI at https://$GLASS_HOSTNAME and log in with one of the configured users to see the team-based isolation in action.

After deployment, access Glass UI at http://localhost:8080 and log in with one of the configured users to see the team-based isolation in action.

After deployment, access Glass UI at http://<node-ip>:31080 and log in with one of the configured users to see the team-based isolation in action.

Login Credentials:

Username Password Access
devops-lead secret DevOps team, all environments, operator permissions
devops-developer secret DevOps team, staging/dev only, read-only
platform-engineer secret Platform team, all environments, operator permissions
admin secret Global administrator, full access

What You Accomplished

Step 1: Labeled Clusters - Applied team, environment, and role labels to user plane clusters - Created the foundation for all subsequent filtering and access control

Step 2: Created Team Dashboards - Built dashboards filtered by team = "devops" expressions - Configured multiple views (all clusters, production only, by environment) - Linked dashboards to REGO permissions

Step 3: Configured Team Navigation - Created hierarchical menu structure for team - Linked navigation items to dashboard URLs - Organized tools and resources for easy access

Step 4: Implemented REGO Policies - Defined team-based cluster visibility rules - Implemented environment-based access restrictions - Created role-based operation permissions (observer, operator) - Configured static users with different access levels

Try It Yourself

Now that you understand how the components work together, here are some modifications you can try to deepen your understanding.

Need Help?

If you get stuck, reference solutions are available in the Team-Based Deployment Solutions guide.

Add a New Team

Add a "security" team with their own clusters and access. You'll need to:

  1. Create a new cluster with security team labels
  2. Add a security team dashboard to your dashboard configuration
  3. Add a navigation menu for the security team
  4. Add REGO policies granting dashboard access to users with the security role
  5. Add a security team user to test access

Change Dashboard Filters

Modify existing dashboards to show different cluster subsets. Try creating filters that:

  • Show only recursors across all teams
  • Show production clusters from multiple teams
  • Exclude development environments

Add a Cross-Team Dashboard

Create a dashboard visible to multiple teams for shared infrastructure. This requires:

  • A dashboard configuration with a filter showing shared resources (e.g., all authoritative servers)
  • REGO rules that grant access to users from multiple teams

Restrict Operator Access by Environment

Modify the REGO policy to only allow operator actions in non-production. Consider:

  • How to check the cluster's environment label
  • Whether to require an additional role for production operations
  • How to structure the rules for clarity

Add a New User Role

Create a "read-only admin" role that can see all clusters but cannot perform operations. Think about:

  • Which team roles to include for visibility
  • Which environment roles to include
  • Which operation roles to omit (operator, content-manager)

Refactoring with Parameterized Routes

As your deployment grows, you may notice repetitive dashboard definitions—one for each team with nearly identical structure. SPOG supports parameterized routes that let you create generic, reusable configurations with dynamic permission checks.

Understanding Route Parameters

SPOG uses route parameters (:paramName) to create dynamic dashboards:

:paramName - For Display and Filtering

Used in dashboard URLs, titles, descriptions, and filter expressions:

  • URL pattern: /team/:team defines a route parameter named team
  • When user visits /team/devops, the :team placeholder becomes "devops"
  • When user visits /team/platform, the :team placeholder becomes "platform"

Dynamic Permission Checking

The requires field supports object format with arguments that pass route parameters to REGO policies:

YAML
1
2
3
4
5
requires:
  - permission: "see_team_dashboard"
    arguments:
      - name: team
        value: ":team"   # Passes route parameter to REGO

When user visits /team/devops:

  1. The :team value is extracted as "devops"
  2. REGO policy receives input.arguments.team = "devops"
  3. Policy evaluates: input.arguments.team in input.user.roles
  4. User with devops role gains access; others are denied

This means a single dashboard definition can serve all teams while maintaining proper access control through dynamic REGO evaluation.

Before: Repetitive Dashboard Definitions

The original configuration requires a separate dashboard for each team:

YAML
# One dashboard per team - repetitive!
devops-overview:
  title: "DevOps Team Dashboard"
  url: /team/devops
  graphs:
    - widget: "cc-state-tree-table"
      args:
        filter: 'team = "devops"'

platform-overview:
  title: "Platform Team Dashboard"
  url: /team/platform
  graphs:
    - widget: "cc-state-tree-table"
      args:
        filter: 'team = "platform"'

# Need to add another dashboard for each new team...

After: Generic Dashboard with Route Parameters

With parameterized routes and dynamic permissions, one definition works for all teams:

YAML
# Single dashboard serves ALL teams
team-overview:
  title: ":team Team Dashboard"
  url: /team/:team
  requires:
    - permission: "see_team_dashboard"
      arguments:
        - name: team
          value: ":team"    # Route parameter passed to REGO
  graphs:
    - widget: "cc-state-tree-table"
      title: ":team Clusters"
      args:
        filter: 'team = ":team"'

    - widget: "cc-state-readiness-heatmap"
      title: "Cluster Health"
      args:
        filter: 'team = ":team"'

Now: - /team/devops shows "devops Team Dashboard" with filter team = "devops" and REGO receives input.arguments.team = "devops" - /team/platform shows "platform Team Dashboard" with filter team = "platform" and REGO receives input.arguments.team = "platform" - /team/security shows "security Team Dashboard" with filter team = "security" and REGO receives input.arguments.team = "security"

Adding a new team requires only adding navigation links and assigning the team role to users.

Refactored Navigation

Navigation items link to the parameterized URLs:

YAML
navigation:
  menus:
    - name: "DevOps Team"
      sections:
        - name: "Dashboards"
          items:
            - name: "Team Overview"
              url: "/team/devops"           # Fills :team with "devops"
            - name: "Production"
              url: "/team/devops/production"

    - name: "Platform Team"
      sections:
        - name: "Dashboards"
          items:
            - name: "Team Overview"
              url: "/team/platform"         # Fills :team with "platform"
            - name: "Production"
              url: "/team/platform/production"

Refactored REGO Policies

With dynamic permission arguments, one REGO rule handles all teams. The input.arguments object receives the route parameter value:

YAML
1
2
3
4
5
6
7
# Dashboard passes route parameter as argument
team-overview:
  requires:
    - permission: "see_team_dashboard"
      arguments:
        - name: team
          value: ":team"    # For /team/devops, REGO receives input.arguments.team = "devops"
Rego
# Single rule handles ALL teams dynamically
see_team_dashboard if {
  input.arguments.team in input.user.roles
}
see_team_dashboard if "admin" in input.user.roles

# For multi-argument scenarios (team + tier)
see_team_tier_dashboard if {
  input.arguments.team in input.user.roles
  input.arguments.tier in input.user.roles
}
see_team_tier_dashboard if "admin" in input.user.roles

How it works:

  1. User visits /team/devops
  2. Dashboard config passes arguments: [{name: "team", value: ":team"}]
  3. UI extracts :team as "devops" and sends to policy engine: {arguments: {team: "devops"}}
  4. REGO evaluates: "devops" in input.user.roles
  5. User with devops role → access granted; user with platform role → access denied

This eliminates team-specific REGO rules entirely—one rule handles unlimited teams.

Benefits of Parameterized Routes

Aspect Before (Hardcoded) After (Parameterized)
Adding a team Add dashboard + navigation + REGO Add navigation link only
Dashboard count One per team (N dashboards) One generic (1 dashboard)
REGO rules One rule per team One rule for all teams
Maintenance Update each dashboard separately Update once, applies to all
Consistency Risk of drift between teams Guaranteed identical structure
Permission security Per-dashboard requires Dynamic via input.arguments

No Per-Team REGO Rules

With dynamic permission arguments, one REGO rule handles all teams. The rule see_team_dashboard if { input.arguments.team in input.user.roles } automatically works for any team—DevOps, Platform, Security, or any team you add in the future. Just assign the team role to users and add navigation links.

Refactored Dashboard Configuration

YAML
# Example: Team-Based Dashboard Configuration with Parameterized Routes
#
# This example demonstrates how to use route parameters to create
# generic, reusable dashboard configurations with dynamic permission checking.
#
# Benefits:
# - Single dashboard definition serves all teams
# - Add new teams without changing dashboard OR REGO config
# - Cleaner, more maintainable configuration
# - Dynamic permission checking via input.arguments
#
# How it works:
# - URLs like /team/:team contain route parameters
# - :team in titles and filters is replaced with the route parameter value
# - The requires field passes arguments to REGO for dynamic evaluation
# - Example: /team/devops -> REGO receives input.arguments.team = "devops"

globalConfig:
  dashboards:
    # Generic team dashboard - works for ANY team
    # Permission uses arguments - e.g., /team/devops passes team="devops" to REGO
    team-overview:
      title: ":team Team Dashboard"
      description: "All :team team PowerDNS clusters"
      url: /team/:team
      requires:
        - permission: "see_team_dashboard"
          arguments:
            - name: team
              value: ":team"    # Route parameter passed to REGO
      graphs:
        # Show all clusters for this team
        - widget: "cc-state-tree-table"
          title: ":team Clusters"
          args:
            filter: 'team = ":team"'

        # Health heatmap for this team
        - widget: "cc-state-readiness-heatmap"
          title: "Cluster Health"
          args:
            filter: 'team = ":team"'

    # Generic production view - works for ANY team
    team-production:
      title: ":team Production"
      description: "Production clusters for :team team"
      url: /team/:team/production
      requires:
        - permission: "see_team_tier_dashboard"
          arguments:
            - name: team
              value: ":team"
            - name: tier
              value: "production"    # Static value
      graphs:
        - widget: "cc-state-tree-table"
          title: "Production Clusters"
          args:
            filter: 'team = ":team" and environment = "production"'

        - widget: "cc-state-readiness-pie-charts"
          title: "Production Readiness Distribution"
          args:
            filter: 'team = ":team" and environment = "production"'

    # Generic environment view - works for ANY team
    team-environments:
      title: ":team Environments"
      description: "All environments for :team team"
      url: /team/:team/environments
      requires:
        - permission: "see_team_dashboard"
          arguments:
            - name: team
              value: ":team"
      graphs:
        - widget: "cc-state-tree-table"
          title: "Development"
          args:
            filter: 'team = ":team" and environment = "development"'
        - widget: "cc-state-tree-table"
          title: "Staging"
          args:
            filter: 'team = ":team" and environment = "staging"'
        - widget: "cc-state-tree-table"
          title: "Production"
          args:
            filter: 'team = ":team" and environment = "production"'

# Comparison: Before vs After Parameterized Routes
# ================================================
#
# BEFORE (hardcoded - one dashboard per team, one REGO rule per team):
# --------------------------------------------------------------------
# devops-overview:
#   title: "DevOps Team Dashboard"
#   url: /team/devops
#   requires:
#     - "dashboard_devops_overview"    # String format
#   filter: 'team = "devops"'
#
# platform-overview:
#   title: "Platform Team Dashboard"
#   url: /team/platform
#   requires:
#     - "dashboard_platform_overview"  # Need separate REGO rule!
#   filter: 'team = "platform"'
#
# AFTER (parameterized - one dashboard for all teams, one REGO rule):
# -------------------------------------------------------------------
# team-overview:
#   title: ":team Team Dashboard"
#   url: /team/:team
#   requires:
#     - permission: "see_team_dashboard"
#       arguments:
#         - name: team
#           value: ":team"             # Dynamic argument
#   filter: 'team = ":team"'
#
# Works for ALL teams - just add navigation links!
# /team/devops -> REGO receives input.arguments.team = "devops"
# /team/platform -> REGO receives input.arguments.team = "platform"
# /team/security -> REGO receives input.arguments.team = "security"

Refactored Navigation Configuration

YAML
# Example: Team-Based Navigation Configuration with Placeholders
#
# Navigation links point to parameterized dashboard URLs.
# When a user clicks "/team/devops", the :team placeholder in the
# dashboard definition is filled with "devops".
#
# Adding a new team only requires:
# 1. Add a new menu section here with links to /team/<new-team>
# 2. Add REGO permissions for the new team
# No dashboard changes needed!

globalConfig:
  navigation:
    menus:
      # DevOps Team Menu - links to /team/devops (fills :team with "devops")
      - name: "DevOps Team"
        sections:
          - name: "Dashboards"
            items:
              - name: "Team Overview"
                url: "/team/devops"
                description: "All DevOps clusters"
              - name: "Production"
                url: "/team/devops/production"
                description: "Production clusters only"
              - name: "By Environment"
                url: "/team/devops/environments"
                description: "Dev, Staging, Prod views"

          # External Links (EXAMPLE ONLY - these URLs are fictional)
          - name: "Resources"
            items:
              - name: "Team Runbook"
                url: "https://docs.example.com/devops/runbook"
                description: "DevOps operational procedures"
                external: true
                requires:
                  - "navigation_devops_resources"

              - name: "Team Wiki"
                url: "https://wiki.example.com/devops"
                description: "DevOps team wiki"
                external: true
                requires:
                  - "navigation_devops_resources"

      # Platform Team Menu - links to /team/platform (fills :team with "platform")
      - name: "Platform Team"
        sections:
          - name: "Dashboards"
            items:
              - name: "Team Overview"
                url: "/team/platform"
                description: "All Platform clusters"
              - name: "Production"
                url: "/team/platform/production"
                description: "Production clusters only"
              - name: "By Environment"
                url: "/team/platform/environments"
                description: "Dev, Staging, Prod views"

          # External Links (EXAMPLE ONLY - these URLs are fictional)
          - name: "Resources"
            items:
              - name: "Team Runbook"
                url: "https://docs.example.com/platform/runbook"
                description: "Platform operational procedures"
                external: true
                requires:
                  - "navigation_platform_resources"

              - name: "Team Wiki"
                url: "https://wiki.example.com/platform"
                description: "Platform team wiki"
                external: true
                requires:
                  - "navigation_platform_resources"

      # Operations Menu (shared across teams)
      - name: "Operations"
        sections:
          - name: "DNS Operations"
            items:
              - name: "DNS Check"
                url: "/dns-query"

              - name: "Cache Management"
                url: "/clear-cache/all"

# Adding a New Team
# =================
#
# To add a "Security" team, just add this menu section:
#
# - name: "Security Team"
#   sections:
#     - name: "Dashboards"
#       items:
#         - name: "Team Overview"
#           url: "/team/security"
#         - name: "Production"
#           url: "/team/security/production"
#         - name: "By Environment"
#           url: "/team/security/environments"
#
# The existing placeholder dashboards will automatically work for the new team.
# You only need to add REGO permissions (see team-based-placeholder-rego.yaml).

Refactored REGO Configuration

YAML
# Example: Team-Based REGO Authorization Policies with Dynamic Arguments
#
# With dynamic permission arguments, one REGO rule handles all teams.
# The input.arguments object receives values from dashboard requires fields,
# enabling truly dynamic access control without per-team rules.
#
# How it works:
# - Dashboard config passes arguments: [{name: "team", value: ":team"}]
# - UI extracts route parameter and sends to policy engine
# - REGO evaluates: input.arguments.team in input.user.roles
# - User with matching team role gains access

policy:
  # Static Users Configuration
  # In production, users would come from OIDC with team claims
  staticUsers:
    # DevOps Team Lead - Full access to DevOps team clusters
    devops-lead:
      id: "devops-lead"
      password: "secret"
      sub: "devops-lead"
      name: "DevOps Team Lead"
      email: "lead@devops.example.com"
      roles:
        - "devops"              # Team membership
        - "team-lead"           # Leadership role
        - "production"          # Can access production
        - "staging"             # Can access staging
        - "development"         # Can access development
        - "operator"            # Can restart instances
        - "observer"            # Can read logs

    # DevOps Developer - Limited access to non-prod
    devops-developer:
      id: "devops-developer"
      password: "secret"
      sub: "devops-developer"
      name: "DevOps Developer"
      email: "dev@devops.example.com"
      roles:
        - "devops"              # Team membership
        - "developer"           # Developer role
        - "staging"             # Can access staging
        - "development"         # Can access development
        - "observer"            # Can read logs
        # Note: No "production" role - cannot see prod clusters
        # Note: No "operator" role - cannot restart instances

    # Platform Team Member - Access to Platform team clusters only
    platform-engineer:
      id: "platform-engineer"
      password: "secret"
      sub: "platform-engineer"
      name: "Platform Engineer"
      email: "eng@platform.example.com"
      roles:
        - "platform"            # Different team
        - "production"
        - "staging"
        - "development"
        - "operator"
        - "observer"
      # Note: Cannot see DevOps team clusters

    # Global Administrator - Access to all teams
    admin:
      id: "admin"
      password: "secret"
      sub: "admin"
      name: "Global Administrator"
      email: "admin@example.com"
      roles:
        - "admin"               # Admin bypass
        - "global"              # Global access

  # REGO Authorization Policies
  policies:
    # Operation Permission Definitions
    # These define well-known operations that services check
    pdns_permissions.rego: |
      package pdns_permissions

      import data.user

      # Basic connection - all authenticated users
      connect if true

      # Read cluster state - requires team membership and visibility
      read if user.can_see_cluster

      # Read logs - requires observer role + cluster visibility
      read_logs if user.can_observe_cluster

      # Clear DNS cache - requires operator role + cluster visibility (destructive operation)
      clear_cache if user.can_manage_instances

      # Restart instances - requires operator role + cluster visibility
      restart_instance_set if user.can_manage_instances

      # Delete pods - requires operator role + cluster visibility
      delete_pod if user.can_manage_instances

      # DNS check - requires observer role + cluster visibility
      dns_check if user.can_observe_cluster

    # Dashboard and Navigation Permission Flags
    # Dynamic permission checking using input.arguments
    pdns_global_flags.rego: |
      package pdns_global_flags

      # =============================================================================
      # DYNAMIC PERMISSION FLAGS (using input.arguments)
      # =============================================================================

      # Dynamic team dashboard access
      # Uses input.arguments.team passed from the UI based on route parameters
      # Example: requires: [{ permission: "see_team_dashboard", arguments: [{ name: "team", value: ":team" }] }]
      see_team_dashboard if {
        input.arguments.team in input.user.roles
      }
      see_team_dashboard if "admin" in input.user.roles

      # Dynamic team and tier dashboard access
      # Uses multiple arguments from route parameters or static values
      # Example: requires: [{ permission: "see_team_tier_dashboard", arguments: [{ name: "team", value: ":team" }, { name: "tier", value: "production" }] }]
      see_team_tier_dashboard if {
        input.arguments.team in input.user.roles
        input.arguments.tier in input.user.roles
      }
      see_team_tier_dashboard if "admin" in input.user.roles

      # =============================================================================
      # STATIC PERMISSION FLAGS (for non-parameterized routes)
      # =============================================================================

      # Global overview - accessible to all authenticated users
      dashboard_global_overview if {
        not "machine" in input.user.roles
        not input.robot
      }

      # Navigation item access flags
      navigation_devops_resources if "devops" in input.user.roles
      navigation_devops_resources if "admin" in input.user.roles

      navigation_platform_resources if "platform" in input.user.roles
      navigation_platform_resources if "admin" in input.user.roles

    # User Authorization Logic
    # This evaluates whether a user can access specific clusters
    user.rego: |
      package user

      roles := input.user.roles

      # Role Flags
      admin if "admin" in roles
      team_lead if "team-lead" in roles
      developer if "developer" in roles
      operator if "operator" in roles
      observer if "observer" in roles

      # Team Membership
      is_devops_team_member if "devops" in roles
      is_platform_team_member if "platform" in roles

      # Team-Based Cluster Visibility
      # User must belong to the same team as the cluster
      has_matching_team if {
        some team in roles
        team in input.cluster.labels.team
      }

      # Admin bypass - admins see all clusters regardless of team
      has_matching_team if admin

      # Environment Access
      # User must have role matching cluster environment
      has_matching_environment if {
        some env in input.cluster.labels.environment
        env in roles
      }

      # Admin bypass for environment
      has_matching_environment if admin

      # Cluster Visibility Rule
      # User can see cluster if team AND environment match
      can_see_cluster if {
        has_matching_team
        has_matching_environment
      }

      # Observer Capabilities
      # Can read logs and check DNS if observer role + visibility
      can_observe_cluster if {
        can_see_cluster
        observer
      }

      can_observe_cluster if admin

      # Operator Capabilities
      # Can restart instances and delete pods if operator role + visibility
      can_manage_instances if {
        can_see_cluster
        operator
      }

      can_manage_instances if admin

# Adding a New Team
# =================
#
# With dynamic permissions, adding a new team is simple:
#
# 1. Assign the team role to users (e.g., "security" role)
# 2. Add navigation links pointing to /team/security
#
# That's it! No REGO changes needed.
#
# The see_team_dashboard rule automatically works:
# - User visits /team/security
# - Dashboard passes arguments: [{name: "team", value: ":team"}]
# - REGO receives input.arguments.team = "security"
# - Rule evaluates: "security" in input.user.roles
# - User with "security" role gains access

Complete Configuration Reference

The complete team-based configuration is split across three files for maintainability. Each file handles one aspect of the Glass UI configuration:

Dashboard Configuration

YAML
# Example: Team-Based Dashboard Configuration

globalConfig:
  dashboards:
    # Dashboard 1: DevOps Team Overview
    # Main dashboard showing all DevOps team clusters
    devops-overview:
      title: "DevOps Team Dashboard"
      description: "PowerDNS clusters managed by the DevOps team"
      url: /team/devops
      requires:
        - "dashboard_devops_overview"  # REGO permission flag
      graphs:
        # Widget 1: Tree table showing all DevOps clusters
        - widget: "cc-state-tree-table"
          title: "DevOps Cluster State"
          args:
            # Filter: Only show clusters with team="devops" label
            filter: 'team = "devops"'

        # Widget 2: Heatmap showing cluster readiness
        - widget: "cc-state-readiness-heatmap"
          title: "DevOps Cluster Health"
          args:
            filter: 'team = "devops"'

    # Dashboard 2: DevOps Production View
    # Filtered view showing only production clusters
    devops-production:
      title: "DevOps Production"
      description: "Production PowerDNS clusters for DevOps team"
      url: /team/devops/production
      requires:
        - "dashboard_devops_production"
      graphs:
        - widget: "cc-state-tree-table"
          title: "Production Clusters"
          args:
            # Multiple conditions: team AND environment
            filter: 'team = "devops" and environment = "production"'

        - widget: "cc-state-readiness-pie-charts"
          title: "Production Readiness Distribution"
          args:
            filter: 'team = "devops" and environment = "production"'

    # Dashboard 3: DevOps by Environment
    # Separate view for each environment (dev, staging, prod)
    devops-by-environment:
      title: "DevOps Environments"
      description: "DevOps clusters organized by environment"
      url: /team/devops/environments
      requires:
        - "dashboard_devops_environments"
      graphs:
        # Development clusters
        - widget: "cc-state-tree-table"
          title: "Development"
          args:
            filter: 'team = "devops" and environment = "development"'

        # Staging clusters
        - widget: "cc-state-tree-table"
          title: "Staging"
          args:
            filter: 'team = "devops" and environment = "staging"'

        # Production clusters
        - widget: "cc-state-tree-table"
          title: "Production"
          args:
            filter: 'team = "devops" and environment = "production"'

    # Dashboard 4: Platform Team Overview
    # Main dashboard showing all Platform team clusters
    platform-overview:
      title: "Platform Team Dashboard"
      description: "PowerDNS clusters managed by the Platform team"
      url: /team/platform
      requires:
        - "dashboard_platform_overview"  # REGO permission flag
      graphs:
        # Widget 1: Tree table showing all Platform clusters
        - widget: "cc-state-tree-table"
          title: "Platform Cluster State"
          args:
            filter: 'team = "platform"'

        # Widget 2: Heatmap showing cluster readiness
        - widget: "cc-state-readiness-heatmap"
          title: "Platform Cluster Health"
          args:
            filter: 'team = "platform"'

    # Dashboard 5: Platform Production View
    # Filtered view showing only production clusters
    platform-production:
      title: "Platform Production"
      description: "Production PowerDNS clusters for Platform team"
      url: /team/platform/production
      requires:
        - "dashboard_platform_production"
      graphs:
        - widget: "cc-state-tree-table"
          title: "Production Clusters"
          args:
            filter: 'team = "platform" and environment = "production"'

        - widget: "cc-state-readiness-pie-charts"
          title: "Production Readiness Distribution"
          args:
            filter: 'team = "platform" and environment = "production"'

    # Dashboard 6: Platform by Environment
    # Separate view for each environment (dev, staging, prod)
    platform-by-environment:
      title: "Platform Environments"
      description: "Platform clusters organized by environment"
      url: /team/platform/environments
      requires:
        - "dashboard_platform_environments"
      graphs:
        # Development clusters
        - widget: "cc-state-tree-table"
          title: "Development"
          args:
            filter: 'team = "platform" and environment = "development"'

        # Staging clusters
        - widget: "cc-state-tree-table"
          title: "Staging"
          args:
            filter: 'team = "platform" and environment = "staging"'

        # Production clusters
        - widget: "cc-state-tree-table"
          title: "Production"
          args:
            filter: 'team = "platform" and environment = "production"'

    # Dashboard 7: Global Overview (Admin Only)
    # Shows all clusters across all teams
    global-overview:
      title: "Global Cluster Overview"
      description: "All PowerDNS clusters across all teams"
      url: /global/all-clusters
      requires:
        - "dashboard_global_admin"  # Only admins can see all clusters
      graphs:
        - widget: "cc-state-tree-table"
          title: "All Clusters"
          args:
            filter: ''  # No filter - show everything

        - widget: "cc-state-readiness-heatmap"
          title: "Global Cluster Health"
          args:
            filter: ''

    # Dashboard 8: Global by Team (Admin Only)
    # Shows separate widgets for each team
    global-by-team:
      title: "Global View by Team"
      description: "All teams and their clusters"
      url: /global/by-team
      requires:
        - "dashboard_global_admin"
      graphs:
        # DevOps team clusters
        - widget: "cc-state-tree-table"
          title: "DevOps Team"
          args:
            filter: 'team = "devops"'

        # Platform team clusters
        - widget: "cc-state-tree-table"
          title: "Platform Team"
          args:
            filter: 'team = "platform"'

        # Readiness summary across all teams
        - widget: "cc-state-readiness-pie-charts"
          title: "Overall Readiness by Team"
          args:
            filter: ''
YAML
# Example: Team-Based Navigation Configuration
# This example shows how to create a custom navigation menu for a specific team.
#
# The DevOps team gets a tailored menu structure focused on their workflows.

globalConfig:
  navigation:
    menus:
      # Menu 1: DevOps Team Menu
      - name: "DevOps Team"
        sections:
          # Section 1: Dashboard Links
          - name: "Dashboards"
            items:
              # Link to main team dashboard
              - name: "Team Overview"
                url: "/team/devops"
                description: "All DevOps clusters"

              # Link to production-only view
              - name: "Production"
                url: "/team/devops/production"
                description: "Production clusters only"

              # Link to environment breakdown
              - name: "By Environment"
                url: "/team/devops/environments"
                description: "Dev, Staging, Prod views"

          # Section 2: External Links (EXAMPLE ONLY - these URLs are fictional)
          - name: "Resources"
            items:
              # Example: Link to team runbook
              - name: "Team Runbook"
                url: "https://docs.example.com/devops/runbook"
                description: "DevOps operational procedures"
                external: true
                requires:
                  - "navigation_devops_resources"

              # Example: Link to team documentation
              - name: "Team Wiki"
                url: "https://wiki.example.com/devops"
                description: "DevOps team wiki"
                external: true
                requires:
                  - "navigation_devops_resources"

      # Menu 2: Platform Team Menu
      - name: "Platform Team"
        sections:
          # Section 1: Dashboard Links
          - name: "Dashboards"
            items:
              # Link to main team dashboard
              - name: "Team Overview"
                url: "/team/platform"
                description: "All Platform clusters"

              # Link to production-only view
              - name: "Production"
                url: "/team/platform/production"
                description: "Production clusters only"

              # Link to environment breakdown
              - name: "By Environment"
                url: "/team/platform/environments"
                description: "Dev, Staging, Prod views"

          # Section 2: External Links (EXAMPLE ONLY - these URLs are fictional)
          - name: "Resources"
            items:
              # Example: Link to team runbook
              - name: "Team Runbook"
                url: "https://docs.example.com/platform/runbook"
                description: "Platform operational procedures"
                external: true
                requires:
                  - "navigation_platform_resources"

              # Example: Link to team documentation
              - name: "Team Wiki"
                url: "https://wiki.example.com/platform"
                description: "Platform team wiki"
                external: true
                requires:
                  - "navigation_platform_resources"

      # Menu 3: Global Admin Menu
      - name: "Global Admin"
        sections:
          # Section 1: Global Dashboard Links
          - name: "Dashboards"
            items:
              # Link to global overview dashboard
              - name: "Global Overview"
                url: "/global/all-clusters"
                description: "All clusters across all teams"

              # Link to by-team view
              - name: "View by Team"
                url: "/global/by-team"
                description: "Clusters organized by team"

          # Section 2: All Teams Quick Access
          - name: "Team Dashboards"
            items:
              - name: "DevOps Team"
                url: "/team/devops"
                description: "DevOps team dashboard"

              - name: "Platform Team"
                url: "/team/platform"
                description: "Platform team dashboard"

# Note: The default "Tools" menu (DNS Query, Cache Management) is automatically
# appended after these custom menus.

# Navigation Visibility:
# - Navigation menus can be conditionally displayed based on user permissions
# - REGO policies control which menus and items a user sees
# - Users only see navigation items they have permission to access
#
# Integration with Dashboards:
# - Menu items link to dashboard URLs defined in team-based-dashboard.yaml
# - Example: "/team/devops" links to devops-overview dashboard

REGO Policy Configuration

YAML
# Example: Team-Based REGO Authorization Policies
# This example demonstrates access control policies for team-based deployments.
#
# Access Model: Users can only see and manage clusters belonging to their team

policy:
  # Static Users Configuration
  # In production, users would come from OIDC with team claims
  staticUsers:
    # DevOps Team Lead - Full access to DevOps team clusters
    devops-lead:
      id: "devops-lead"
      password: "secret"
      sub: "devops-lead"
      name: "DevOps Team Lead"
      email: "lead@devops.example.com"
      roles:
        - "devops"              # Team membership
        - "team-lead"           # Leadership role
        - "production"          # Can access production
        - "staging"             # Can access staging
        - "development"         # Can access development
        - "operator"            # Can restart instances
        - "observer"            # Can read logs

    # DevOps Developer - Limited access to non-prod
    devops-developer:
      id: "devops-developer"
      password: "secret"
      sub: "devops-developer"
      name: "DevOps Developer"
      email: "dev@devops.example.com"
      roles:
        - "devops"              # Team membership
        - "developer"           # Developer role
        - "staging"             # Can access staging
        - "development"         # Can access development
        - "observer"            # Can read logs
        # Note: No "production" role - cannot see prod clusters
        # Note: No "operator" role - cannot restart instances

    # Platform Team Member - Access to Platform team clusters only
    platform-engineer:
      id: "platform-engineer"
      password: "secret"
      sub: "platform-engineer"
      name: "Platform Engineer"
      email: "eng@platform.example.com"
      roles:
        - "platform"            # Different team
        - "production"
        - "staging"
        - "development"
        - "operator"
        - "observer"
      # Note: Cannot see DevOps team clusters

    # Global Administrator - Access to all teams
    admin:
      id: "admin"
      password: "secret"
      sub: "admin"
      name: "Global Administrator"
      email: "admin@example.com"
      roles:
        - "admin"               # Admin bypass
        - "global"              # Global access

  # REGO Authorization Policies
  policies:
    # Operation Permission Definitions
    # These define well-known operations that services check
    pdns_permissions.rego: |
      package pdns_permissions

      import data.user

      # Basic connection - all authenticated users
      connect if true

      # Read cluster state - requires team membership and visibility
      read if user.can_see_cluster

      # Read logs - requires observer role + cluster visibility
      read_logs if user.can_observe_cluster

      # Clear DNS cache - requires operator role + cluster visibility (destructive operation)
      clear_cache if user.can_manage_instances

      # Restart instances - requires operator role + cluster visibility
      restart_instance_set if user.can_manage_instances

      # Delete pods - requires operator role + cluster visibility
      delete_pod if user.can_manage_instances

      # DNS check - requires observer role + cluster visibility
      dns_check if user.can_observe_cluster

    # Dashboard and Navigation Permission Flags
    # These are custom flags for controlling UI access
    pdns_global_flags.rego: |
      package pdns_global_flags

      # Dashboard access flags
      dashboard_devops_overview if "devops" in input.user.roles
      dashboard_devops_overview if "admin" in input.user.roles

      dashboard_devops_production if "devops" in input.user.roles
      dashboard_devops_production if "admin" in input.user.roles

      dashboard_devops_environments if "devops" in input.user.roles
      dashboard_devops_environments if "admin" in input.user.roles

      dashboard_platform_overview if "platform" in input.user.roles
      dashboard_platform_overview if "admin" in input.user.roles

      dashboard_platform_production if "platform" in input.user.roles
      dashboard_platform_production if "admin" in input.user.roles

      dashboard_platform_environments if "platform" in input.user.roles
      dashboard_platform_environments if "admin" in input.user.roles

      dashboard_global_admin if "admin" in input.user.roles

      # Navigation item access flags
      navigation_devops_resources if "devops" in input.user.roles
      navigation_devops_resources if "admin" in input.user.roles

      navigation_platform_resources if "platform" in input.user.roles
      navigation_platform_resources if "admin" in input.user.roles

    # User Authorization Logic
    # This evaluates whether a user can access specific clusters
    user.rego: |
      package user

      roles := input.user.roles

      # Role Flags
      admin if "admin" in roles
      team_lead if "team-lead" in roles
      developer if "developer" in roles
      operator if "operator" in roles
      observer if "observer" in roles

      # Team Membership
      is_devops_team_member if "devops" in roles
      is_platform_team_member if "platform" in roles

      # Team-Based Cluster Visibility
      # User must belong to the same team as the cluster
      has_matching_team if {
        some team in roles
        team in input.cluster.labels.team
      }

      # Admin bypass - admins see all clusters regardless of team
      has_matching_team if admin

      # Environment Access
      # User must have role matching cluster environment
      has_matching_environment if {
        some env in input.cluster.labels.environment
        env in roles
      }

      # Admin bypass for environment
      has_matching_environment if admin

      # Cluster Visibility Rule
      # User can see cluster if team AND environment match
      can_see_cluster if {
        has_matching_team
        has_matching_environment
      }

      # Observer Capabilities
      # Can read logs and check DNS if observer role + visibility
      can_observe_cluster if {
        can_see_cluster
        observer
      }

      can_observe_cluster if admin

      # Operator Capabilities
      # Can restart instances and delete pods if operator role + visibility
      can_manage_instances if {
        can_see_cluster
        operator
      }

      can_manage_instances if admin
Cleanup

To remove the team-based deployment:

Bash
# Uninstall Glass UI
helm uninstall glass-ui -n controlplane

# Uninstall Glass Instrumentation from each cluster
helm uninstall glass-instrumentation -n devops-prod
helm uninstall glass-instrumentation -n devops-staging
helm uninstall glass-instrumentation -n devops-dev
helm uninstall glass-instrumentation -n platform-prod

# Uninstall PowerDNS clusters
helm uninstall devops-prod -n devops-prod
helm uninstall devops-staging -n devops-staging
helm uninstall devops-dev -n devops-dev
helm uninstall platform-prod -n platform-prod

# Uninstall Controlplane
helm uninstall team-controlplane -n controlplane

# Delete namespaces
kubectl delete namespace controlplane devops-prod devops-staging devops-dev platform-prod

# Uninstall PowerDNS CRDs
helm uninstall powerdns-crds