Skip to content

Model Schema Reference

Complete reference for system.model.yaml — the file that describes your system.

On this page


Minimal example

meta:
  name: my-system
  version: "1.0"
  providers:
    - kubernetes

components:
  api:
    type: deployment
    depends:
      - on: db

  db:
    type: rds_instance
    providers:
      - aws

Full schema

meta:
  name: <string>            # required — system name
  version: <string>         # required — model version (semver)
  providers:                # optional — default providers for all components
    - <provider-name>
  vars:                     # optional — variables substituted into probe commands
    <key>: <value>
  strict_types: <bool>      # optional — reject the generic fallback at validate time
  scenarios: none           # optional — opt out of scenarios.yaml generation + drift check

components:
  <component-name>:
    type: <type-name>       # required — a type defined by a provider
    resource: <string>      # optional — upstream resource id the provider probes
                            #   (supports {key} placeholders from meta.vars)
    providers:              # optional — override meta.providers for this component
      - <provider-name>
    depends:                # optional — components this one depends on
      - on: <component-name>
    healthy:                # optional — replaces the provider type's default
                            #            healthy rules (does not merge)
      - <expression>
    vars:                   # optional — per-component overrides of meta.vars
      <key>: <value>

meta section

Field Required Description
name yes System name. Used in output and state file naming.
version yes Model version. Quoted string — "1.0", not 1.0.
providers no List of provider names. Default providers for all components. When omitted or a type isn't matched, mgtt falls back to a built-in generic.component that prompts the operator interactively. Install typed providers with mgtt provider install <name>.
vars no Key-value pairs substituted into probe commands as {key}. Common: namespace, region, cluster.
strict_types no true rejects the generic fallback: any component whose type has no typed provider becomes a validation error. Default false emits one INFO line per component that falls back.
scenarios no Set to none to opt the model out of scenarios.yaml generation and drift detection. Use for empty placeholder models or works-in-progress where the sidecar would be meaningless.

components section

Each key under components is the component name. Names must be unique within the model.

Field Required Description
type yes A type defined by one of the listed providers. See Type Catalog for available types.
resource no Upstream resource identifier. When set, the provider looks up <resource> instead of the component key at probe time. Lets you keep readable component keys (e.g. rds:) while probing the real backing resource (e.g. an RDS DB instance id, a kubectl-named Deployment, a Docker container name — whatever the owning provider expects). Supports {key} placeholders that expand against meta.vars at load time — a model shipped across environments can use resource: my-database-{env}. Unresolved placeholders are a load-time error.
providers no Override meta.providers for this component. Use when one component belongs to a provider different from the model's default set (e.g., a single RDS instance in a model whose defaults are Kubernetes).
depends no List of dependency entries. See Dependencies below.
healthy no Health conditions for this component. When set, replaces the provider type's default healthy rules (does not merge). See Health expressions below for semantics and a common pitfall.
vars no Per-component overrides of meta.vars. Merged on top of meta.vars at probe time — component keys win on collision, missing keys fall through to the model-wide default. Use when a single component lives in a different namespace, region, or cluster from the rest. Example: an ExternalSecrets operator in namespace external-secrets in a model whose other components live in default.

Readable component keys vs. provider resource identifiers

Real infrastructure identifiers are often noisy (E3AB12CD34EF56, my-app-prod-media-a12d4c, /config/prod/env_php). Use resource: to keep the model's dependency graph readable while probes hit the real resources:

components:
  rds:
    type: rds_instance
    resource: my-database-name
  cdn:
    type: cloudfront_distribution
    resource: E3AB12CD34EF56

meta.vars substitution lets a single model ship across environments:

meta:
  vars:
    env: stage
components:
  rds:
    type: rds_instance
    resource: my-database-{env}   # expands to my-database-stage

Unresolved {key} placeholders are a load-time error — better to fail here than to produce a literal {env} string in a kubectl call at 3am.

Dependencies

Each entry in depends declares a dependency on another component:

depends:
  - on: api
  - on: rds
Field Required Description
on yes Name of the component this one depends on. Must exist in components.

Dependencies are directional: nginx.depends.on: api means "nginx depends on api" — if api is unhealthy, nginx may be affected.

The engine uses the dependency graph to:

  1. Determine probe order (start from outermost, work inward)
  2. Build failure paths (trace from symptom to root cause)
  3. Eliminate healthy branches (if a dependency is healthy, its sub-tree is cleared)

Soft dependencies

All dependencies are currently treated as hard — if a dependency is unhealthy, the engine considers the dependent component potentially affected. Soft/optional dependency support (soft: true) is planned but not yet implemented. If your system has optional dependencies, model only the hard ones for now.

Health expressions

The healthy field sets the health rules for the component. When omitted, the provider type's default healthy rules apply (e.g. deployment defaults to ready_replicas == desired_replicas; elasticache_cluster defaults to available == true && cache_hit_ratio > 80).

healthy:
  - connection_count < 500
  - response_time < 1000

Override semantics — replace, don't merge

A component-level healthy: block replaces the type's default rules; it does not merge with them. This is the most common mgtt modelling pitfall, so it's worth stating loudly:

If you set healthy: on a component, the provider type's default rules for that component are no longer evaluated.

Concretely: the elasticache_cluster type defaults to available == true && cache_hit_ratio > 80. If your model says —

redis:
  type: elasticache_cluster
  healthy:
    - cache_hit_ratio > 70          # intended: "prod baseline"

— you've lost the available == true check. A redis with available=false but some residual cache hits could pass your rule.

The fix is to restate every rule you want enforced, in full:

redis:
  type: elasticache_cluster
  healthy:
    - available == true
    - cache_hit_ratio > 70

Or, on environments where a stricter default is itself a false-positive (idle stage has cache_hit_ratio = 0), narrow the rule to only what's meaningful:

redis:
  type: elasticache_cluster
  healthy:
    - available == true               # cache_hit_ratio omitted on purpose

Component-level rules win because per-environment health criteria vary: a 70% cache-hit ratio is great on prod, meaningless on an idle stage. Merging the provider's prod-shaped default with a per-env override would produce confusing AND-conjunctions that fail in ways the operator didn't author. See the Type Catalog for each provider type's default rules — read before overriding.

Expression syntax

Each expression follows the pattern:

<fact_name> <operator> <value>

Operators:

Operator Meaning
== equals
!= not equals
< less than
> greater than
<= less than or equal
>= greater than or equal

Values: integers (500), floats (0.95), booleans (true, false), strings ("running").

Fact names must be facts defined by the provider for that component's type. See Type Catalog for which facts each type exposes.

Compound expressions (used in provider state definitions, not in model healthy fields):

Syntax Meaning
expr1 & expr2 both must be true
expr1 \| expr2 either must be true

In the model's healthy list, each entry is a separate condition. All must hold for the component to be healthy (implicit AND).


Complete example

meta:
  name: storefront
  version: "1.0"
  providers:
    - kubernetes
  vars:
    namespace: production

components:
  nginx:
    type: ingress           # from kubernetes provider
    depends:
      - on: frontend
      - on: api

  frontend:
    type: deployment         # from kubernetes provider
    depends:
      - on: api

  api:
    type: deployment         # from kubernetes provider
    depends:
      - on: rds

  rds:
    providers:
      - aws                  # override: use aws provider, not kubernetes
    type: rds_instance       # from aws provider
    healthy:
      - connection_count < 500  # additional condition beyond provider defaults

Validation

mgtt model validate              # validate the default system.model.yaml
mgtt model validate path/to.yaml # validate a specific file

The validator checks:

  • All component types exist in the declared providers (or fall back to generic.component and emit an INFO line, unless strict_types: true)
  • All dependency targets exist in components
  • No circular dependencies
  • Health expressions reference valid facts for the component's type
  • Expression syntax is correct
  • state.triggered_by labels (declared by providers) have at least one failure_modes.can_cause producer — unknown labels raise a warning (unreachable state)
  • Existing scenarios.yaml sidecar still matches the model + types (source-hash drift) — unless meta.scenarios: none
  • Duplicate (type, resource) tuples under the same provider produce a warning (usually a copy-paste mistake; legitimate when two probes target one resource with different fact sets).

See also: