structlint uses YAML or JSON configuration files to define validation rules.
By default, structlint looks for:
--configflag valueSTRUCTLINT_CONFIGenvironment variable.structlint.yamlin current directory.structlint.ymlin current directory.structlint.jsonin current directory
dir_structure:
allowedPaths: [] # Glob patterns for allowed directories
disallowedPaths: [] # Glob patterns for disallowed directories
requiredPaths: [] # Directories that must exist
file_naming_pattern:
allowed: [] # Glob patterns for allowed files
disallowed: [] # Glob patterns for disallowed files
required: [] # Files that must exist (supports globs)
placement: [] # Files that must live under specific directories
requiredGroups: [] # One-of and per-directory required files
boundaries: [] # Go import boundary rules
ignore: [] # Paths to skip during validationConfiguration loading is strict. Unknown keys such as allowed_paths fail before validation starts, which helps catch CI drift caused by typos.
structlint supports standard glob patterns:
| Pattern | Description |
|---|---|
* |
Matches any sequence of characters (not including /) |
** |
Matches any sequence including / (recursive) |
? |
Matches any single character |
[abc] |
Matches any character in the set |
[!abc] |
Matches any character not in the set |
allowedPaths:
- "." # Root directory only
- "cmd/**" # cmd/ and all subdirectories
- "src/*.go" # Go files directly in src/
- "test/*" # Direct children of test/Directories that are permitted. If specified, any directory not matching these patterns is a violation.
dir_structure:
allowedPaths:
- "."
- "cmd/**"
- "internal/**"
- "pkg/**"
- "test/**"Directories that are explicitly forbidden.
dir_structure:
disallowedPaths:
- "vendor/**"
- "node_modules/**"
- "tmp/**"
- ".cache/**"Directories that must exist.
dir_structure:
requiredPaths:
- "cmd"
- "internal"
- "docs"File patterns that are permitted.
file_naming_pattern:
allowed:
- "*.go"
- "*.yaml"
- "*.yml"
- "*.json"
- "*.md"
- "Makefile"
- "Dockerfile*"
- ".gitignore"File patterns that are forbidden.
file_naming_pattern:
disallowed:
- "*.env*"
- "*.log"
- "*.tmp"
- "*~"
- "*.bak"Files that must exist.
file_naming_pattern:
required:
- "go.mod"
- "README.md"
- ".gitignore"
- "*.go" # At least one Go filePaths to completely skip during validation.
ignore:
- ".git"
- "vendor"
- "node_modules"
- "bin"
- "dist"
- ".idea"
- ".vscode"Placement rules ensure files of a given kind live in the expected part of the repository.
placement:
- id: sql-in-migrations
files: ["*.sql"]
mustBeUnder: ["migrations/**"]
- id: tests-near-code
files: ["*_test.go"]
mustBeUnder: ["internal/**", "pkg/**", "test/**"]| Field | Description |
|---|---|
id |
Stable rule identifier used in JSON, SARIF, and GitHub annotations |
files |
File name or path globs to match |
mustBeUnder |
Directory globs where matching files are allowed |
severity |
Optional severity, defaults to error |
Required groups model repository contracts that are more expressive than a single required path.
requiredGroups:
- id: build-entrypoint
oneOf: ["Makefile", "Taskfile.yml", "justfile"]
- id: commands-have-main
eachDirMatching: "cmd/*"
mustContain: ["main.go"]
requireMatch: true
- id: packages-have-docs
eachDirMatching: "internal/*"
mustContainOneOf: ["README.md", "doc.go"]| Field | Description |
|---|---|
oneOf |
At least one listed path or glob must exist |
eachDirMatching |
Directory glob to apply per-directory checks to |
mustContain |
Every matching directory must contain each listed file |
mustContainOneOf |
Every matching directory must contain at least one listed file |
requireMatch |
Fail if eachDirMatching finds no directories |
Boundary rules parse imports and block unwanted dependencies between layers. They are language-aware for Go, JavaScript, TypeScript, and Python source files.
boundaries:
- id: domain-no-db
from: "internal/domain/**"
cannotImport:
- "internal/db/**"
- "internal/http/**"For Go module imports, structlint reads go.mod and converts imports like example.com/app/internal/db to internal/db before matching cannotImport. For JS/TS relative imports, paths like ../db/client are resolved relative to the importing file. For Python, dotted imports like app.db.client are normalized to app/db/client.
# .structlint.yaml - Go Project Configuration
dir_structure:
allowedPaths:
- "."
- "cmd/**"
- "internal/**"
- "pkg/**"
- "api/**"
- "web/**"
- "configs/**"
- "scripts/**"
- "test/**"
- "docs/**"
- ".github/**"
disallowedPaths:
- "vendor/**"
- "node_modules/**"
- "tmp/**"
- "temp/**"
requiredPaths:
- "cmd"
- "internal"
file_naming_pattern:
allowed:
- "*.go"
- "*.mod"
- "*.sum"
- "*.yaml"
- "*.yml"
- "*.json"
- "*.toml"
- "*.md"
- "*.txt"
- "Makefile"
- "Dockerfile*"
- ".gitignore"
- ".golangci.yml"
- "go.work"
disallowed:
- "*.env*"
- "*.log"
- "*.tmp"
- "*~"
- "*.swp"
- ".DS_Store"
required:
- "go.mod"
- "README.md"
- ".gitignore"
placement:
- id: migrations-only
files: ["*.sql"]
mustBeUnder: ["migrations/**"]
requiredGroups:
- id: build-entrypoint
oneOf: ["Makefile", "Taskfile.yml", "justfile"]
- id: commands-have-main
eachDirMatching: "cmd/*"
mustContain: ["main.go"]
boundaries:
- id: domain-no-infrastructure
from: "internal/domain/**"
cannotImport: ["internal/db/**", "internal/http/**"]
ignore:
- ".git"
- "vendor"
- "bin"
- "dist"
- ".idea"
- ".vscode"All configuration options can be overridden via environment variables:
| Variable | Description |
|---|---|
STRUCTLINT_CONFIG |
Path to configuration file |
STRUCTLINT_LOG_LEVEL |
Log level (debug, info, warn, error) |
STRUCTLINT_NO_COLOR |
Disable colored output |