Skip to content
GitHub

LiveReportArtefact

LiveReportArtefact manifests define production-ready web applications that serve multiple ReportArtefacts as navigable pages with support for dynamic query parameter substitution.

The smallest well-formed LiveReportArtefact sets the required spec.title and the mandatory root route / with the deliverable it serves.

apiVersion: bino.bi/v1alpha1
kind: LiveReportArtefact
metadata:
  name: sales-dashboard
spec:
  title: "Sales Dashboard"
  routes:
    "/":
      artefact: overview-report

All attributes are listed in the Attribute Reference below.

Each route serves either an artefact or one or more layoutPages (see spec.routes and Parameterized LayoutPages in routes), and the application becomes installable only when spec.pwa is set.

Human-readable title of the live report application.

Optional description of the application.

Optional. When present, bino serve serves the application as an installable Progressive Web App: it generates a web app manifest and a service worker, and injects the manifest link, the service-worker registration, and — when configured — a theme-color meta tag and an apple-touch-icon link into every page. Titled non-root routes are additionally exposed as app shortcuts (the jump list on the installed app's icon). When spec.pwa is absent, serving behavior is completely unchanged — no extra tags, routes, or defaults.

  • name – full application name shown during installation and on splash screens. Defaults to spec.title.
  • shortName – short name shown under the home screen icon. Defaults to name.
  • description – description shown in install prompts. Defaults to spec.description.
  • themeColor – theme color applied to the browser UI around the installed app, e.g. "#0B5FFF".
  • backgroundColor – background color of the splash screen shown while the installed app loads, e.g. "#FFFFFF".
  • display – display mode of the installed app. One of standalone (default), fullscreen, minimal-ui, browser.
  • icons (required) – installable app icons, see below.

Icons offered to the operating system for the installed app. At least one icon is required; include a 512x512 icon for best results.

Each icon references an Asset manifest:

  • asset (required) – metadata.name of an Asset with spec.type: image and a source.localPath. Icons are served from the local file; remote or inline asset sources are rejected.
  • sizes (required) – pixel dimensions as WIDTHxHEIGHT, e.g. 512x512.
  • purposeany (default) for a regular icon, or maskable for an icon designed with a safe zone so the OS may apply an adaptive mask.

Example:

---
apiVersion: bino.bi/v1alpha1
kind: Asset
metadata:
  name: app-icon-192
spec:
  type: image
  mediaType: image/png
  source:
    localPath: ./assets/icon-192.png
---
apiVersion: bino.bi/v1alpha1
kind: Asset
metadata:
  name: app-icon-512
spec:
  type: image
  mediaType: image/png
  source:
    localPath: ./assets/icon-512.png
---
apiVersion: bino.bi/v1alpha1
kind: Asset
metadata:
  name: app-icon-maskable
spec:
  type: image
  mediaType: image/png
  source:
    localPath: ./assets/icon-maskable.png
---
apiVersion: bino.bi/v1alpha1
kind: LiveReportArtefact
metadata:
  name: sales-dashboard
spec:
  title: "Sales Dashboard"
  pwa:
    shortName: Sales
    themeColor: "#0B5FFF"
    backgroundColor: "#FFFFFF"
    icons:
      - asset: app-icon-192
        sizes: 192x192
      - asset: app-icon-512
        sizes: 512x512
      - asset: app-icon-maskable
        sizes: 512x512
        purpose: maskable
  routes:
    "/":
      artefact: overview-report

Map of URL paths to route configurations. The root route / is mandatory.

Each route must specify either artefact or layoutPages (but not both):

  • artefact – name of a ReportArtefact to render for this route.
  • layoutPages – a single LayoutPage name, or an array whose entries are either a name, a glob pattern matching LayoutPage names, or an object with page and params, rendered directly (without a ReportArtefact wrapper).
  • title – optional page title override.
  • queryParams – array of query parameters allowed for this route.

Routes are accessible at their defined paths, e.g., /sales renders sales-report.

Using artefact:

routes:
  "/":
    artefact: overview-report

Using layoutPages (single page):

routes:
  "/":
    layoutPages: overview-page

Using layoutPages (multiple pages):

routes:
  "/":
    layoutPages:
      - page-1
      - page-2
      - page-3

String entries in the array may also be glob patterns (*, ?, […]) that match LayoutPage names, e.g. - "sales-*". A glob entry expands to every matching page in name order; it cannot carry params.

The layoutPages option is useful when you want to serve individual LayoutPage documents directly without defining a full ReportArtefact.

Array of query parameters allowed for this specific route.

Each parameter has:

  • name (required) – parameter name, used as ${name} in documents.
  • type – input type for the serve UI. One of: string (default), number, number_range, select, date, date_time.
  • default – default value if parameter is not provided.
  • optional – if true, the parameter is optional even without a default value. The parameter will be empty if not provided.
  • description – human-readable description.
  • options – configuration for select, number, and number_range types: items for a static option list (each item requires a value, its label defaults to that value), dataset with valueColumn and labelColumn for options loaded from a DataSet, and min, max, step for numeric inputs.

Important: If a parameter has no default and optional is not true, the server returns HTTP 400 with a JSON error listing the missing parameters.

TypeInput controlDescription
stringText inputDefault type. Free-text input.
numberNumber inputNumeric input with optional min/max/step.
number_rangeDual range sliderFor min/max range filtering. Creates two parameters: {name} and {name}_max.
selectDropdownSelection from predefined options (static or from a DataSet).
dateDate pickerDate selection (YYYY-MM-DD format).
date_timeDateTime pickerDate and time selection (ISO 8601 format).

The options object supports different settings depending on the parameter type:

For select type:

queryParams:
  - name: REGION
    type: select
    description: "Select region"
    options:
      items:
        - value: "EU"
          label: "Europe"
        - value: "US"
          label: "United States"
        - value: "APAC"
          label: "Asia Pacific"

For select parameters with static items, you can access both the value and label:

  • ${REGION} – the value (e.g., "EU")
  • ${REGION_LABEL} – the label (e.g., "Europe")

This is useful for displaying human-readable text while using the value for data filtering.

For select with DataSet:

queryParams:
  - name: CATEGORY
    type: select
    description: "Select category"
    options:
      dataset: category_list
      valueColumn: category_id
      labelColumn: category_name

The referenced DataSet should return rows with the specified columns. If labelColumn is omitted, valueColumn is used for both.

For number and number_range types:

queryParams:
  - name: YEAR
    type: number
    default: "2024"
    options:
      min: 2000
      max: 2030
      step: 1
  - name: PRICE
    type: number_range
    description: "Price range filter"
    options:
      min: 0
      max: 10000
      step: 100

Query parameters are substituted into your report documents using the same ${VAR} syntax as environment variables. This allows dynamic filtering of data.

Example DataSet using a query parameter:

apiVersion: bino.bi/v1alpha1
kind: DataSet
metadata:
  name: filtered_sales
spec:
  type: query
  query: |
    SELECT * FROM sales
    WHERE year = ${YEAR}
    AND region = '${REGION}'

When accessed with ?YEAR=2024&REGION=EU, the query becomes:

SELECT * FROM sales
WHERE year = 2024
AND region = 'EU'

For select parameters with static items, use ${PARAM_LABEL} to show the human-readable label:

apiVersion: bino.bi/v1alpha1
kind: LayoutPage
metadata:
  name: sales-page
spec:
  children:
    - kind: Text
      spec:
        # Shows "Sales Report for Europe" instead of "Sales Report for EU"
        value: "Sales Report for ${REGION_LABEL}"
---
apiVersion: bino.bi/v1alpha1
kind: ReportArtefact
metadata:
  name: main-report
spec:
  filename: main.pdf
  title: "Main Report"
---
apiVersion: bino.bi/v1alpha1
kind: LiveReportArtefact
metadata:
  name: dashboard
spec:
  title: "Dashboard"
  routes:
    "/":
      artefact: main-report

Serve with:

bino serve --live dashboard
---
apiVersion: bino.bi/v1alpha1
kind: ReportArtefact
metadata:
  name: overview
spec:
  filename: overview.pdf
  title: "Overview"
---
apiVersion: bino.bi/v1alpha1
kind: ReportArtefact
metadata:
  name: sales
spec:
  filename: sales.pdf
  title: "Sales Detail"
---
apiVersion: bino.bi/v1alpha1
kind: ReportArtefact
metadata:
  name: regions
spec:
  filename: regions.pdf
  title: "Regional Analysis"
---
apiVersion: bino.bi/v1alpha1
kind: LiveReportArtefact
metadata:
  name: sales-app
spec:
  title: "Sales Application"
  routes:
    "/":
      artefact: overview
      queryParams:
        - name: YEAR
          default: "2024"
          description: "Reporting year"
    "/sales":
      artefact: sales
      title: "Sales Details"
    "/regions":
      artefact: regions
      title: "Regions"
---
apiVersion: bino.bi/v1alpha1
kind: DataSet
metadata:
  name: year_sales
spec:
  type: query
  query: |
    SELECT * FROM raw_sales
    WHERE extract(year from sale_date) = ${YEAR:2024}
---
apiVersion: bino.bi/v1alpha1
kind: DataSet
metadata:
  name: department_list
spec:
  type: query
  query: |
    SELECT DISTINCT department_id, department_name FROM departments ORDER BY department_name
---
apiVersion: bino.bi/v1alpha1
kind: LiveReportArtefact
metadata:
  name: dynamic-report
spec:
  title: "Dynamic Report"
  routes:
    "/":
      artefact: main-report
      queryParams:
        - name: YEAR
          type: number
          default: "2024"
          description: "Year for analysis"
          options:
            min: 2000
            max: 2030
        - name: DEPARTMENT
          type: select
          description: "Department (required)"
          options:
            dataset: department_list
            valueColumn: department_id
            labelColumn: department_name
        - name: START_DATE
          type: date
          description: "Start date for report period"
        - name: AMOUNT_RANGE
          type: number_range
          description: "Filter by amount"
          options:
            min: 0
            step: 100

Access patterns:

  • / – uses default YEAR=2024, but requires DEPARTMENT
  • /?DEPARTMENT=sales – works, uses default YEAR
  • /?YEAR=2023&DEPARTMENT=marketing – overrides YEAR
  • /?YEAR=2023 – returns HTTP 400: missing DEPARTMENT

LiveReportArtefact applications include built-in navigation. When navigating between routes:

  1. The browser URL updates via History API.
  2. Only the report content is fetched and swapped.
  3. Assets (CSS, fonts, scripts) are not reloaded.

This provides a smooth single-page application experience while maintaining proper URL routing.

When using layoutPages in a route, you can pass parameters to LayoutPages that define them. This combines the power of LayoutPage parameters with dynamic query parameters.

apiVersion: bino.bi/v1alpha1
kind: LiveReportArtefact
metadata:
  name: regional-dashboard
spec:
  title: "Regional Dashboard"
  routes:
    "/regional":
      layoutPages:
        - page: regional-sales
          params:
            REGION: ${REGION}      # From query params
            YEAR: ${YEAR}
      queryParams:
        - name: REGION
          type: select
          default: "EU"
          options:
            items:
              - value: "EU"
                label: "Europe"
              - value: "US"
                label: "North America"
              - value: "APAC"
                label: "Asia Pacific"
        - name: YEAR
          type: number
          default: "2024"
          options:
            min: 2020
            max: 2030

When users visit /regional?REGION=US&YEAR=2023, the query params flow into the LayoutPage params, rendering the page with those values.

You can mix static values with dynamic query param references:

routes:
  "/comparison":
    layoutPages:
      # First page: current year, user-selected region
      - page: regional-sales
        params:
          REGION: ${REGION}
          YEAR: "2024"              # Static value
          LABEL: "Current Year"

      # Second page: previous year, same region
      - page: regional-sales
        params:
          REGION: ${REGION}         # Same as user selected
          YEAR: "2023"              # Static: previous year
          LABEL: "Previous Year"
    queryParams:
      - name: REGION
        type: select
        default: "EU"

This creates a comparison view showing two years side by side.

Include the same LayoutPage multiple times with different parameter combinations:

routes:
  "/all-regions":
    layoutPages:
      - page: regional-sales
        params:
          REGION: EU
          YEAR: ${YEAR}
      - page: regional-sales
        params:
          REGION: US
          YEAR: ${YEAR}
      - page: regional-sales
        params:
          REGION: APAC
          YEAR: ${YEAR}
    queryParams:
      - name: YEAR
        type: number
        default: "2024"

This shows all three regions on a single page, with the year controlled by the query parameter.

---
# Define a parameterized LayoutPage
apiVersion: bino.bi/v1alpha1
kind: LayoutPage
metadata:
  name: department-kpis
  params:
    - name: DEPARTMENT
      type: string
      required: true
    - name: YEAR
      type: number
      default: "2024"
    - name: SHOW_FORECAST
      type: boolean
      default: "false"
spec:
  titleBusinessUnit: "${DEPARTMENT} KPIs"
  pageLayout: 2x2
  children:
    - kind: Text
      spec:
        value: "${DEPARTMENT} Performance - ${YEAR}"
    - kind: ChartStructure
      spec:
        dataset: department-data
        chartTitle: "Revenue"
    - kind: Table
      spec:
        dataset: department-data

---
# Use it in a LiveReportArtefact
apiVersion: bino.bi/v1alpha1
kind: LiveReportArtefact
metadata:
  name: department-dashboard
spec:
  title: "Department Dashboard"
  routes:
    # Single department view
    "/":
      layoutPages:
        - page: department-kpis
          params:
            DEPARTMENT: ${DEPARTMENT}
            YEAR: ${YEAR}
            SHOW_FORECAST: ${SHOW_FORECAST:false}
      queryParams:
        - name: DEPARTMENT
          type: select
          description: "Select department"
          options:
            dataset: department_list
            valueColumn: dept_id
            labelColumn: dept_name
        - name: YEAR
          type: number
          default: "2024"
          options:
            min: 2020
            max: 2030
        - name: SHOW_FORECAST
          type: select
          default: "false"
          options:
            items:
              - value: "true"
                label: "Yes"
              - value: "false"
                label: "No"

    # All departments view
    "/all":
      layoutPages:
        - page: department-kpis
          params:
            DEPARTMENT: "Sales"
            YEAR: ${YEAR}
        - page: department-kpis
          params:
            DEPARTMENT: "Marketing"
            YEAR: ${YEAR}
        - page: department-kpis
          params:
            DEPARTMENT: "Engineering"
            YEAR: ${YEAR}
      queryParams:
        - name: YEAR
          type: number
          default: "2024"

Access patterns:

  • /?DEPARTMENT=Sales – Shows Sales department for 2024
  • /?DEPARTMENT=Marketing&YEAR=2023 – Shows Marketing for 2023
  • /all – Shows all departments for 2024
  • /all?YEAR=2022 – Shows all departments for 2022

The CLI validates LiveReportArtefact manifests at load time:

  • Root route / must be present.
  • All route paths must start with /.
  • The paths /manifest.webmanifest and /sw.js are reserved for PWA serving and are rejected as route paths — even when spec.pwa is not set.
  • All referenced artefact names must correspond to existing ReportArtefact manifests.
  • All referenced LayoutPages in layoutPages must exist.
  • Query parameter names must be unique within each route.
  • Every spec.pwa.icons[].asset must name an existing Asset manifest with spec.type: image and a source.localPath.

Validation errors are reported before the server starts.

AttributeTypeRequiredDefaultDescription
apiVersionstringyesMust be bino.bi/v1alpha1.
kindstringyesMust be LiveReportArtefact.
metadata.namestringyesUnique identifier, used with bino serve --live <name>.
metadata.labelsobjectnoKey-value pairs for categorization and constraint matching.
metadata.annotationsobjectnoArbitrary key-value metadata, not used by the system.
metadata.descriptionstringnoFree-form description.
metadata.constraintsarraynoConditional inclusion rules. See Constraints.
AttributeTypeRequiredDefaultDescriptionSample
spec.titlestringyesHuman-readable title of the live report application. Must not be empty.title: "Sales Dashboard"
spec.descriptionstringnoDescription of the application. Also the fallback for spec.pwa.description.description: "Interactive sales reporting application"
spec.pwaobjectnoProgressive Web App configuration. When present, bino serve adds a web app manifest, a service worker, and the related tags; when absent, serving behavior is unchanged. See spec.pwa.pwa: { shortName: Sales, icons: [...] }
spec.pwa.namestringnospec.titleFull application name shown during installation and on splash screens.name: "Sales Dashboard"
spec.pwa.shortNamestringnopwa.nameShort name shown under the home screen icon.shortName: Sales
spec.pwa.descriptionstringnospec.descriptionDescription shown in install prompts.description: "Sales KPIs on the go"
spec.pwa.themeColorstringnoTheme color applied to the browser UI around the installed app.themeColor: "#0B5FFF"
spec.pwa.backgroundColorstringnoBackground color of the splash screen shown while the installed app loads.backgroundColor: "#FFFFFF"
spec.pwa.displaystringnostandaloneDisplay mode of the installed app. Values: standalone, fullscreen, minimal-ui, browser.display: fullscreen
spec.pwa.iconsarrayyes (within pwa)Icons offered to the operating system, at least one. A 512x512 icon is recommended. See spec.pwa.icons.icons: [{ asset: app-icon, sizes: 512x512 }]
spec.pwa.icons[].assetstringyesmetadata.name of an Asset with spec.type: image and a source.localPath. Remote or inline asset sources are rejected.asset: app-icon-512
spec.pwa.icons[].sizesstringyesPixel dimensions as WIDTHxHEIGHT.sizes: 512x512
spec.pwa.icons[].purposestringnoanyIcon purpose. Values: any for a regular icon, maskable for an icon with a safe zone the OS may mask.purpose: maskable
spec.routesobjectyesMap of URL paths to route configurations. The key / is mandatory; every path must start with /; /manifest.webmanifest and /sw.js are reserved. See spec.routes.routes: { "/": { artefact: overview-report } }
spec.routes[path]objectOne route. Must set exactly one of artefact or layoutPages."/sales": { artefact: sales-report, title: "Sales Analysis" }
spec.routes[path].artefactstringone of artefact / layoutPagesmetadata.name of the ReportArtefact rendered for this route. Cannot be combined with layoutPages.artefact: overview-report
spec.routes[path].layoutPagesstring or arrayone of artefact / layoutPagesOne or more LayoutPage documents rendered directly, without a ReportArtefact wrapper. Cannot be combined with artefact.layoutPages: overview-page
spec.routes[path].layoutPages[]string or objectArray entry: a LayoutPage name, a glob pattern matching names, or the object form below.- summary-page
spec.routes[path].layoutPages[].pagestringyes (in object form)Exact LayoutPage name, no glob pattern. See Parameterized LayoutPages in routes.page: regional-sales
spec.routes[path].layoutPages[].paramsobject of stringsnoParameter values passed to the LayoutPage; keys must match its metadata.params. Values may be static or ${QUERY_PARAM} references.params: { REGION: "${REGION}" }
spec.routes[path].titlestringnoPage title override for this route. Titled non-root routes also become PWA app shortcuts.title: "Regional Breakdown"
spec.routes[path].queryParamsarraynoQuery parameters allowed for this route, substituted as ${VAR} in the documents. Names must be unique per route. See spec.routes[path].queryParams.queryParams: [{ name: YEAR, default: "2024" }]
spec.routes[path].queryParams[].namestringyesParameter name, used as ${name} in documents. Must match ^[A-Za-z_][A-Za-z0-9_]*$.name: REGION
spec.routes[path].queryParams[].typestringnostringInput control in the serve UI. Values: string, number, number_range, select, date, date_time. See Parameter types.type: select
spec.routes[path].queryParams[].defaultstringnoDefault value when the parameter is not provided. Without a default and without optional: true the request returns HTTP 400.default: "2024"
spec.routes[path].queryParams[].optionalbooleannofalseIf true, the parameter may be missing without a default; it is then empty.optional: true
spec.routes[path].queryParams[].descriptionstringnoHuman-readable description of what the parameter controls.description: "Year to display"
spec.routes[path].queryParams[].optionsobjectnoInput configuration for select, number, and number_range. See Options configuration.options: { min: 2000, max: 2030 }
spec.routes[path].queryParams[].options.itemsarraynoStatic option list for select.items: [{ value: "EU", label: "Europe" }]
spec.routes[path].queryParams[].options.items[].valuestringyesValue submitted when the option is selected, available as ${NAME}.value: "EU"
spec.routes[path].queryParams[].options.items[].labelstringnothe item's valueHuman-readable label, available as ${NAME_LABEL}.label: "Europe"
spec.routes[path].queryParams[].options.datasetstringnoName of a DataSet the select options are loaded from.dataset: category_list
spec.routes[path].queryParams[].options.valueColumnstringnoDataset column used as the option value.valueColumn: category_id
spec.routes[path].queryParams[].options.labelColumnstringnovalueColumnDataset column used as the option label.labelColumn: category_name
spec.routes[path].queryParams[].options.minnumbernoMinimum value for number and number_range.min: 2000
spec.routes[path].queryParams[].options.maxnumbernoMaximum value for number and number_range.max: 2030
spec.routes[path].queryParams[].options.stepnumbernoStep increment for number and number_range.step: 100

See ReportArtefact for the PDF deliverable served by a route, and bino serve for running the application.