# Views

Views are dynamic, data-driven dashboards in Mission Control that aggregate and visualize data from multiple sources. They collect data from multiple sources into an in-memory SQLite database, enabling you to run any SQL query for filtering, joining, and aggregating your observability data.

![](/assets/images/views-c1262c246f49c713f39d642c6f12d441.svg)

Views Concept

Views serve as a powerful data aggregation and visualization layer in Mission Control. They:

* **Aggregate data** from multiple sources including configuration items, changes, and Prometheus metrics
* **Display data** as interactive tables and visualization panels (gauges, pie charts, ...)
* **Support templating** with variables that users can adjust to dynamically filter and update the view
* **Cache results** intelligently to balance data freshness with performance
* **Transform data** using expressions to map raw query results to meaningful display columns
* **Embed views** inside config item pages or assemble multiple views on one page with sections

## Key Features[​](#key-features "Direct link to Key Features")

### Data Sources[​](#data-sources "Direct link to Data Sources")

Views can query data from multiple sources:

* **Config Items**: Infrastructure components and configurations via the `configs` selector
* **Changes**: Configuration change tracking and audit data via the `changes` selector
* **Metrics**: Time-series data from Prometheus using PromQL queries
* **SQL**: Direct SQL queries via stored connections (PostgreSQL, MySQL, SQL Server)
* **Other Views**: Reuse cached tables from existing views with `viewTableSelector`

### Output Types[​](#output-types "Direct link to Output Types")

Views can generate:

* **One Table**: Structured data with typed columns for detailed data presentation
* **Multiple Panels**: Interactive visualizations including pie charts, gauges, numbers, and summary tables

## How Views Work[​](#how-views-work "Direct link to How Views Work")

Views transform your data into an in-memory SQL database, giving you the full power of SQL to slice, dice, and analyze your infrastructure:

### 1. Named Queries (Required)[​](#1-named-queries-required "Direct link to 1. Named Queries (Required)")

Every view **must** define queries with unique names:

```
queries:

  deployments: # Table name in SQL

    configs:

      types: ['Kubernetes::Deployment']

  pipelines: # Another table name

    changes:

      search: change_type=GitHubActionRun
```

### 2. In-Memory SQL Database[​](#2-in-memory-sql-database "Direct link to 2. In-Memory SQL Database")

Views execute your named queries and create an **SQLite database in memory**:

* Each query becomes a **table** with the query's name
* Table schema gets generated dynamically from the query results
* Example: queries named `deployments`, `pipelines`, `metrics` create 3 SQL tables

### 3. SQL-Powered Data Processing[​](#3-sql-powered-data-processing "Direct link to 3. SQL-Powered Data Processing")

Once you load data, you can run **any SQL query** against it to combine data from multiple sources:

```
# View table data (optional)

merge: |

  SELECT d.name, d.status, p.duration

  FROM deployments d

  LEFT JOIN pipelines p ON d.name = p.app



# Panel queries (required for panels)

panels:

  - name: 'Status Distribution'

    type: piechart

    query: 'SELECT COUNT(*) AS count, status FROM deployments GROUP BY status'
```

This SQL-first approach gives you unlimited flexibility to slice, filter, and aggregate your data.

### Compose Pages with Sections[​](#compose-pages-with-sections "Direct link to Compose Pages with Sections")

Use `spec.sections` to render multiple views together. Mission Control aggregates their variables and shows a single filter bar for all sections.

```
workload-overview.yaml
apiVersion: mission-control.flanksource.com/v1

kind: View

metadata:

  name: workload-overview

  namespace: mc

spec:

  description: Overview page that stitches together multiple workload views.

  display:

    title: Workload Overview

    icon: view-details

    sidebar: true

  sections:

    - title: Deployments

      icon: kubernetes::deployment

      viewRef:

        namespace: mc

        name: deployments

    - title: Pods

      icon: kubernetes::pod

      viewRef:

        namespace: mc

        name: pods

    - title: Pipelines

      icon: github

      viewRef:

        namespace: mc

        name: pipelines

---

apiVersion: mission-control.flanksource.com/v1

kind: View

metadata:

  name: pods

  namespace: mc

spec:

  templating:

    - key: cluster

      label: Cluster

      valueFrom:

        config:

          types:

            - Kubernetes::Cluster

    - key: namespace

      label: Namespace

      dependsOn:

        - cluster

      valueFrom:

        config:

          tagSelector: "cluster=$(.var.cluster)"

          types:

            - Kubernetes::Namespace

  cache:

    maxAge: 1m

    minAge: 5s

  display:

    title: Pods

    icon: pod

    sidebar: true

  columns:

    - name: name

      type: string

      primaryKey: true

    - name: namespace

      type: string

      primaryKey: true

    - name: status

      type: status

    - name: health

      type: health

    - name: memory

      type: bytes

    - name: memory_limit

      type: bytes

    - name: cpu

      type: millicore

    - name: cpu_limit

      type: millicore

    - name: tags

      type: labels

    - name: created

      type: datetime

  merge: |

    SELECT

      pod.name,

      json_extract(pod.tags, '$.namespace') AS namespace,

      pod.status,

      pod.health,

      pod.tags,

      pod.created_at,

      CAST(memory.value AS TEXT) as memory,

      COALESCE(

        json_extract(pod.config, '$.spec.containers[0].resources.limits.memory'),

        ''

      ) AS memory_limit,

      COALESCE(CAST(cpu.value AS TEXT), '0') as cpu,

      COALESCE(

        json_extract(pod.config, '$.spec.containers[0].resources.limits.cpu'),

        ''

      ) AS cpu_limit

    FROM pod

    LEFT JOIN memory

      ON pod.name = memory.pod

      AND json_extract(pod.tags, '$.namespace') = memory.namespace

    LEFT JOIN cpu

      ON pod.name = cpu.pod

      AND json_extract(pod.tags, '$.namespace') = cpu.namespace

  queries:

    memory:

      columns:

        namespace: string

        pod: string

        value: decimal

      prometheus:

        connection: connection://mc/prometheus

        query: |

          sum by (namespace, pod) (

            container_memory_working_set_bytes{

              container!="POD", # Skip The pause/infra container

              image!="" # Skip dead containers

            }

          )

    cpu:

      columns:

        namespace: string

        pod: string

        value: decimal

      prometheus:

        connection: connection://mc/prometheus

        query: |

          sum by (namespace, pod) (

            irate(container_cpu_usage_seconds_total{

              container!="POD", # Skip The pause/infra container

              image!="" # Skip dead containers

            }[30s])

          ) * 1000

    pod:

      configs:

        search: "@order=name"

        tagSelector: namespace in (default)

        types:

          - Kubernetes::Pod

  mapping:

    created: row.created_at
```

### Attach Views to Config Tabs[​](#attach-views-to-config-tabs "Direct link to Attach Views to Config Tabs")

`display.plugins` lets you place a view as a tab on matching config items. Variables can be templated from the config to scope the view automatically.

```
namespace.yaml
apiVersion: mission-control.flanksource.com/v1

kind: View

metadata:

  name: namespace

  namespace: mc

spec:

  columns:

    - cardPosition: title

      name: name

      primaryKey: true

      type: config_item

      url:

        config: row.id

    - cardPosition: deck

      filter:

        type: multiselect

      icon: row.health

      name: status

      type: status

    - filter:

        type: multiselect

      hidden: true

      name: health

      type: health

    - cardPosition: deck

      description: Memory usage

      gauge:

        max: row.memory_limit

        thresholds:

          - color: green

            percent: 0

          - color: orange

            percent: 75

          - color: red

            percent: 90

      name: memory

      type: gauge

      unit: bytes

    - cardPosition: deck

      gauge:

        max: row.cpu_limit

        thresholds:

          - color: "#8BC34A"

            percent: 0

          - color: "#F4B23C"

            percent: 70

          - color: "#F25C54"

            percent: 85

      name: cpu

      type: gauge

      unit: millicore

    - cardPosition: footer

      name: updated

      type: datetime

    - cardPosition: footer

      name: created

      type: datetime

    - cardPosition: body

      hidden: true

      name: image

      type: string

    - cardPosition: body

      hidden: true

      name: node

      type: string

  description: View for inspecting a Kubernetes Namespace

  display:

    icon: namespace

    plugins:

      - configTab:

          types:

            - Kubernetes::Namespace

        variables:

          cluster: $(.config.tags.cluster)

          namespace: $(.config.name)

    sidebar: true

    title: Namespace

  mapping:

    created: row.created_at

    image: |

      has(row.config) ? row.config.JSON().spec.containers[0].image : "N/A"

    node: |

      has(row.config) ? (has(row.config.JSON().spec.nodeName) ? row.config.JSON().spec.nodeName : "N/A"): "N/A"

    updated: row.updated_at

  merge: |

    "SELECT

      pod.id,

      pod.name,

      json_extract(pod.tags, '$.namespace')

    AS namespace,

      pod.status,

      pod.health,

      pod.config,

      pod.created_at,

    pod.updated_at,

      memory.value as memory,

      to_bytes(COALESCE(

        json_extract(pod.config,

    '$.spec.containers[0].resources.limits.memory'), 

        ''

      )) AS memory_limit,

    cpu.value as cpu,

      to_millicores(COALESCE(

        json_extract(pod.config, '$.spec.containers[0].resources.limits.cpu'),

    

    ''

          )) AS cpu_limit\nFROM pod\nLEFT JOIN memory

          ON pod.name = memory.pod

    

        AND json_extract(pod.tags, '$.namespace') = memory.namespace\nLEFT JOIN cpu

    ON pod.name = cpu.pod 

      AND json_extract(pod.tags, '$.namespace') = cpu.namespace\nORDER

    BY namespace, name

    "

  panels:

    - description: Total Pods in the namespace

      name: Total Pods

      query: SELECT COUNT(*) AS value FROM pod

      type: number

  queries:

    cpu:

      columns:

        namespace: string

        pod: string

        value: decimal

      prometheus:

        bearer: {}

        connection: connection://mc/prometheus

        oauth:

          clientID: {}

          clientSecret: {}

        password: {}

        query: |

          sum by (namespace, pod) (

            irate(container_cpu_usage_seconds_total{

              container!="POD", # Skip The pause/infra container

              image!="" # Skip dead containers

            }[30s])

          ) * 1000

        tls:

          ca: {}

          cert: {}

          key: {}

        username: {}

    memory:

      columns:

        namespace: string

        pod: string

        value: decimal

      prometheus:

        bearer: {}

        connection: connection://mc/prometheus

        oauth:

          clientID: {}

          clientSecret: {}

        password: {}

        query: |

          sum by (namespace, pod) (

            container_memory_working_set_bytes{

              container!="POD", # Skip The pause/infra container

              image!="" # Skip dead containers

            }

          )

        tls:

          ca: {}

          cert: {}

          key: {}

        username: {}

    memory_usage:

      columns:

        namespace: string

        pod: string

        value: decimal

      prometheus:

        bearer: {}

        connection: connection://mc/prometheus

        oauth:

          clientID: {}

          clientSecret: {}

        password: {}

        query: |

          sum(rate(container_memory_working_set_bytes{namespace=~"$(.var.namespace)", image!=""}[5m])) / sum(machine_memory_bytes{})

        tls:

          ca: {}

          cert: {}

          key: {}

        username: {}

    pod:

      configs:

        agent: all

        tagSelector: cluster=$(.var.cluster),namespace=$(.var.namespace)

        types:

          - Kubernetes::Pod

  templating:

    - key: cluster

      label: Cluster

      valueFrom:

        config:

          types:

            - Kubernetes::Cluster

    - dependsOn:

        - cluster

      key: namespace

      label: Namespace

      valueFrom:

        config:

          tagSelector: cluster=$(.var.cluster)

          types:

            - Kubernetes::Namespace

status: {}
```

## API Reference[​](#api-reference "Direct link to API Reference")

| Field                  | Description                                                                                                                        | Scheme                                                         |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| `queries*`             | Named queries that populate the view data sources                                                                                  | [`map[string]Query`](#query)                                   |
| `cache.maxAge`         | Maximum age of cache before it's deemed stale *(default: 15m)*                                                                     | [Duration](/docs/reference/types.md#duration)                  |
| `cache.minAge`         | Minimum age of cache a user can request *(default: 10s)*                                                                           | [Duration](/docs/reference/types.md#duration)                  |
| `cache.refreshTimeout` | Duration to wait for a view to process before returning stale data *(default: 5s)*                                                 | [Duration](/docs/reference/types.md#duration)                  |
| `columns`              | Define the structure and data types of the view table                                                                              | [\[\]ViewColumnDef](#view-column-definition)                   |
| `description`          | Short explanation of what the view shows                                                                                           | `string`                                                       |
| `display.card`         | Default card layout for the view (body column count and whether cards are the default mode)                                        | [DisplayCard](#view-card-layout)                               |
| `display.icon`         | Icon identifier for navigation and visual identification                                                                           | [Icon](/docs/reference/types.md#icon)                          |
| `display.ordinal`      | Sort order for navigation, lower numbers appear first *(default: 0)*                                                               | `integer`                                                      |
| `display.plugins[]`    | Attach the view as a tab on matching config items with templated variables extracted from the config                               | [ViewConfigUIPlugin](#config-tabs)                             |
| `display.sidebar`      | Whether the view appears in sidebar navigation *(default: false)*                                                                  | `boolean`                                                      |
| `display.table`        | Default table layout for the view (sort order and page size)                                                                       | [DisplayTable](#view-table-layout)                             |
| `display.title`        | Human-readable title displayed in the UI *(defaults to view name)*                                                                 | `string`                                                       |
| `mapping`              | CEL expressions to map query results to view columns. *If not specified, columns are automatically populated from query results.*  | [`map[string]CELExpression`](/docs/reference/scripting/cel.md) |
| `mcp`                  | How this view is presented to LLM clients as an [MCP](/docs/guide/mcp/.md) tool                                                    | [MCP](/docs/reference/playbooks/.md#mcp)                       |
| `merge`                | SQL query to merge/join data from multiple named queries. Required when using multiple queries with table output *(columns only)*. | `string`                                                       |
| `panels`               | Interactive visualizations for aggregated data                                                                                     | [\[\]PanelDef](#panel-definition)                              |
| `pdf`                  | Rendering options used when the view is exported to PDF or HTML through a facet server                                             | [Facet](/docs/guide/playbooks/actions/report.md#facet)         |
| `sections`             | List of other views to render on the same page                                                                                     | [\[\]ViewSection](#sections)                                   |
| `templating`           | Variable definitions that can be substituted into queries and mappings                                                             | [\[\]ViewVariable](/docs/guide/views/concepts/templating.md)   |

Validation Requirements

* **At least one query**: Views must define at least one named query (unless the view only renders `sections`)
* **Output definition**: Views must have either `panels` or `columns` (or both)
* **Merge requirement**: When using multiple queries with table output (columns only), you must provide a `merge` SQL query
* **Column definition**: When using Prometheus or HTTP queries, `columns` map must be defined to generate the table schema

### Query[​](#query "Direct link to Query")

Query definition for data sources.

| Field               | Description                                                                     | Scheme                                                   |
| ------------------- | ------------------------------------------------------------------------------- | -------------------------------------------------------- |
| `changes`           | Query configuration changes and audit data                                      | [ResourceSelector](/docs/reference/resource-selector.md) |
| `columns`           | Define column types for the query results (useful for HTTP/Prometheus)          | map\[string]ColumnType                                   |
| `configs`           | Query config items from the catalog                                             | [ResourceSelector](/docs/reference/resource-selector.md) |
| `http`              | Query external APIs via HTTP                                                    | [HTTP Query](#http-query)                                |
| `prometheus`        | Query time-series data from Prometheus                                          | [Prometheus Query](#prometheus-query)                    |
| `sql`               | Run a SQL query against a configured connection (PostgreSQL, MySQL, SQL Server) | [SQL query](/docs/guide/views/queries/sql.md)            |
| `viewTableSelector` | Read cached rows from another view table                                        | [View Table Selector](#view-table-selector)              |

#### HTTP Query[​](#http-query "Direct link to HTTP Query")

Query configuration for HTTP requests.

| Field        | Description                                       | Scheme                                   |
| ------------ | ------------------------------------------------- | ---------------------------------------- |
| `url*`       | Target URL                                        | `string`                                 |
| `bearer`     | Bearer token                                      | [EnvVar](/docs/reference/env-var.md)     |
| `body`       | Request body for POST/PUT/PATCH                   | `string`                                 |
| `connection` | Reference to a connection                         | `string`                                 |
| `headers`    | Custom request headers                            | [\[\]EnvVar](/docs/reference/env-var.md) |
| `jsonpath`   | JSONPath expression to extract rows from response | `string`                                 |
| `method`     | HTTP method *(default: GET)*                      | `string`                                 |
| `password`   | Basic auth password                               | [EnvVar](/docs/reference/env-var.md)     |
| `username`   | Basic auth username                               | [EnvVar](/docs/reference/env-var.md)     |

#### Prometheus Query[​](#prometheus-query "Direct link to Prometheus Query")

Query configuration for Prometheus metrics.

| Field        | Description                                  | Scheme                               |
| ------------ | -------------------------------------------- | ------------------------------------ |
| `query*`     | PromQL query string                          | `string`                             |
| `bearer`     | Bearer token for authentication              | [EnvVar](/docs/reference/env-var.md) |
| `connection` | Reference to a connection for authentication | `string`                             |
| `password`   | Basic auth password                          | [EnvVar](/docs/reference/env-var.md) |
| `url`        | Prometheus server URL                        | `string`                             |
| `username`   | Basic auth username                          | [EnvVar](/docs/reference/env-var.md) |

### View Column Definition[​](#view-column-definition "Direct link to View Column Definition")

Column definition for view tables with data types and visualization properties.

| Field         | Description                                                                                                                                                                            | Scheme                                                                                                                                       |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `name*`       | Column name *(must be a valid SQL identifier)*                                                                                                                                         | `string`                                                                                                                                     |
| `type*`       | Data type that determines formatting and visualization                                                                                                                                 | `string`, `number`, `boolean`, `datetime`, `duration`, `health`, `status`, `gauge`, `bytes`, `decimal`, `millicore`, `config_item`, `labels` |
| `badge`       | Badge styling configuration for the column                                                                                                                                             | [BadgeConfig](#badge-configuration)                                                                                                          |
| `card`        | Card layout configuration for this column (position and accent highlighting)                                                                                                           | CardConfig                                                                                                                                   |
| `configItem`  | Options for `config_item` columns (choose which field to treat as the config ID)                                                                                                       | ConfigItemColumnType                                                                                                                         |
| `description` | Human-readable description of the column                                                                                                                                               | `string`                                                                                                                                     |
| `filter`      | Enable column filtering in the UI. Options are populated from the cached table per request fingerprint *(type: multiselect; labels columns support key/value include-exclude filters)* | object                                                                                                                                       |
| `for`         | Reference to another column this column provides data for *(helper columns)*. Only applicable for type=url.                                                                            | `string`                                                                                                                                     |
| `gauge`       | Configuration for gauge visualization *(required when type=gauge)*                                                                                                                     | [GaugeConfig](#gauge-configuration)                                                                                                          |
| `hidden`      | Whether to hide the column from the UI *(default: false)*                                                                                                                              | `boolean`                                                                                                                                    |
| `icon`        | CEL expression that selects an icon per row (for tables and cards)                                                                                                                     | `string`                                                                                                                                     |
| `primaryKey`  | Whether this column is part of the primary key *(default: false)*. At least one column must be marked as primary key.                                                                  | `boolean`                                                                                                                                    |
| `unit`        | Unit string appended to values (e.g., %, bytes, millicore)                                                                                                                             | `string`                                                                                                                                     |
| `url`         | Link to configs, other views, or custom URLs using CEL/Go templates                                                                                                                    | [ColumnURL](#column-urls)                                                                                                                    |
| `width`       | Display width of the column, e.g. `150px` or a flex weight like `2`                                                                                                                    | `string`                                                                                                                                     |

Primary Key Requirement

If `columns` are defined, at least one must be marked as `primaryKey: true`.

### Panel Definition[​](#panel-definition "Direct link to Panel Definition")

Panel definition for interactive visualizations.

| Field         | Description                                                                           | Scheme                                                                                             |
| ------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `name*`       | Name of the panel                                                                     | `string`                                                                                           |
| `query*`      | SQL query to aggregate data from the view's named queries                             | `string`                                                                                           |
| `type*`       | Type of panel visualization                                                           | `piechart`, `table`, `text`, `number`, `duration`, `gauge`, `bargauge`, `timeseries`, `properties` |
| `bargauge`    | Configuration for bar gauge panels                                                    | [Bar Gauge Configuration](#bar-gauge-configuration)                                                |
| `description` | Description of the panel                                                              | `string`                                                                                           |
| `gauge`       | Configuration for gauge panels                                                        | [Gauge Configuration](#gauge-configuration)                                                        |
| `number`      | Configuration for number panels                                                       | [Panel Number Configuration](#panel-number-configuration)                                          |
| `piechart`    | Configuration for pie chart panels                                                    | [Pie Chart Configuration](#pie-chart-configuration)                                                |
| `properties`  | Properties panel renders query results as key-value pairs (no specific configuration) | object                                                                                             |
| `timeseries`  | Configuration for timeseries panels                                                   | [Timeseries Configuration](#timeseries-configuration)                                              |

#### Pie Chart Configuration[​](#pie-chart-configuration "Direct link to Pie Chart Configuration")

Configuration for pie chart panels.

| Field        | Description                         | Scheme                |
| ------------ | ----------------------------------- | --------------------- |
| `colors`     | Custom color mapping for categories | `[map[string]string]` |
| `showLabels` | Display labels on pie slices        | `boolean`             |

#### Gauge Configuration[​](#gauge-configuration "Direct link to Gauge Configuration")

Configuration for gauge visualizations with thresholds and color coding.

| Field                  | Description                                            | Scheme    |
| ---------------------- | ------------------------------------------------------ | --------- |
| `max`                  | Maximum value for the gauge (number or CEL expression) | `string`  |
| `min`                  | Minimum value for the gauge (number or CEL expression) | `string`  |
| `precision`            | Number of decimal places to display                    | `integer` |
| `thresholds[].color`   | Color for this threshold                               | `string`  |
| `thresholds[].percent` | Threshold as a percentage of the gauge range (0–100)   | `integer` |
| `thresholds[].value`   | Threshold value (deprecated; use percent)              | `integer` |

#### Panel Number Configuration[​](#panel-number-configuration "Direct link to Panel Number Configuration")

Configuration for number panels.

| Field       | Description                                 | Scheme    |
| ----------- | ------------------------------------------- | --------- |
| `precision` | Number of decimal places                    | `integer` |
| `unit`      | Display unit *(e.g., 'seconds', 'MB', '%')* | `string`  |

#### Bar Gauge Configuration[​](#bar-gauge-configuration "Direct link to Bar Gauge Configuration")

Configuration for bar gauge panels.

| Field                | Description                                      | Scheme    |
| -------------------- | ------------------------------------------------ | --------- |
| `format`             | Display format (`percentage` or `multiplier`)    | `string`  |
| `group`              | Optional group name to merge multiple bar gauges | `string`  |
| `max`                | Maximum value (number or CEL expression)         | `string`  |
| `min`                | Minimum value (number or CEL expression)         | `string`  |
| `precision`          | Number of decimal places                         | `integer` |
| `thresholds[].color` | Color for this threshold                         | `string`  |
| `thresholds[].value` | Threshold value (deprecated; use percent)        | `integer` |
| `unit`               | Unit label appended to values                    | `string`  |

#### Timeseries Configuration[​](#timeseries-configuration "Direct link to Timeseries Configuration")

Configuration for timeseries panels (API-level; UI rendering in progress).

| Field      | Description                                                  | Scheme   |
| ---------- | ------------------------------------------------------------ | -------- |
| `style`    | Visualization style: `lines`, `area`, or `points`            | `string` |
| `timeKey`  | Field name for the timestamp column *(default: `timestamp`)* | `string` |
| `valueKey` | Field name for the numeric value column *(default: `value`)* | `string` |

### View Table Layout[​](#view-table-layout "Direct link to View Table Layout")

`display.table` controls the default table layout for the view.

| Field  | Description                                                     | Scheme    |
| ------ | --------------------------------------------------------------- | --------- |
| `size` | Default page size for the table.                                | `integer` |
| `sort` | Default sort column name. Prefix with `-` for descending order. | `string`  |

### View Card Layout[​](#view-card-layout "Direct link to View Card Layout")

`display.card` controls the default card layout for the view (when `default: true`, cards are shown instead of the table).

| Field      | Description                                              | Scheme    |
| ---------- | -------------------------------------------------------- | --------- |
| `columns*` | Number of columns in the card body                       | `integer` |
| `default`  | Whether the Cards tab should be the default display mode | `boolean` |

### Config Tabs[​](#config-tabs "Direct link to Config Tabs")

Use `display.plugins` to attach a view as a tab on config detail pages. The `configTab` selector chooses matching configs, and `variables` templating pulls values from the config into the view request.

```
namespace.yaml
apiVersion: mission-control.flanksource.com/v1

kind: View

metadata:

  name: namespace

  namespace: mc

spec:

  columns:

    - cardPosition: title

      name: name

      primaryKey: true

      type: config_item

      url:

        config: row.id

    - cardPosition: deck

      filter:

        type: multiselect

      icon: row.health

      name: status

      type: status

    - filter:

        type: multiselect

      hidden: true

      name: health

      type: health

    - cardPosition: deck

      description: Memory usage

      gauge:

        max: row.memory_limit

        thresholds:

          - color: green

            percent: 0

          - color: orange

            percent: 75

          - color: red

            percent: 90

      name: memory

      type: gauge

      unit: bytes

    - cardPosition: deck

      gauge:

        max: row.cpu_limit

        thresholds:

          - color: "#8BC34A"

            percent: 0

          - color: "#F4B23C"

            percent: 70

          - color: "#F25C54"

            percent: 85

      name: cpu

      type: gauge

      unit: millicore

    - cardPosition: footer

      name: updated

      type: datetime

    - cardPosition: footer

      name: created

      type: datetime

    - cardPosition: body

      hidden: true

      name: image

      type: string

    - cardPosition: body

      hidden: true

      name: node

      type: string

  description: View for inspecting a Kubernetes Namespace

  display:

    icon: namespace

    plugins:

      - configTab:

          types:

            - Kubernetes::Namespace

        variables:

          cluster: $(.config.tags.cluster)

          namespace: $(.config.name)

    sidebar: true

    title: Namespace

  mapping:

    created: row.created_at

    image: |

      has(row.config) ? row.config.JSON().spec.containers[0].image : "N/A"

    node: |

      has(row.config) ? (has(row.config.JSON().spec.nodeName) ? row.config.JSON().spec.nodeName : "N/A"): "N/A"

    updated: row.updated_at

  merge: |

    "SELECT

      pod.id,

      pod.name,

      json_extract(pod.tags, '$.namespace')

    AS namespace,

      pod.status,

      pod.health,

      pod.config,

      pod.created_at,

    pod.updated_at,

      memory.value as memory,

      to_bytes(COALESCE(

        json_extract(pod.config,

    '$.spec.containers[0].resources.limits.memory'), 

        ''

      )) AS memory_limit,

    cpu.value as cpu,

      to_millicores(COALESCE(

        json_extract(pod.config, '$.spec.containers[0].resources.limits.cpu'),

    

    ''

          )) AS cpu_limit\nFROM pod\nLEFT JOIN memory

          ON pod.name = memory.pod

    

        AND json_extract(pod.tags, '$.namespace') = memory.namespace\nLEFT JOIN cpu

    ON pod.name = cpu.pod 

      AND json_extract(pod.tags, '$.namespace') = cpu.namespace\nORDER

    BY namespace, name

    "

  panels:

    - description: Total Pods in the namespace

      name: Total Pods

      query: SELECT COUNT(*) AS value FROM pod

      type: number

  queries:

    cpu:

      columns:

        namespace: string

        pod: string

        value: decimal

      prometheus:

        bearer: {}

        connection: connection://mc/prometheus

        oauth:

          clientID: {}

          clientSecret: {}

        password: {}

        query: |

          sum by (namespace, pod) (

            irate(container_cpu_usage_seconds_total{

              container!="POD", # Skip The pause/infra container

              image!="" # Skip dead containers

            }[30s])

          ) * 1000

        tls:

          ca: {}

          cert: {}

          key: {}

        username: {}

    memory:

      columns:

        namespace: string

        pod: string

        value: decimal

      prometheus:

        bearer: {}

        connection: connection://mc/prometheus

        oauth:

          clientID: {}

          clientSecret: {}

        password: {}

        query: |

          sum by (namespace, pod) (

            container_memory_working_set_bytes{

              container!="POD", # Skip The pause/infra container

              image!="" # Skip dead containers

            }

          )

        tls:

          ca: {}

          cert: {}

          key: {}

        username: {}

    memory_usage:

      columns:

        namespace: string

        pod: string

        value: decimal

      prometheus:

        bearer: {}

        connection: connection://mc/prometheus

        oauth:

          clientID: {}

          clientSecret: {}

        password: {}

        query: |

          sum(rate(container_memory_working_set_bytes{namespace=~"$(.var.namespace)", image!=""}[5m])) / sum(machine_memory_bytes{})

        tls:

          ca: {}

          cert: {}

          key: {}

        username: {}

    pod:

      configs:

        agent: all

        tagSelector: cluster=$(.var.cluster),namespace=$(.var.namespace)

        types:

          - Kubernetes::Pod

  templating:

    - key: cluster

      label: Cluster

      valueFrom:

        config:

          types:

            - Kubernetes::Cluster

    - dependsOn:

        - cluster

      key: namespace

      label: Namespace

      valueFrom:

        config:

          tagSelector: cluster=$(.var.cluster)

          types:

            - Kubernetes::Namespace

status: {}
```

### Column Filters[​](#column-filters "Direct link to Column Filters")

Enable `columns[].filter.type: multiselect` to expose server-side filters without re-running the view. Mission Control gathers distinct values from the cached Postgres table (scoped to the current `request_fingerprint`), builds dropdown options, and applies PostgREST filters. Labels columns support include/exclude tokens so you can target specific keys.

```
ingress.yaml
apiVersion: mission-control.flanksource.com/v1

kind: View

metadata:

  name: ingresses

  namespace: default

spec:

  description: Ingress view with card rendering support

  display:

    title: Ingresses

    icon: ingress

    sidebar: true

    card:

      columns: 2

      default: true

  queries:

    ingress:

      configs:

        search: "@order=name"

        types:

          - Kubernetes::Ingress

  columns:

    - name: id

      type: string

      primaryKey: true

      hidden: true

    - name: name

      type: config_item

      card:

        position: title

    - name: namespace

      type: string

      card:

        position: subtitle

    - name: status

      type: status

      icon: row.health

      card:

        position: subtitle

      filter:

        type: multiselect

    - name: health

      type: health

      hidden: true

      card:

        useForAccent: true

      filter:

        type: multiselect

    - name: hosts

      type: string

      card:

        position: body

    - name: class

      type: string

      card:

        position: body

    - name: address

      type: string

      card:

        position: body

    - name: created

      type: datetime

      card:

        position: body

  mapping:

    namespace: row.tags.namespace

    hosts: >

      has(row.config.spec.rules) && size(row.config.spec.rules) > 0

      ? row.config.spec.rules.map(r, has(r.host) ? r.host : "").join(", ")

      : "N/A"

    class: >

      has(row.config.spec.ingressClassName) ? row.config.spec.ingressClassName : "default"

    address: >

      has(row.config.status.loadBalancer.ingress) && size(row.config.status.loadBalancer.ingress) > 0

      ? row.config.status.loadBalancer.ingress.map(i, has(i.ip) ? i.ip : has(i.hostname) ? i.hostname : "").join(", ")

      : "N/A"

    created: row.created_at
```

### Column URLs[​](#column-urls "Direct link to Column URLs")

`columns[].url` can link to Mission Control objects or external destinations:

* `config`: CEL expression resolving to a config ID or search query (redirects to catalog page)
* `view`: Link to another view with optional query params built from CEL expressions
* `template`: Go template that renders a custom URL

```
deployments.yaml
apiVersion: mission-control.flanksource.com/v1

kind: View

metadata:

  name: deployments

  namespace: mc

spec:

  columns:

    - description: The id of the deployment.

      hidden: true

      name: id

      primaryKey: true

      type: string

    - card:

        position: title

      description: The deployment name.

      name: name

      type: config_item

    - card:

        position: subtitle

      description: The namespace name.

      filter:

        type: multiselect

      name: namespace

      type: string

      url:

        config:

          f("name=$(.namespace) tags.cluster=$(.cluster) type=Kubernetes::Namespace",

          row)

    - card:

        position: body

      description: The number of ready replicas.

      name: readyReplicas

      type: string

    - card:

        position: body

      description: The desired number of replicas.

      name: replicas

      type: string

    - card:

        position: subtitle

      description: The status of the deployment.

      filter:

        type: multiselect

      icon: row.health

      name: status

      type: status

    - card:

        useForAccent: true

      description: The health of the deployment.

      filter:

        type: multiselect

      hidden: true

      name: health

      type: health

    - card:

        position: headerRight

      description: CPU usage across all pods.

      gauge:

        thresholds:

          - color: "#8BC34A"

            percent: 0

          - color: "#F4B23C"

            percent: 70

          - color: "#F25C54"

            percent: 85

      name: cpu

      type: gauge

      unit: millicore

    - card:

        position: headerRight

      description: Memory usage across all pods.

      gauge:

        max: row.memory_limit

        thresholds:

          - color: green

            percent: 0

          - color: orange

            percent: 75

          - color: red

            percent: 90

      name: memory

      type: gauge

      unit: bytes

    - card:

        position: body

      description: The creation timestamp.

      name: createdAt

      type: datetime

    - card:

        position: body

      description: The last updated time.

      name: lastUpdated

      type: datetime

  display:

    icon: kubernetes::deployment

    sidebar: true

    title: Deployments

  mapping:

    cpu: row.cpu

    createdAt: row.created_at

    lastUpdated: row.updated_at

    memory: row.memory

    memory_limit: row.memory_limit

  merge: |

    SELECT

      deployments.id,

      deployments.name,

      json_extract(deployments.tags, '$.namespace') AS namespace,

      json_extract(deployments.tags, '$.cluster') AS cluster,

      deployments.status,

      deployments.health,

      deployments.created_at,

      deployments.updated_at,

      CAST(CASE

        WHEN json_extract(deployments.config, '$.status.readyReplicas') IS NULL THEN 0

        ELSE CAST(json_extract(deployments.config, '$.status.readyReplicas') AS INTEGER)

      END AS TEXT) AS readyReplicas,

      CAST(json_extract(deployments.config, '$.spec.replicas') AS TEXT) AS replicas,

      COALESCE(cpu_usage.value, 0) as cpu,

      COALESCE(memory_usage.value, 0) as memory,

      COALESCE(memory_limit.value, 0) as memory_limit

    FROM deployments

    LEFT JOIN cpu_usage_by_deployment AS cpu_usage

      ON deployments.name = cpu_usage.deployment

      AND json_extract(deployments.tags, '$.namespace') = cpu_usage.namespace

    LEFT JOIN memory_usage_by_deployment AS memory_usage

      ON deployments.name = memory_usage.deployment

      AND json_extract(deployments.tags, '$.namespace') = memory_usage.namespace

    LEFT JOIN memory_limit_by_deployment AS memory_limit

      ON deployments.name = memory_limit.deployment

      AND json_extract(deployments.tags, '$.namespace') = memory_limit.namespace

  panels:

    - description: Deployments grouped by health

      name: Health

      piechart:

        colors:

          healthy: "#28C19B"

          unhealthy: "#F04E6E"

          unknown: "#666666"

          warning: "#F4B23C"

        showLabels: true

      query: SELECT COUNT(*) AS count, health FROM deployments GROUP BY health

      type: piechart

    - description: Deployments grouped by status

      name: Status

      query: SELECT COUNT(*) AS count, status FROM deployments GROUP BY status

      type: piechart

  queries:

    cpu_usage_by_deployment:

      columns:

        deployment: string

        namespace: string

        value: decimal

      prometheus:

        connection: connection://mc/prometheus

        query: |

          sum by (namespace, deployment) (

            label_replace(

              rate(container_cpu_usage_seconds_total{

                container!="",

                container!="POD"

              }[5m]),

              "deployment", "$1", "pod", "^(.*)-[0-9a-f]+-[0-9a-z]+$"

            )

          ) * 1000

    deployments:

      configs:

        agent: all

        tagSelector: cluster=$(.var.cluster)

        types:

          - Kubernetes::Deployment

    memory_limit_by_deployment:

      columns:

        deployment: string

        namespace: string

        value: decimal

      prometheus:

        connection: connection://mc/prometheus

        query: |

          sum by (namespace, deployment) (

            label_replace(

              kube_pod_container_resource_limits{

                resource="memory",

                container!="",

                container!="POD"

              },

              "deployment", "$1", "pod", "^(.*)-[0-9a-f]+-[0-9a-z]+$"

            )

          )

    memory_usage_by_deployment:

      columns:

        deployment: string

        namespace: string

        value: decimal

      prometheus:

        connection: connection://mc/prometheus

        query: |

          sum by (namespace, deployment) (

            label_replace(

              container_memory_working_set_bytes{

                container!="",

                container!="POD"

              },

              "deployment", "$1", "pod", "^(.*)-[0-9a-f]+-[0-9a-z]+$"

            )

          )

  templating:

    - key: cluster

      label: Cluster

      valueFrom:

        config:

          agent: all

          types:

            - Kubernetes::Cluster
```

### Badge Configuration[​](#badge-configuration "Direct link to Badge Configuration")

`columns[].badge` controls the styling of badge/tag values.

| Field        | Description                                              | Scheme                |
| ------------ | -------------------------------------------------------- | --------------------- |
| `color.auto` | Use heuristics to choose a color based on the cell value | `boolean`             |
| `color.map`  | Explicit value-to-color mapping, e.g. `Running: green`   | `[map[string]string]` |

### Row Attributes and Grants[​](#row-attributes-and-grants "Direct link to Row Attributes and Grants")

Mission Control appends hidden, reserved columns to every view table:

* `__row__attributes` (`row_attributes` type) stores per-column metadata generated during mapping: evaluated icons, URLs, gauge min/max overrides, and config item details (`id`, `type`, `class`, `health`, `status`). The UI uses these attributes to render icons, clickable links, and card accents.
* `__grants` (`grants` type) stores scope IDs computed for config queries so row-level security policies can filter PostgREST responses.

You do not need to declare these columns, but they appear in the materialized Postgres table and PostgREST output.

### View Table Selector[​](#view-table-selector "Direct link to View Table Selector")

Reference cached data from another view instead of re-querying sources. The selector matches views by `name`, `namespace`, or `labelSelector`, then treats their materialized tables as a query source.

```
deployments-summary.yaml
apiVersion: mission-control.flanksource.com/v1

kind: View

metadata:

  name: deployments-summary

  namespace: mc

spec:

  description: Summarize cached deployment health by namespace using a view table selector.

  display:

    title: Deployment Health Summary

    icon: view-details

    sidebar: true

  queries:

    deployments:

      viewTableSelector:

        name: deployments

        namespace: mc

  merge: |

    SELECT

      namespace,

      SUM(CASE WHEN health = 'healthy' THEN 1 ELSE 0 END) AS healthy,

      SUM(CASE WHEN health IN ('unhealthy', 'warning') THEN 1 ELSE 0 END) AS unhealthy,

      COUNT(*) AS total

    FROM deployments

    GROUP BY namespace

  columns:

    - name: namespace

      type: string

      primaryKey: true

      filter:

        type: multiselect

    - name: healthy

      type: number

    - name: unhealthy

      type: number

    - name: total

      type: number

  panels:

    - name: Health Mix

      description: Health distribution across all deployments

      type: piechart

      query: SELECT COUNT(*) AS count, health FROM deployments GROUP BY health
```

### Sections[​](#sections "Direct link to Sections")

Render multiple views on one page. Section-only views can omit panels and columns; each section points at another view and is rendered with shared variable controls.

| Field               | Description                           | Scheme                                  |
| ------------------- | ------------------------------------- | --------------------------------------- |
| `title*`            | Title of the section                  | `string`                                |
| `icon`              | Icon for the section                  | `string`                                |
| `uiRef.changes`     | Embed the native Changes UI component | [ChangesUIFilters](#changes-ui-filters) |
| `uiRef.configs`     | Embed the native Configs UI component | [ConfigsUIFilters](#configs-ui-filters) |
| `viewRef.name`      | Name of the view to include           | `string`                                |
| `viewRef.namespace` | Namespace of the view to include      | `string`                                |

#### Changes UI Filters[​](#changes-ui-filters "Direct link to Changes UI Filters")

| Field         | Description                                | Scheme   |
| ------------- | ------------------------------------------ | -------- |
| `changeType`  | Filter by change type                      | `string` |
| `configTypes` | Filter by config types (comma-separated)   | `string` |
| `from`        | Start time (duration string, e.g. 24h)     | `string` |
| `severity`    | Filter by severity                         | `string` |
| `tags`        | Filter by tags (Kubernetes style selector) | `string` |

#### Configs UI Filters[​](#configs-ui-filters "Direct link to Configs UI Filters")

| Field        | Description                                  | Scheme   |
| ------------ | -------------------------------------------- | -------- |
| `configType` | Filter by config type                        | `string` |
| `health`     | Filter by health                             | `string` |
| `labels`     | Filter by labels (Kubernetes style selector) | `string` |
| `search`     | Free text search                             | `string` |
| `status`     | Filter by status                             | `string` |

```
workload-overview.yaml
apiVersion: mission-control.flanksource.com/v1

kind: View

metadata:

  name: workload-overview

  namespace: mc

spec:

  description: Overview page that stitches together multiple workload views.

  display:

    title: Workload Overview

    icon: view-details

    sidebar: true

  sections:

    - title: Deployments

      icon: kubernetes::deployment

      viewRef:

        namespace: mc

        name: deployments

    - title: Pods

      icon: kubernetes::pod

      viewRef:

        namespace: mc

        name: pods

    - title: Pipelines

      icon: github

      viewRef:

        namespace: mc

        name: pipelines

---

apiVersion: mission-control.flanksource.com/v1

kind: View

metadata:

  name: pods

  namespace: mc

spec:

  templating:

    - key: cluster

      label: Cluster

      valueFrom:

        config:

          types:

            - Kubernetes::Cluster

    - key: namespace

      label: Namespace

      dependsOn:

        - cluster

      valueFrom:

        config:

          tagSelector: "cluster=$(.var.cluster)"

          types:

            - Kubernetes::Namespace

  cache:

    maxAge: 1m

    minAge: 5s

  display:

    title: Pods

    icon: pod

    sidebar: true

  columns:

    - name: name

      type: string

      primaryKey: true

    - name: namespace

      type: string

      primaryKey: true

    - name: status

      type: status

    - name: health

      type: health

    - name: memory

      type: bytes

    - name: memory_limit

      type: bytes

    - name: cpu

      type: millicore

    - name: cpu_limit

      type: millicore

    - name: tags

      type: labels

    - name: created

      type: datetime

  merge: |

    SELECT

      pod.name,

      json_extract(pod.tags, '$.namespace') AS namespace,

      pod.status,

      pod.health,

      pod.tags,

      pod.created_at,

      CAST(memory.value AS TEXT) as memory,

      COALESCE(

        json_extract(pod.config, '$.spec.containers[0].resources.limits.memory'),

        ''

      ) AS memory_limit,

      COALESCE(CAST(cpu.value AS TEXT), '0') as cpu,

      COALESCE(

        json_extract(pod.config, '$.spec.containers[0].resources.limits.cpu'),

        ''

      ) AS cpu_limit

    FROM pod

    LEFT JOIN memory

      ON pod.name = memory.pod

      AND json_extract(pod.tags, '$.namespace') = memory.namespace

    LEFT JOIN cpu

      ON pod.name = cpu.pod

      AND json_extract(pod.tags, '$.namespace') = cpu.namespace

  queries:

    memory:

      columns:

        namespace: string

        pod: string

        value: decimal

      prometheus:

        connection: connection://mc/prometheus

        query: |

          sum by (namespace, pod) (

            container_memory_working_set_bytes{

              container!="POD", # Skip The pause/infra container

              image!="" # Skip dead containers

            }

          )

    cpu:

      columns:

        namespace: string

        pod: string

        value: decimal

      prometheus:

        connection: connection://mc/prometheus

        query: |

          sum by (namespace, pod) (

            irate(container_cpu_usage_seconds_total{

              container!="POD", # Skip The pause/infra container

              image!="" # Skip dead containers

            }[30s])

          ) * 1000

    pod:

      configs:

        search: "@order=name"

        tagSelector: namespace in (default)

        types:

          - Kubernetes::Pod

  mapping:

    created: row.created_at
```
