Skip to main content

Morphir Configuration Guide

This guide explains how to configure the Morphir CLI and tooling.

Quick Start

Initialize a new workspace:

morphir workspace init

This creates a morphir.toml file and .morphir/ directory. Edit morphir.toml to customize settings.

Configuration Files

Morphir loads configuration from multiple sources, merged in priority order:

PrioritySourcePathPurpose
0 (lowest)Built-in defaults(compiled in)Sensible defaults
100System config/etc/morphir/morphir.toml (%PROGRAMDATA%\morphir\morphir.toml on Windows)System-wide settings
200Global user configPlatform config directory or user-home .morphir directoryUser preferences
300Project or workspace primaryRoot, hidden, or dot-config primary layoutShared project settings
350Selected workspace-member primaryRoot, hidden, or dot-config primary layout in the memberMember settings
400User overrideAdjacent to a selected project, workspace, or member primaryLocal overrides
600 (highest)Environment variablesMORPHIR_*Runtime overrides

Higher-priority sources override lower-priority ones for the same setting. Each configuration location accepts its TOML and YAML pair. If both exist, Morphir reports an ambiguity error instead of choosing one. See the merge rules for the full algorithm.

File Locations

Project Configuration

Place morphir.toml or morphir.yaml in your project root:

my-project/
├── morphir.toml # Primary configuration (morphir.yaml is also supported)
├── morphir.user.toml # Adjacent personal override
└── src/

Or use the hidden style with morphir workspace init --hidden:

my-project/
├── .morphir/
│ ├── morphir.toml # Primary configuration (morphir.yaml is also supported)
│ └── morphir.user.toml # Adjacent personal override
└── src/

The dot-config layout is also supported:

my-project/
├── .config/
│ └── morphir/
│ ├── config.toml # Primary configuration (config.yaml is also supported)
│ └── config.user.toml # Adjacent personal override
└── src/

Choose one primary file from all six TOML and YAML candidates. Morphir reports an ambiguity error if more than one exists. Its adjacent override uses the matching base name: morphir.user.* beside morphir.*, or config.user.* beside config.*.

Global User Configuration

Create one global user file for settings that apply to all projects.

On Linux and other XDG systems, Morphir checks:

  • $XDG_CONFIG_HOME/morphir/morphir.toml or morphir.yaml when XDG_CONFIG_HOME is an absolute path
  • $HOME/.config/morphir/morphir.toml or morphir.yaml when XDG_CONFIG_HOME is unset, empty, or relative
  • $HOME/.morphir/morphir.toml or morphir.yaml

On macOS, Morphir checks a valid $XDG_CONFIG_HOME first. Without it, the standard location is $HOME/Library/Application Support/morphir/morphir.toml or morphir.yaml. The $HOME/.morphir alternative also applies.

On Windows, Morphir checks:

  • %APPDATA%\morphir\morphir.toml or morphir.yaml, resolved through the Windows FOLDERID_RoamingAppData known folder
  • %USERPROFILE%\.morphir\morphir.toml or morphir.yaml, resolved through FOLDERID_Profile

The paths are alternatives at the same precedence. If more than one candidate exists, Morphir reports an ambiguity error.

When the MORPHIR_HOME environment variable is set, the user-home candidate on every platform becomes $MORPHIR_HOME/morphir.toml or morphir.yaml instead of the .morphir directory under the user home. The platform config-directory candidates are unaffected.

TOML example:

[ui]
theme = "dark"

[ir]
format_version = 4

YAML example:

ui:
theme: dark

ir:
format_version: 4

System Configuration

Administrators can create /etc/morphir/morphir.toml (or morphir.yaml) for organization-wide defaults. On Windows the system location is %PROGRAMDATA%\morphir\morphir.toml, which falls back to C:\ProgramData\morphir\morphir.toml when PROGRAMDATA is not set.

Configuration Sections

[morphir]

Core Morphir settings:

[morphir]
# Morphir IR version constraint (semver syntax)
version = "^3.0.0"

[workspace]

Workspace paths:

[workspace]
# Workspace root (usually left empty)
root = ""

# Output directory for generated artifacts
output_dir = ".morphir"

[ir]

IR processing settings:

[ir]
# IR format version (1-10)
format_version = 3

# Enable strict validation
strict_mode = false

[codegen]

Code generation settings:

[codegen]
# Target languages
targets = ["go", "typescript"]

# Custom template directory
template_dir = ""

# Output format: pretty, compact, minified
output_format = "pretty"

[cache]

Caching settings:

[cache]
# Enable caching
enabled = true

# Cache directory (empty = default)
dir = ""

# Max cache size in bytes (0 = unlimited)
max_size = 0

[logging]

CLI startup logging is currently configured through environment variables, not through discovered morphir.toml or morphir.yaml files. Use:

export MORPHIR_LOGGING__LEVEL=info
export MORPHIR_LOGGING__FILE_LEVEL=debug
export MORPHIR_LOG_FILE=true
export MORPHIR_LOG_DIR=/path/to/logs

MORPHIR_LOG_LEVEL and MORPHIR_LOG_FILE_LEVEL remain compatibility aliases for the two canonical level variables.

Configuration-file support for startup logging remains planned. Until it is implemented, a [logging] table may be inspected by morphir config, but it does not control the active logger.

[ui]

UI settings:

[ui]
# Enable colored output
color = true

# Enable interactive mode
interactive = true

# Theme: default, light, dark
theme = "default"

Environment Variables

Override any setting with environment variables using the MORPHIR_ prefix. A double underscore (__) separates nesting levels; single underscores stay part of the key name:

# Override logging level
export MORPHIR_LOGGING__LEVEL=debug

# Disable caching
export MORPHIR_CACHE__ENABLED=false

# Set IR format version
export MORPHIR_IR__FORMAT_VERSION=3

# Disable colors
export MORPHIR_UI__COLOR=false

Mapping examples:

  • logging.levelMORPHIR_LOGGING__LEVEL
  • ir.format_versionMORPHIR_IR__FORMAT_VERSION
  • codegen.go.packageMORPHIR_CODEGEN__GO__PACKAGE

Values are typed mechanically: true and false become booleans, integers become numbers, values that start with [ or { and parse as JSON become arrays or objects, and anything else stays a string. Key segments are lower-cased.

Special Environment Variables

Some environment variables control Morphir directly rather than overriding a configuration key:

VariablePurposeDefault
MORPHIR_HOMERelocates the Morphir home directory, which holds user-global state such as the tool, distribution, and extension registries and fallback log output; when set, caches also relocate under $MORPHIR_HOME/cache$HOME/.morphir on Linux/macOS, %USERPROFILE%\.morphir on Windows
MORPHIR_LOG_DIROverrides the CLI log output directory$MORPHIR_HOME/logs/cli
MORPHIR_LOG_FILEEnables or disables local CLI file loggingtrue when Morphir Home resolves; otherwise console-only

Relocating the home directory is useful for testing, CI, or sandboxed environments where the real user home is unavailable or should stay untouched:

export MORPHIR_HOME=/tmp/morphir-test-home

An empty value is treated as unset.

CLI Commands

View Configuration

Show the resolved configuration:

# Human-readable format
morphir config show

# JSON format (for scripting)
morphir config show --json

See Secrets for how credentials are displayed.

Show Configuration Sources

See which files were loaded:

# Human-readable format
morphir config path

# JSON format
morphir config path --json

Example output:

Configuration sources (in priority order):

[✓] project
Path: /home/user/my-project/morphir.toml
Status: loaded
Priority: 300

[✗] global
Path: /home/user/.config/morphir/morphir.toml
Status: not found
Priority: 200

Initialize Workspace

Create a new workspace:

# In current directory
morphir workspace init

# In specific directory
morphir workspace init /path/to/project

# With hidden config style
morphir workspace init --hidden

# With custom project name
morphir workspace init --name my-project

# JSON output (for scripting)
morphir workspace init --json

User Overrides

A user override is personal configuration that should not be committed to version control. It is adjacent to the selected primary configuration: morphir.user.toml or morphir.user.yaml for root and hidden layouts, or config.user.toml or config.user.yaml for the dot-config layout. A loader and morphir workspace init do not edit ignore files. Add the applicable adjacent *.user.* names to your repository's ignore policy yourself.

In a workspace, Morphir applies the workspace override before the selected member's override. Both use the layout of their own primary configuration, so the member override wins.

Common uses:

  • Custom cache locations
  • Personal UI preferences

Example:

# morphir.user.toml next to morphir.toml

[ui]
theme = "dark"

Secrets

Never put credentials in a committed configuration file. The configuration format specifies a secret reference for this, written instead of the secret itself:

[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" } }
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 } }

The morphir-rust API, built with Rust 1.88, resolves one requested dotted key through EffectiveConfig::resolve_secret or EffectiveConfig::resolve_secret_with. It never resolves every reference eagerly. env reads a non-empty environment value. file reads non-empty UTF-8 text, removes one line ending, and resolves a relative path from the configuration file that supplied the winning value. A relative file reference requires that declaring configuration file. ~ expands to the current user's home directory.

command runs the array's program and arguments directly, without a shell or standard input. It runs from the directory of the configuration file that supplied the winning value. If there is no declaring file, it uses the process current directory. Successful standard output must be non-empty UTF-8 text after one line ending is removed. keyring reads an existing native keyring password for its service and account. It never writes to the keyring.

The resolver returns a protected secret. Formatting redacts it, ordinary serialization is unavailable, and callers need an explicit exposure operation to read it. Loading, validation, morphir config show, and morphir config path do not resolve references. Resolution failures identify the configuration key and safe source metadata without including secret text.

How morphir config show displays values

morphir config show redacts sensitive values before printing them. Redaction is a key-name heuristic, not a check on the value's shape. Any configuration key whose name, case-insensitively and with - treated as _, contains token, password, passwd, secret, credential, api_key, apikey, private_key, or access_key has its entire value replaced with <redacted>. This applies to plain strings, secret-reference tables, and arbitrary nested values. The command does not resolve a reference.

Environment variables under a sensitive key are redacted the same way, whatever value they carry: MORPHIR_REGISTRY__TOKEN='{"env":"GH_TOKEN"}' also displays as <redacted>.

Validation

The configuration system validates values and reports errors and warnings:

  • Errors (fatal): Invalid log level, negative cache size, malformed paths
  • Warnings (non-fatal): Unknown theme, unusual IR version

Invalid configuration prevents the CLI from running. Warnings are displayed but don't block execution.

Examples

Minimal Configuration

[morphir]
version = "^3.0.0"

[codegen]
targets = ["go"]

Full Configuration

See examples/morphir.toml for a fully commented example.

CI/CD Configuration

For CI environments, use environment variables:

# GitHub Actions example
env:
MORPHIR_LOGGING__LEVEL: warn
MORPHIR_UI__COLOR: false
MORPHIR_UI__INTERACTIVE: false
MORPHIR_CACHE__DIR: /tmp/morphir-cache

Multi-Target Code Generation

[codegen]
targets = ["go", "typescript", "scala"]
output_format = "pretty"