DollarLint is a validation tool designed to ensure consistency and correctness in JSON (including JSONC, JSON5, JSON Lines), YAML, and TOML files by validating them against JSON Schemas.
Supports schema declarations within files or via configuration, enabling flexible validation strategies.
Integrates with CI/CD pipelines through GitHub Actions and SARIF output for code scanning.
Includes schema catalogs for automatic schema matching based on file patterns or content.
Provides verbose explanations of schema associations and validation results for improved debugging.
Audience & Benefit:
Ideal for developers, DevOps engineers, and technical teams working with structured configuration files. DollarLint helps maintain consistency across projects by identifying schema violations early in the development cycle. It is particularly beneficial for teams managing complex configurations or adhering to strict schema standards.
Install via winget: winget install dollarlint/dollarlint.
README
DollarLint
DollarLint validates JSON (including JSONC, JSON5, JSON Lines), YAML, and TOML files against JSON Schemas.
Install
With Homebrew:
brew install --cask dollarlint/tap/dollarlint
With npm:
npm install -g dollarlint
With Go:
go install github.com/dollarlint/dollarlint/cmd/dollarlint@latest
To build from a local checkout:
go build -o bin/dollarlint ./cmd/dollarlint
./bin/dollarlint validate .
GitHub Action
Run DollarLint in GitHub Actions by pinning the action to the release tag you
want:
dollarlint init creates a starter .dollarlint.toml in the current directory. It is safe by default and will not overwrite an existing file unless you confirm or pass --force.
dollarlint inspect lists every discovered file, the schema DollarLint would associate with it, and the reason for that association. Files without a schema are shown as schema: none; catalog decisions include the same explainable schemaMatch metadata and suggested config snippets as validation output.
Exit codes:
0: no non-ignored issues
1: validation, schema loading, coverage, or parsing issues were found
2: CLI/configuration error
Schema declarations
Supported in-file conventions:
JSON: root $schema, for example {"$schema":"./schema.json"}
JSONC/JSON5: root $schema, using the same convention after comments and JSON5 syntax are normalized
JSON Lines files (.jsonl and .ndjson) are validated one non-empty line at a time. Because the file has no single root object, associate a schema through config, the --schema flag, or a catalog match.
Config-level schema associations can validate files that do not declare a schema themselves.
Configuration
DollarLint configuration is TOML only. For each run, the CLI looks for one .dollarlint.toml in the target root, or beside an explicitly passed file. By default, that one config applies to the whole run. Set configs.mode = "nearest" in the root config to let nested .dollarlint.toml files apply to their own subtrees. A nested config inherits only when it declares extends.
Output format and artifact location are run-time options, not persistent config. Use --format text|json|sarif and --output on dollarlint validate when you need machine-readable output.
Discovery defaults
If discovery.include is unset, DollarLint discovers JSON, JSONC, JSON5, JSON Lines (.jsonl and .ndjson), YAML, YML, and TOML files at any depth.
Set discovery.include only when you want to replace the default file set.
Set discovery.include = [] only when you intentionally want to discover no files from config.
A glob without a slash matches basenames at any depth (*.json matches package.json and config/settings.json).
discovery.exclude adds project-specific excludes on top of defaults.
respectGitIgnore = true applies .gitignore patterns during directory discovery, including nested .gitignore files as the walk descends.
forceExclude = true also applies excludes to explicitly passed files.
Config-authored discovery, association, and ignore globs are relative to the directory containing .dollarlint.toml. CLI globs such as --include, --exclude, and --schema are relative to the validation root. Relative schema and local catalog paths in config are also resolved from the config file's directory.
Nested configs and extends
Use extends when a config should inherit another config:
extends = "../../.dollarlint.toml"
Use nearest mode in the root config when a parent-directory run should apply nested configs:
[configs]
mode = "nearest"
In nearest mode, each file uses the closest .dollarlint.toml at or above its directory. A nested config does not implicitly inherit parent settings; add extends when you want shared defaults. Passing --config uses that explicit config for the whole run and suppresses nested config discovery.
JSON parsing
parsing.json.mode = "auto" is the default. .json files are parsed as strict JSON first, then retried as JSONC when strict parsing fails, so comments and trailing commas work in projects whose existing tools accept them.
Use "strict" to parse every .json file as standard JSON.
Use "jsonc" to allow JSONC syntax in every .json file.
Files ending in .jsonc always use JSONC parsing.
Remote schema fetching
Remote http(s) schema fetching is enabled by default, and successful schemas/catalogs are cached on disk.
Set schemas.fetch.cache = false or pass --no-schema-cache to disable caching.
Transient network failures (408, 425, 429, retryable 5xx) are retried with bounded backoff.
schemas.fetch.blockedDomains denies hosts even if they otherwise match the allowlist.
Leave allowedDomains empty to allow any remote schema host.
For SchemaStore, prefer *.schemastore.org or include both www.schemastore.org and json.schemastore.org.
For RubySchema, include www.rubyschema.org.
Catalog matching and coverage
When schemas.catalogs.enabled = true, files without explicit schemas can match by filename using the built-in SchemaStore and RubySchema catalog sources, a local SchemaStore-shaped catalog, or additional sources. The default schemas.catalogs.match = "auto" skips low-confidence generic bare filename matches such as tasks.json; use "all" when you want every catalog filename match applied.
When the built-in SchemaStore source is enabled, DollarLint layers on a small set of curated filename associations for known catalog gaps and drift. Today that includes Rust's rustfmt.toml / .rustfmt.toml, release-plz.toml / .release-plz.toml, and .NET's launchSettings.json / Properties/launchSettings.json.
The built-in RubySchema source covers common Ruby and Rails project configs such as RuboCop, Standard, Rails config/database.yml, Sidekiq, Shoryuken, Packwerk, i18n, Mongoid, Kamal, and related monitoring configs. Ambiguous Ruby/Rails filenames require nearby project evidence, such as config/application.rb, bin/rails, Gemfile, Gemfile.lock, .ruby-version, or Packwerk markers.
For important config files that DollarLint recognizes but cannot currently validate from a built-in or catalog schema, JSON output includes schemaGap context and skipped-file text explains the known gap. Examples include netlify.toml, .cargo/config.toml, .terraform-docs.yml, and .asf.yaml.
Catalog matches are explainable in JSON output as schemaMatch and in text hints for catalog-sourced issues. DollarLint reports the catalog fileMatch pattern, confidence, why it matched or skipped, and a suggested config rule. If a catalog match is correct, add the suggested [[schemas.associations]] entry to make it explicit. If a file should never be inferred from catalogs, add:
[[schemas.catalogs.ignore]]
file = "data/tasks.json"
reason = "application data, not a tool config"
Precedence is:
in-file schema declaration
config association
DollarLint's built-in .dollarlint.toml association
catalog match
skipped
Set schemas.requireCoverage = true to fail the run when any discovered included file is not covered by one of those sources.
DollarLint also validates discovered .dollarlint.toml files against its embedded config schema. You can override that with an in-file schema declaration or a config association for .dollarlint.toml.
Catalog failures are separate from validation issues. With schemas.catalogs.failure = "warn" (default), DollarLint records a warning, skips catalog inference or catalog-inferred validation, still validates explicit/configured schemas, and exits 0 unless validation issues exist. Use "error" to fail with exit 2, or "skip" for a silent fallback. Files with explicit in-file schemas still report schema load or compile failures as issues.
Known JSON Schema metaschemas are handled by the validator and are not pre-fetched as ordinary schema dependencies.
Azure optimization
Azure Resource Manager deployment schemas from schema.management.azure.com are pruned to the resource provider schemas used by the template before compilation. This avoids compiling the full Azure provider catalog for typical ARM templates.
Set schemas.optimizations.azure.pruneResources = false to disable Azure pruning.
Set schemas.optimizations.enabled = false to disable all schema optimizations.
Examples
The examples/ directory includes a small local schema demo, a nested-config demo, a examples/schemastore/ suite that validates common config files against remote schemas from https://www.schemastore.org, and Azure ARM deployment templates that exercise remote schema fetching plus ARM resource pruning.
dollarlint found 2 validation issues in 1 file after 47ms
settings.json
/name type expected string, received number
/count minimum must be >= 1
Summary: 4 discovered, 3 validated, 1 skipped, 2 validation issues in 47ms
Use --locations to opt into line/column source mapping for text output. JSON and SARIF request source mapping automatically.
settings.json
3:11 type expected string, received number /name
4:12 minimum must be >= 1 /count
Use --verbose to show schema URI and keyword metadata under each issue. Use --quiet for terse success output.
Use --show-skipped to show skipped files grouped by reason, class, and coverage signal. Large low-signal groups such as fixtures, lockfiles, and data files are summarized in text output with a count; --format json keeps every skipped file.
Set output.branchErrors = "all" when you need every failed oneOf/anyOf branch leaf for schema debugging; the default "best" reports the closest matching branch.
Issue hints are enabled by default with output.issueHints = "auto". Set "off" to show raw issues without curated hints, or "verbose" to include hint rule IDs, confidence, and source links in text output.
JSON output (--format json) includes a top-level $schema declaration, formatVersion, relative path fields, root-relative local schemas, active findings in issues, ignored findings in ignoredIssues, always-present arrays, per-issue category, structured issueHint metadata when a hint applies, structured warnings, skip classification fields on skipped files, and numeric summary.durationNanos. External catalog and remote schema compile failures are reported as grouped schema-source warnings with affected files marked skipped. The output schema is published in schemas/dollarlint-result.schema.json.
Use --format bundle when you need one artifact that captures every renderer from the same validation run. Bundle output is JSON with json, sarif, and styled keys; styled includes the normal terminal text as both ANSI and plain strings, plus the text-output options used to render it. If skipped-file text was summarized, styled.truncated is true while the json payload still contains full detail.
Text output uses subtle terminal styling when color is available and stays plain for machine-readable formats such as --format json, --format sarif, and --format bundle.
SARIF
Use --format sarif to emit SARIF 2.1.0 for GitHub code scanning and similar tools. Use --output to write the SARIF artifact directly:
DollarLint builds source-location maps for JSON and SARIF runs, and for text output when --locations is requested. Source locations are best-effort:
JSON-family positions are derived from a token walk over the source.
YAML positions come from yaml.Node line/column metadata.
TOML positions come from a conservative line scanner for common keys, tables, arrays, and inline tables.
When a validation issue points to something missing, such as a required property, SARIF falls back to the nearest parent object location. If source mapping fails for any reason, validation still succeeds and SARIF falls back to file-level results.
Development
go test ./...
go test -coverprofile=coverage.out ./internal/engine
go tool cover -func=coverage.out
Most implementation lives in internal/engine, with CLI wiring in internal/cli, so future integrations such as serve, LSP, and MCP can share the same validation engine without expanding the public Go API accidentally.