Skip to main content
Version: v0.38.0

Playbooks

Playbooks are the core of the regis evaluation engine. They define the security and compliance rules that the tool evaluates against container image metadata.

Purpose

A playbook serves two primary functions:

  1. Policy Enforcement: It defines a set of rules that an image must pass to be considered compliant.
  2. Report Structuring: It controls what badges, tiers, and links appear in the generated report.

By using playbooks, you can decouple the raw data extraction (performed by analyzers like OCI or CVE) from the business logic used to evaluate that data. This allows you to apply different compliance standards to different environments or projects without changing the underlying analysis code.

Playbook format

Playbooks use a Kubernetes-style resource envelope. Every playbook.yaml must declare these four top-level keys:

FieldRequiredDescription
apiVersionyesMust be regis.io/v1alpha1.
kindyesMust be Playbook.
metadatayesIdentity and version of this playbook (see below).
specyesRules, tiers, badges, presentation directives, and links (see below).

metadata fields

FieldRequiredDescription
metadata.nameyesMachine identifier — RFC 1123 DNS label (lowercase alphanumerics and -, max 63 chars).
metadata.titlenoHuman-readable display name shown in reports.
metadata.descriptionnoHuman-readable description of what this playbook evaluates.
metadata.labels["app.kubernetes.io/version"]yesSemVer of your playbook bundle (e.g. "1.0.0"). Bump when you change rules.
metadata.annotationsnoFree-form non-identifying metadata (arbitrary string key/value pairs).

spec fields

FieldRequiredDescription
spec.tiersnoCompliance tier thresholds (Gold / Silver / Bronze).
spec.rulesnoRules: bindings of a criterion (provider + criterion slug + options).
spec.badgesnoDynamic status badges displayed in the report header.
spec.presentationnoPlatform-neutral presentation directives (badge labels, checklists, templates) consumed by integrations (e.g. GitLab MR, GitHub PR, Backstage).
spec.linksnoCustom action links displayed in the report.

Field mapping from the legacy format

If you have an existing playbook that uses the old flat format, run:

regis playbook upgrade path/to/playbook.yaml

The mapping is:

Legacy fieldNew location
schemaVersionreplaced by apiVersion
versionmetadata.labels["app.kubernetes.io/version"]
namemetadata.title
slugmetadata.name
descriptionmetadata.description
tiersspec.tiers
rulesspec.rules
badgesspec.badges
integrationsspec.presentation
linksspec.links
pages/sections/sidebarremoved (not used by the report viewer)

Core Concepts

The following concepts are central to understanding and creating playbooks. For a complete technical reference of all available attributes, refer to the Playbook Schema Reference.

Tiers

Playbooks can define Tiers to categorize the overall quality of an image based on the compliance score. Each tier is defined by a name, a condition, and an optional icon.

spec:
tiers:
- name: Gold
icon: "🥇" # optional display icon (emoji)
condition: { ">": [{ var: rules_summary.score }, 90] }
- name: Silver
icon: "🥈"
condition: { ">": [{ var: rules_summary.score }, 70] }
- name: Bronze
icon: "🥉"
condition: { ">": [{ var: rules_summary.score }, 50] }

The evaluator checks tiers in the order they are defined. The first tier whose condition evaluates to truthy is assigned to the report. The icon is surfaced in the analyze verdict block and reports; a tier without an icon renders with a neutral marker.

Badges

Badges provide high-level visual status indicators in the report header. They are dynamic and support variable interpolation using the ${var.path} syntax.

spec:
badges:
- slug: score
scope: Score
value: "${rules_summary.score}"
class: information
- slug: freshness
scope: Freshness
condition: { "==": [{ var: rules.freshness-age.passed }, true] }
class: success
FieldDescription
slugA unique machine-readable identifier for the badge (used for lookups and integrations).
scopeThe primary label for the badge (e.g., "Score", "CVE").
valueThe value to display. Can use ${} interpolation to reference report data.
conditionA JSON Logic expression. If provided, the badge is only displayed when the condition is truthy.
classThe visual style/color of the badge. Supported: success (green), warning (yellow), error (red), information (blue).

Evaluation Mechanism

regis uses two powerful technologies to evaluate and present data in playbooks.

JSON Logic

Scorecard conditions and widget display preferences use JSON Logic. This is a lightweight, language-agnostic way to define complex conditional logic as JSON objects.

You use JSON Logic to access analysis results and perform comparisons. For example, to check if an image has no critical vulnerabilities, you would use:

{ "==": [{ "var": "results.cve.critical_count" }, 0] }

Or, to check the overall playbook score:

{
">=": [{ "var": "playbooks.0.score" }, 90]
}

Commonly used operators include:

  • ==, !=: Equality and inequality.
  • >, >=, <, <=: Numeric comparisons.
  • in: Checks if a value is present in a list.
  • !, !!: Logical NOT and non-null checks.
  • and, or: Logical combinations.

Template Interpolation (Jinja2)

While JSON Logic handles the evaluation "truth," regis uses Jinja2 for dynamic data interpolation within the report. You can use Jinja2 expressions within widgets to format values or calculate percentages directly from the analysis context.

For example, to display the overall compliance score in a widget, you might use:

- label: Overall Compliance
value: "{{ playbooks.0.score }}%"
- label: Mandatory Checks
value: "{{ playbooks.0.score }}%"

Presentation Directives

spec.presentation defines platform-neutral directives that consuming integrations (GitLab MR, GitHub PR, Backstage, etc.) can render. The playbook engine evaluates these directives once and exposes the results in the report; each integration then decides how to surface them.

Badge Labels

The badge_labels list specifies which badge slugs integrations should surface as status labels. Each slug must match a badge defined in spec.badges.

spec:
presentation:
badge_labels:
- score
- freshness
- cve-critical

Integrations use this list to keep their label surface (e.g. GitLab MR labels, GitHub PR labels) in sync with the badge values shown in the HTML report.

Checklists

The checklists list adds configurable verification checklists to integration surfaces (e.g. the Merge Request or Pull Request description). Each checklist lets you define manual steps that reviewers must tick off before approving.

Each checklist can have a title and a list of items. Each item has a mandatory label and two optional conditions:

FieldDescription
show_ifA JSON Logic expression. If provided, the item is only added to the checklist when the expression evaluates to truthy. Items referencing unavailable data are excluded.
check_ifA JSON Logic expression. If provided and truthy, the checkbox renders pre-checked (- [x]). Otherwise it renders unchecked (- [ ]).
spec:
presentation:
checklists:
- title: 📝 Security Review
items:
- label: Security review completed # <1>
- label: No critical vulnerabilities found
show_if: { "==": [{ var: results.cve.critical_count }, 0] } # <2>
check_if: { "==": [{ var: results.cve.critical_count }, 0] } # <3>
- title: 🚀 Compliance checks
items:
- label: Image from a trusted registry
show_if:
{
"in":
[{ var: request.registry }, [docker.io, quay.io, ghcr.io]],
}
check_if:
{
"in":
[{ var: request.registry }, [docker.io, quay.io, ghcr.io]],
}

(1) Unconditional item — always included, always unchecked. (2) show_if — only included when critical_count equals 0. (3) check_if — if included, renders pre-checked when critical_count is 0.

tip

You can use show_if and check_if independently. For example, an item may always be shown (no show_if) but pre-checked only when a condition passes.

The engine exposes the resolved checklists as checklists (a list of {title, items: [{label, checked}]} objects) in the playbook evaluation result. Each integration converts this list to its native format — for example, a GitLab CI job appends them as Markdown checklists to the MR description.

Templates

The templates list lets you define Cookiecutter templates that integrations can render into their target branch or workspace. This is useful for automatically generating boilerplate code, security compliance files, or evidence reports based on the analysis results.

Each item must have a url and an optional condition:

FieldDescription
urlThe HTTP URL or local path to a Cookiecutter template folder or repository.
directoryIf url points to an overarching Git repository containing multiple environments, this specifies the subdirectory containing the Cookiecutter template.
conditionA JSON Logic expression. If provided, the template is only evaluated and generated when the condition evaluates to truthy.
spec:
presentation:
templates:
- url: "https://github.com/my-org/security-evidence-template"
directory: "templates/my-evidence" # optional
condition: { ">": [{ var: results.cve.critical_count }, 0] }
warning

Because Cookiecutter ignores extra context variables that aren't defined in the template, your template's cookiecutter.json must include a "regis" property (even if it's just an empty object) to receive the analysis context:

{
"regis": {}
}

During evaluation, templates whose condition passes are aggregated and exposed as templates in the playbook result. Each integration then executes these templates with the full analysis context (e.g., cookiecutter.regis.score) and writes the generated files to the appropriate location.

Bundle Structure

A playbook is a directory (bundle) rather than a single file. The bundle uses fixed filenames by convention:

my-playbook/
├── playbook.yaml # apiVersion/kind/metadata/spec envelope
├── meta.schema.json # JSON Schema for --meta validation
└── README.md

You can pass a bundle directory anywhere a playbook path is accepted:

regis analyze myimage:latest --playbook ./my-playbook/

Legacy single-file playbooks using the old schemaVersion/name format are automatically upgraded on load. To migrate permanently, run:

regis playbook upgrade path/to/playbook.yaml

Metadata

Playbooks can declare required and optional metadata fields that must be supplied by the project using --meta KEY=VALUE flags. Typical use cases: internal project identifiers, security validation document URLs, or compliance ticket references.

Well-known fields

Regis ships with a base schema defining standard CI metadata fields:

FieldTypeDescription
ci.platform"github" | "gitlab"CI platform
ci.job.idstringCI job identifier
ci.job.urlstring (URI)URL to the CI job run

These fields are always recognized — no schema required to use them.

Extending the schema

To declare project-specific required or optional fields, create a meta.schema.json inside the bundle that extends the well-known base via JSON Schema allOf:

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"allOf": [{ "$ref": "https://regis/schemas/meta/well-known.schema.json" }],
"properties": {
"PROJECT_ID": {
"type": "string",
"description": "Internal project identifier"
},
"SEC_DOC_URL": {
"type": "string",
"format": "uri",
"description": "Link to security validation document"
}
},
"required": ["PROJECT_ID"]
}

Missing required fields cause the metadata analyzer to report a validation failure. Optional fields not provided are recorded as null but do not fail the analysis.

Supplying metadata

Pass metadata at analysis time using --meta:

regis analyze myimage:latest \
--playbook ./my-playbook/ \
-m PROJECT_ID=PROJ-42 \
-m SEC_DOC_URL=https://jira.example.com/browse/SEC-99 \
-m ci.platform=github

Metadata is stored in the report under the top-level metadata namespace, making it accessible in all rule conditions, badges, and template expressions (for example {"var": "metadata.ci.platform"}).

Deferred metadata (--rerun)

Metadata is often not available at analysis time — for example, the security validation document URL is only known after a parallel approval process completes. You can re-inject metadata without re-running the full image analysis:

# Day 1: full analysis (PROJECT_ID not yet known)
regis analyze myimage:latest --playbook ./my-playbook/

# Day N: once the project ID is confirmed
regis analyze --rerun metadata \
--report ./reports/registry/repo/digest/ \
-m PROJECT_ID=PROJ-42 \
-m SEC_DOC_URL=https://jira.example.com/browse/SEC-99

The --rerun path updates report.json in place and replays the full playbook evaluation (rules → tiers → badges) against the patched data.

Using metadata in rules, badges, and checklists

Metadata values are accessible under metadata.* via JSON Logic var:

Rule — require PROJECT_ID before granting a compliance tier:

spec:
rules:
- provider: metadata
criterion: metadata
slug: project-registered
level: critical
condition:
"!!": [{ var: "metadata.PROJECT_ID" }]
messages:
pass: "Project ID provided: ${metadata.PROJECT_ID}"
fail: "PROJECT_ID is required for compliance reporting"

Badge — display the project ID:

spec:
badges:
- slug: project-id
scope: Project
value: "${metadata.PROJECT_ID}"
condition:
"!!": [{ var: "metadata.PROJECT_ID" }]
class: information

Presentation checklist — link to the security document:

spec:
presentation:
checklists:
- title: 📋 Security Evidence
items:
- label: Security validation document submitted
show_if: { "!!": [{ var: "metadata.SEC_DOC_URL" }] }
check_if: { "!!": [{ var: "metadata.SEC_DOC_URL" }] }
- label: "Review document: ${metadata.SEC_DOC_URL}"
show_if: { "!!": [{ var: "metadata.SEC_DOC_URL" }] }

Tier condition — gate Gold tier on CI platform:

spec:
tiers:
- name: Gold
condition:
and:
- { ">": [{ var: rules_summary.score }, 90] }
- { "==": [{ var: "metadata.ci.platform" }, "github"] }

Creating a Custom Playbook

While you can write a playbook from scratch, the easiest way to start is by using the bootstrap playbook command. This creates a new directory with a pre-configured playbook template and all necessary files.

regis bootstrap playbook my-custom-playbook

This command will prompt you for basic information (name, slug, etc.) and generate a skeleton playbook that you can then customize with your own rules and scorecards. For more information, see the Bootstrapping Reference.

Example Playbook

The following example shows a minimal valid playbook definition:

# yaml-language-server: $schema=https://trivoallan.github.io/regis/schemas/playbook/v1alpha1/playbook.schema.json
apiVersion: regis.io/v1alpha1
kind: Playbook
metadata:
name: minimal
title: Minimal Playbook
labels:
app.kubernetes.io/version: "1.0.0"
spec:
tiers:
- name: Gold
condition: { ">": [{ var: rules_summary.score }, 90] }
- name: Silver
condition: { ">": [{ var: rules_summary.score }, 70] }
- name: Bronze
condition: { ">": [{ var: rules_summary.score }, 50] }
rules:
- provider: oci
criterion: user-blacklist
slug: no-root
level: critical
options:
blacklist: [root, "0"]
tip

To see the full set of rules and report organization enforced by the tool out-of-the-box, check the Default Playbook Reference.

Local Evaluation (Dry-run)

When developing custom playbooks, you can evaluate them against existing analysis results without re-running the full image analysis. This is faster and doesn't require registry access once you have a base report.json.

Use the evaluate subcommand:

# 1. Run a full analysis once to get the raw data
regis analyze nginx:latest -o report.json

# 2. Iterate on your playbook locally
regis evaluate report.json -p my-playbook.yaml --html

The evaluate command supports most of the reporting options found in analyze, including --html and --output-dir.