Skip to main content

Morphir YAML configuration specification

Status and scope

This document specifies the morphir.yaml configuration format. It is a second serialization of the same configuration model as morphir.toml.

  • Status: Supported by the Morphir Rust configuration loader and CLI.
  • Applies to: Project, workspace, user, and system configuration.
  • Out of scope: Morphir IR YAML and the legacy morphir.json project file.

The words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY state requirements for conforming implementations.

Design rule

Parsing equivalent TOML and YAML files MUST produce the same nested configuration value. Field names, defaults, validation, path resolution, and merge behavior do not change with the serialization.

For example, these documents are equivalent:

morphir.toml
[project]
name = "acme/orders"
source_directory = "src"
exposed_modules = ["Orders.Api", "Orders.Types"]

[ir]
format_version = 3
strict_mode = true
morphir.yaml
project:
name: acme/orders
source_directory: src
exposed_modules:
- Orders.Api
- Orders.Types

ir:
format_version: 3
strict_mode: true

Both parse to:

{
"project": {
"name": "acme/orders",
"source_directory": "src",
"exposed_modules": ["Orders.Api", "Orders.Types"]
},
"ir": {
"format_version": 3,
"strict_mode": true
}
}

YAML profile

A morphir.yaml file MUST meet these syntax rules:

  • Use YAML 1.2 and the Core Schema.
  • Encode the file as UTF-8.
  • Contain exactly one YAML document.
  • Use a mapping at the document root.
  • Use strings for every mapping key. Keys are case-sensitive.
  • Use only values that have a direct TOML and JSON representation: mappings, sequences, strings, finite numbers, and booleans.
  • Reject null values. Omit an optional key instead.
  • Reject duplicate mapping keys.
  • Reject custom tags, anchors, aliases, and the YAML merge key <<.

These limits remove features that YAML libraries handle differently. They also make conversion to the shared JSON-equivalent configuration model deterministic.

Authors SHOULD quote a string when a reader could mistake it for another scalar type. This is especially useful for versions, durations, dates, and strings such as null, true, or 1.0.

project:
version: "1.0"

toolchain:
morphir-elm:
timeout: "5m"

Configuration model

The root mapping accepts the same optional keys as morphir.toml:

KeyValueMeaning
morphirmappingMorphir IR version constraints
workspacemappingWorkspace discovery and output layout
projectmappingProject metadata and decorations
irmappingIR processing settings
codegenmappingCode generation settings
cachemappingCache settings
loggingmappingLogging settings
uimappingUI and TUI settings
frontendmappingFrontend parsing settings
sourcesmappingRemote source settings
dependenciesmappingProject dependencies
dev-dependenciesmappingDevelopment-only dependencies
extensionsmappingExtension definitions
tasksmappingProject task definitions
workflowsmappingNamed workflows
bindingsmappingExternal binding type mappings
toolchainmappingExternal tool adapters and task catalogs

The field definitions, allowed values, defaults, and constraints in the Morphir TOML configuration specification are normative for both serializations. In YAML, each TOML table becomes a mapping and each TOML array becomes a sequence. Snake-case field names remain unchanged.

The machine-readable definition is the shared Morphir configuration schema. A loader MUST validate the parsed configuration value, not the YAML syntax tree, against that schema.

Unknown properties follow the shared schema. Version 1 currently permits them so tools can add settings without invalidating the whole document. A tool MAY warn when it does not recognize a property, but it MUST preserve the distinction between an unknown property and a known property with an invalid value.

File names and discovery

morphir.yaml is the canonical YAML file name. A conforming implementation MUST NOT discover morphir.yml implicitly. A user may still select a .yml file through a command option that accepts an explicit path.

YAML uses the locations corresponding to the TOML configuration sources:

PrecedenceYAML path
System/etc/morphir/morphir.yaml
Global user<platform-config-directory>/morphir/morphir.yaml or <user-home>/.morphir/morphir.yaml
Projectmorphir.yaml, .morphir/morphir.yaml, or .config/morphir/config.yaml
User overrideAdjacent YAML override for the selected primary layout

Built-in defaults and MORPHIR_* environment variables have no file serialization.

The global user path resolution rules define the XDG, macOS, and Windows directories for both serializations. The two directories are alternate locations at the same precedence. Across both directories and both serializations, a loader MUST discover at most one global user file. If it finds more than one, discovery MUST fail with an ambiguity error that names every candidate. A loader MUST NOT merge global user files or choose one by directory or extension precedence.

At any other location, a loader MUST accept at most one serialization. If corresponding TOML and YAML files both exist, discovery MUST fail with an ambiguity error that names both files. A loader MUST NOT merge sibling TOML and YAML files or choose one by extension precedence.

The root, hidden, and dot-config project paths are alternate locations, not merge layers. If more than one exists, discovery MUST report the same kind of ambiguity. The merge rules define the layout-derived user-override paths and workspace/member order.

Merge behavior

After parsing, YAML sources use the Morphir configuration merge rules. The merge algorithm operates on the configuration model, so maps merge recursively, sequences replace earlier sequences, and later sources take precedence.

Secret values

Secret references use the same reserved shape as TOML, written as an ordinary mapping. No YAML tag is involved, so the YAML profile is unchanged:

registry:
token: { env: GITHUB_TOKEN }
password: { file: "~/.config/morphir/registry-password" }
command_token: { command: [gh, auth, token] }
keyring_token: { keyring: { service: github.com, account: damre } }

Block style is equivalent:

registry:
token:
env: GITHUB_TOKEN

The recognition rule, resolution rules, display rules, and merge behaviour are defined once in the TOML specification and apply unchanged. A mapping is a secret reference only when it has exactly one of the four documented env, file, command, or keyring shapes. Anything else is an ordinary mapping.

Complete example

morphir.yaml
morphir:
version: "^3.0.0"

workspace:
output_dir: .morphir
members:
- packages/*
exclude:
- packages/experimental-*
default_member: packages/orders

project:
name: acme/orders
version: "1.2.0"
source_directory: src
exposed_modules:
- Orders.Api
module_prefix: Orders
decorations:
pii:
display_name: Personal data
ir: decorations/pii-ir.json
entry_point: Acme.Decorations:Pii:Definition
storage_location: decorations/pii-values.json

ir:
format_version: 3
strict_mode: true

codegen:
targets:
- go
- json-schema
template_dir: templates
output_format: pretty

cache:
enabled: true
dir: .morphir/cache
max_size: 1073741824

logging:
level: info
format: text

ui:
color: true
interactive: true
theme: default

tasks:
compile:
kind: intrinsic
action: morphir.pipeline.compile
inputs:
- src/**/*.elm
outputs:
- .morphir/morphir-ir.json
params:
optimize: true
check-generated:
kind: command
cmd:
- git
- diff
- --exit-code
- generated
depends_on:
- compile
env:
CI: "true"

workflows:
verify:
description: Compile the model and check generated files
stages:
- name: build
targets:
- compile
- name: check
targets:
- check-generated
parallel: false

bindings:
wit:
primitives:
- external: u64
morphir: Morphir.SDK:Int:Int
bidirectional: true
priority: 100

toolchain:
morphir-elm:
enabled: true
version: "2.90.0"
working_dir: .
timeout: "5m"
acquire:
backend: path
executable: morphir-elm
tasks:
make:
exec: morphir-elm
args:
- make
fulfills:
- make
inputs:
files:
- src/**/*.elm
outputs:
ir:
path: .morphir/morphir-ir.json
type: morphir-ir

Conversion requirements

A TOML-to-YAML converter MUST preserve the parsed configuration value. Comments, key order, quoting style, and whitespace are presentation details and do not need to survive conversion.

A converter MUST report a value that the target serialization cannot represent without loss. It MUST NOT coerce that value silently. Every field defined by the shared configuration schema has a direct representation in both formats.

Implementation checklist

A Morphir tool that adds YAML support should:

  1. Discover morphir.yaml at the supported source locations.
  2. Detect sibling-file and alternate-project-path ambiguity before loading configuration.
  3. Parse the restricted YAML 1.2 profile into the shared configuration model.
  4. Validate that value with morphir-config-v1.
  5. Apply the serialization-independent merge rules.
  6. Include the source path and YAML location in parse and validation diagnostics.