Configuration: .boundary.toml
Boundary is configured via a .boundary.toml file. Run boundary init to generate a starter config.
Config Discovery
Boundary searches for .boundary.toml starting from the analysis target directory and walking up parent directories (similar to how Git finds .git). The first config file found is used. If no config is found, built-in defaults are used.
This means you can place .boundary.toml at the repository root and analyze any subdirectory — the config will be discovered automatically.
Full Reference
[project]
languages = ["go"]
exclude_patterns = ["vendor/**", "**/*_test.go", "**/testdata/**"]
# services_pattern = "services/*" # For monorepo per-service analysis
[layers]
# Glob patterns to classify files into architectural layers.
domain = ["**/domain/**", "**/entity/**", "**/model/**"]
application = ["**/application/**", "**/usecase/**", "**/service/**"]
infrastructure = ["**/infrastructure/**", "**/adapter/**", "**/repository/**", "**/persistence/**"]
presentation = ["**/presentation/**", "**/handler/**", "**/api/**", "**/cmd/**"]
# Paths exempt from layer violation checks (cross-cutting concerns)
# cross_cutting = ["common/utils/**", "pkg/logger/**", "pkg/errors/**"]
# Global architecture mode: "ddd" (default), "active-record", or "service-oriented"
# architecture_mode = "ddd"
[scoring]
# Weights for score components (should sum to 1.0)
layer_isolation_weight = 0.4
dependency_direction_weight = 0.4
interface_coverage_weight = 0.2
[rules]
# Minimum severity to cause failure: "error", "warning", or "info"
fail_on = "error"
# min_score = 70.0 # Optional minimum architecture score
# detect_init_functions = true # Detect Go init() side effects
[rules.severities]
layer_boundary = "error"
circular_dependency = "error"
missing_port = "warning"
init_coupling = "warning"
Sections
[project]
| Key | Type | Default | Description |
|---|---|---|---|
languages | list | [] (auto-detect) | Languages to analyze. Options: go, rust, typescript, java |
exclude_patterns | list | ["vendor/**", "**/*_test.go", "**/testdata/**"] | Glob patterns for files to skip |
services_pattern | string | (none) | Glob for service directories in monorepos (e.g., "services/*") |
[layers]
Each layer accepts a list of glob patterns. Files matching a pattern are classified into that layer.
| Key | Default Patterns |
|---|---|
domain | **/domain/**, **/entity/**, **/model/** |
application | **/application/**, **/usecase/**, **/service/** |
infrastructure | **/infrastructure/**, **/adapter/**, **/repository/**, **/persistence/** |
presentation | **/presentation/**, **/handler/**, **/api/**, **/cmd/** |
Additional fields:
| Key | Type | Description |
|---|---|---|
cross_cutting | list | Paths exempt from layer violation checks (applies to both source files and import targets) |
architecture_mode | string | Global mode: "ddd", "active-record", or "service-oriented" |
[[layers.overrides]]
Per-module overrides for layer classification. The first matching scope wins.
[[layers.overrides]]
scope = "services/auth/**"
domain = ["services/auth/core/**"]
infrastructure = ["services/auth/server/**", "services/auth/adapters/**"]
# architecture_mode = "active-record" # Optional per-module mode
Omitted layers fall back to the global patterns.
[scoring]
| Key | Default | Description |
|---|---|---|
layer_isolation_weight | 0.4 | Weight for layer isolation score |
dependency_direction_weight | 0.4 | Weight for dependency direction score |
interface_coverage_weight | 0.2 | Weight for interface coverage score |
Weights should sum to 1.0.
[rules]
| Key | Type | Default | Description |
|---|---|---|---|
fail_on | string | "error" | Minimum severity to cause non-zero exit |
min_score | float | (none) | Optional minimum overall score |
detect_init_functions | bool | true | Detect Go init() side-effect coupling |
[rules.severities]
Override the default severity for built-in violation types. Both category names and rule IDs are accepted as keys. Rule IDs take precedence over category names.
Category Names
| Category Name | Default Severity | Description |
|---|---|---|
layer_boundary | error | Inner layer depends on outer layer |
circular_dependency | error | Circular dependency between components |
missing_port | warning | Adapter without a corresponding port interface |
constructor_concrete | warning | Constructor returns concrete type instead of port |
init_coupling | warning | Go init() function creates hidden coupling |
domain_infra_leak | error | Domain references infrastructure types |
Rule IDs
You can also use specific rule IDs (e.g., L001, PA001) for more granular control:
[rules.severities]
missing_port = "warning" # Category-wide default
PA001 = "info" # Override just missing-port-interface to info
See Rules & Rule IDs for the full rule catalog.
[[rules.ignore]]
Suppress specific rules for files matching glob patterns:
[[rules.ignore]]
rule = "PA001"
paths = ["infrastructure/**/*document.go"]
[[rules.ignore]]
rule = "L005"
paths = ["legacy/**"]
| Key | Type | Description |
|---|---|---|
rule | string | Rule ID to suppress (e.g., PA001, L001) |
paths | list | Glob patterns — violation is suppressed if the file matches any pattern |
Custom Rules
Define custom dependency rules:
[[rules.custom_rules]]
name = "no-http-in-domain"
from_pattern = "**/domain/**"
to_pattern = "**/net/http**"
action = "deny"
severity = "error"
message = "Domain layer must not import HTTP packages"
| Key | Description |
|---|---|
name | Rule identifier |
from_pattern | Glob for the source of the dependency |
to_pattern | Glob for the target of the dependency |
action | "deny" (only option currently) |
severity | "error", "warning", or "info" |
message | Custom violation message |