Skip to main content

alint

crates.io CI License

A fast, general linter.

Code linters check what's inside a source file. alint checks the repository around it: files that should or shouldn't exist, naming conventions, file contents, values buried in package.json, Cargo.toml or a workflow YAML, and the relationships between files. The rules live in one .alint.yml and run on every pull request, in pre-commit and in your editor.

Fast at scale: the workspace-bundle benchmark checks 100,000 files in about 1.5 seconds and one million in under 20 seconds (measured on every release).

Every repository has a shape it is supposed to keep. Every package carries a README. Workflows pin their actions to a commit SHA. Nobody commits target/ or node_modules/. Source files that need a license header have one. These rules are real, and a repo drifts when they slip, but they usually live in a reviewer's head or a line of CONTRIBUTING.md, where nothing fails when a pull request ignores them.

alint writes those rules down and enforces them. It takes over from Repolinter (archived in 2026), runs alongside ESLint, Clippy, or Ruff, and ships as a single static Rust binary with nothing to install at runtime.

alint finding structural violations in a repo, fixing the mechanical ones, and leaving the structural ones for a human

A tracked target/, an unpinned GitHub Action, a scratch planning doc at the repo root, and whitespace drift. alint finds all of it, fixes what is mechanical, and leaves what needs a decision. No per-language linter sees any of it.

Quickstart

# Install (Linux and macOS; Windows via npm, cargo, or the release tarball):
curl -fsSL https://alint.org/install.sh | bash

# Start from a few bundled rulesets:
cat > .alint.yml <<'YAML'
version: 1
extends:
  - alint://bundled/oss-baseline@v1          # README, LICENSE, hygiene, merge markers
  - alint://bundled/rust@v1                   # auto-skips when there is no Rust
  - alint://bundled/ci/github-actions@v1      # pin actions, least-privilege workflows
YAML

alint check            # report what is wrong
alint fix --dry-run    # preview the mechanical repairs
alint fix              # apply them

alint init writes that file for you by detecting the ecosystems in your repo, so you rarely start from a blank page. The quickstart on alint.org walks a first config end to end.

Ruleset authors can put versioned fixture documents under alint-tests/ and run alint test. Each test owns an isolated repository tree, config, environment, Git history, ordered check/fix steps, inline expectations, and an optional JSON snapshot. The public alint-testkit crate exposes the same hermetic runner.

How it works

A config is a list of rules, but you rarely write them all by hand. extends: composes rulesets, and alint ships 24 of them compiled into the binary, so there is no download and no network round trip. Most are gated on what the repo actually contains, so listing the Rust ruleset in a repo with no Rust does nothing. You layer your own rules on top, and override or switch off anything you inherit.

A rule is three things: a kind (the check to run, such as file_exists, filename_case, or pair), the paths it applies to, and a severity. There are 102 rule kinds across 13 families, plus 11 aliases. A rule can also carry a when: condition (run only on the default branch, or only when some file exists) and a fix:.

# .alint.yml
version: 1
extends:
  - alint://bundled/oss-baseline@v1
rules:
  - id: no-focused-tests                 # a rule of your own, on top of the ruleset
    kind: file_content_forbidden
    paths: ["**/*.test.ts"]
    pattern: '\.only\('
    message: "Remove .only() so CI runs the whole suite, not one test"
    level: error

Under the hood, alint walks the repository once, in parallel, honoring .gitignore, and reads each file's bytes at most once. The engine, the rules, the output formatters, and a language server for editors are all one binary, with no dynamically loaded plugins (an external tool can only be spawned per file by the opt-in, top-level-only command rule kind) and nothing from Node, the JVM, or Python in the path. The concepts guide and ARCHITECTURE.md go deeper.

What it can check

The 102 rule kinds group into a handful of themes. The rule reference has every one with an example; a tour of the highlights:

  • Files and structure. Require or forbid specific files and directories; cap file size, line count, directory depth, and files per directory; reject committed build output and OS junk.
  • Naming. Filename case and regex conventions, with case-collision and reserved-name safety for cross-platform checkouts.
  • Content. Required headers and footers, forbidden patterns such as merge markers or debug residue, and the everyday hygiene: trailing whitespace, final newlines, line endings, line width.
  • Config values. Read into JSON, YAML, TOML, XML, dotenv, INI, .properties, and HCL with RFC 9535 JSONPath, then assert a value, match a pattern, forbid a key, or validate the whole file against a JSON Schema. Require package.json to declare a license, or every workflow to set permissions: contents: read.
  • Cross-file relationships. The primitives most tools lack: pair (every .proto has its generated binding), every_matching_has (every packages/* has a README and a manifest), unique_by (no two crates share a name), file_graph (no import cycles or orphaned modules), and generated_file_fresh (a checked-in generated file still matches its source).
  • Security and encoding. Immutable CI/container references across GitHub Actions, pre-commit, CircleCI, GitLab, Cloud Build, and Dockerfiles; Trojan-Source bidirectional controls, zero-width characters, byte-order marks, and text-encoding sanity.
  • Git hygiene. Commit-message shape, sign-off and GPG signing, denied paths, and blame age.
  • Metadata. Executable bits and shebangs, symlink and submodule policy, portable-filename checks.

When no kind fits, the command kind runs a tool you already trust and turns its exit code into a violation, so a one-off check still lives in the same config as everything else.

Fixing, not just finding

Plenty of violations are mechanical: a missing final newline, a stray byte-order mark, an unsorted CODEOWNERS, a license header sitting above the shebang instead of below it. alint fix repairs those in place, with 26 ops covering content hygiene, structured-config edits, file and git operations, and list ordering. The ones that need a human decision it shows you or suggests, rather than guessing.

Each of the 26 auto-fix ops carries a safety tier, so a bare alint fix never surprises you:

  • Safe fixes apply on a plain alint fix: whitespace and newline hygiene, line endings, header placement, sorting a marked block, reindentation.
  • Unsafe fixes change meaning or remove content, so they wait for alint fix --unsafe-fixes: deleting a file, a regex replace, dropping a committed artifact from git's index. (As of v0.17, removing a file is Unsafe. A plain alint fix now suggests the deletion rather than doing it.)
  • Suggestions are reported but never written, for violations with no mechanical repair.

Preview with alint fix --dry-run, or alint fix --diff for the exact edits. alint batches a file's edits in memory, writes each file once, and re-runs until the tree stops changing; a size limit (1 MiB by default) skips oversize files rather than rewriting them.

Because extends: can pull a ruleset from a URL, a fix that injects or remotely aims content (replace, file_create, file_prepend, and similar operations) from a remote or nested ruleset is demoted to a suggestion unless you list that source under trusted_extends:. The cap is applied to the effective fixer after field composition and template expansion, while a fix that shells out is refused from anywhere but your own top-level config. Every op is listed in the rule reference.

Adopt it without a flag day

You do not have to fix everything the first time alint runs. alint baseline records today's violations behind a content fingerprint, and alint check --baseline then passes on those and fails only on genuinely new ones. Turn it on as a blocking gate on day one and burn down the backlog on your own schedule, the way you would introduce a type checker to an untyped codebase.

Where it runs

  • Pull requests. The asamarts/alint GitHub Action annotates changed lines inline or uploads SARIF to Code Scanning, and alint check --changed lints only the files a PR touched.
  • Commits. A pre-commit hook (a prebuilt wheel, no toolchain) checks every commit; a manual alint-fix hook repairs on request.
  • Your editor. alint lsp is a language server (diagnostics, hover-to-explain, apply-fix code actions), with marketplace extensions for VS Code and JetBrains, a source extension for Zed, and ready configs for Neovim, Sublime Text, Emacs, and Helix.
  • Coding agents. The agent output format carries a per-violation instruction and fix command, and alint export-agents-md keeps the directives in AGENTS.md or CLAUDE.md in step with the rules alint enforces, so the agent and CI agree on the contract.
  • Any CI. 8 output formats cover most pipelines: human, json, sarif, github, markdown, junit, gitlab, and agent. Exit codes are stable: 0 clean, 1 violations, 2 config error, 3 internal.

Bundled rulesets

alint ships 24 bundled ecosystem rulesets inside the binary, pinned to the version you run and reachable as alint://bundled/<name>@v1. They group into:

  • Ecosystem baselines, each gated so it is a no-op off-ecosystem: rust, node, python, go, java, dotnet, php, plus the always-on oss-baseline (the README / LICENSE / hygiene starting point, and a clean migration target for Repolinter).
  • Cross-file consistency: versioning keeps release versions aligned across manifests, charts, API descriptions, and changelogs; toolchain-pins aligns exact Node, Rust, Python, Ruby, JVM, and .NET pins across local, Docker, and CI declarations.
  • Monorepo overlays: a monorepo base plus cargo-workspace, pnpm-workspace, and yarn-workspace overlays that scope per-member checks to real package directories.
  • CI and tooling: ci/github-actions (OpenSSF-guided workflow hardening), tooling/editorconfig, docs/adr, and the hygiene/* artifact and lockfile sets.
  • Compliance and governance: compliance/reuse, compliance/apache-2, and apache/governance (the Apache TLP release discipline that arrow, spark, and airflow each re-implement by hand).
  • Agent-aware: agent-hygiene (scratch docs, duplicate-versioned files, and debug residue that cluster in agent-authored commits) and agent-context (keeps AGENTS.md and friends honest).

Rulesets ship non-blocking by default: info or warning for recommendations, error only for unambiguous bugs. Redeclare a rule id in your own config to change its severity or scope, or set level: off. The bundled-ruleset reference lists every rule in each.

On real repositories

alint ships working configs for 30 open-source projects, from single-language libraries to polyglot monorepos, each with a writeup of what alint catches that the project's own tooling misses. Writing them surfaced a few patterns that recur:

  • Projects that lean on a convention without enforcing it. tokio has no validation scripts, yet its pipeline quietly assumes conventions alint can hold to.
  • Projects drowning in hand-rolled verify scripts. Kubernetes maintains dozens of hack/verify-*.sh; the structural ones collapse into a handful of declarative rules.
  • Projects with excellent code linters but no structural layer. ruff ships 900+ Python rules and still cannot check that its own internal crates keep publish = false.
  • Polyglot trees no single per-language linter sees across: apache/arrow spans six languages, vercel/next.js is TypeScript and Rust, NixOS/nixpkgs is tens of thousands of files.

Start from the example closest to your repo, or see how alint compares with other tools and how to migrate from Repolinter.

What alint is not

alint checks the shape and contents of a repository, not the semantics of the code inside it. It is deliberately not:

  • a code or AST linter (use ESLint, Clippy, Ruff)
  • a SAST scanner (use Semgrep, CodeQL)
  • an IaC scanner (use Checkov, Conftest)
  • a full commit-message linter: the git_commit_* rule kinds cover basic shape checks (subject length and regex, sign-off, fixup commits), but for conventional-commit grammars, scopes and body/footer rules use commitlint
  • a secret scanner (use gitleaks, TruffleHog)

It runs underneath those, and keeps its rules on the filesystem so they can keep theirs on the code.

Install

# install.sh (Linux, macOS; x86_64 and aarch64)
curl -fsSL https://alint.org/install.sh | bash

# Homebrew (macOS, Linuxbrew)
brew install asamarts/alint/alint

# crates.io
cargo install alint

# npm (also puts alint on PATH; for Windows or a project-local dev dependency)
npm install -g @asamarts/alint

# PyPI (uvx / pipx / pip; the wheel embeds the binary, so no Python in the hot path)
uvx alint check
uv tool install alint

install.sh detects your platform, downloads the matching tarball, verifies its SHA-256, and installs to ~/.local/bin. The npm and PyPI packages ship the same prebuilt binary; the PyPI wheel even installs cleanly where the npm shim cannot, including under --ignore-scripts, on offline mirrors, and on Windows. A distroless multi-arch Docker image is published to ghcr.io on every release:

docker run --rm -v "$PWD:/repo" ghcr.io/asamarts/alint:v0.20.1 check

Build from source with cargo build --release -p alint. Every channel and platform is covered in the installation guide.

alint is telemetry-free and makes no network access at runtime, except for the extends: URLs you write yourself, which must be SRI-pinned. The threat model is in SECURITY.md.

Documentation

alint.org is the narrative home. The pages worth bookmarking:

In the repo: CHANGELOG.md for the per-version history, docs/benchmarks/ for methodology and per-release results, and SECURITY.md for the threat model.

Development

git clone https://github.com/asamarts/alint
cd alint
cargo test --workspace     # Rust tests plus 578 declarative end-to-end policy tests
cargo run -- check         # dogfood: alint lints its own repo
cargo bench -p alint-bench # criterion micro-benches

alint is a ten-crate Cargo workspace. Seven crates publish to crates.io: alint (the binary), alint-core (the engine API), alint-testkit (the versioned policy-test model and hermetic runner), plus the internal implementation crates alint-dsl, alint-rules, alint-output, and alint-lsp. General policy tests live under crates/alint-e2e/scenarios/; bundled ruleset tests live beside their owners under crates/alint-dsl/ruleset-tests/v1/. CLI snapshots run through trycmd, and CI jobs use scripts under ci/scripts/.

License

Dual-licensed under either Apache License 2.0 or the MIT License, at your option. Unless you state otherwise, any contribution you submit for inclusion in alint is dual-licensed the same way, with no additional terms.

Metadata

Release files for alint 0.20.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for alint 0.20.1
File Actions
alint-0.20.1-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details for alint-0.20.1-py3-none-win_amd64.whl
alint-0.20.1-py3-none-manylinux_2_17_x86_64.musllinux_1_2_x86_64.whl Python 3 none Linux musl 1.2+ x86-64, Linux glibc 2.17+ x86-64 Details for alint-0.20.1-py3-none-manylinux_2_17_x86_64.musllinux_1_2_x86_64.whl
alint-0.20.1-py3-none-manylinux_2_17_aarch64.musllinux_1_2_aarch64.whl Python 3 none Linux musl 1.2+ ARM64, Linux glibc 2.17+ ARM64 Details for alint-0.20.1-py3-none-manylinux_2_17_aarch64.musllinux_1_2_aarch64.whl
alint-0.20.1-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details for alint-0.20.1-py3-none-macosx_11_0_arm64.whl
alint-0.20.1-py3-none-macosx_10_12_x86_64.whl Python 3 none macOS 10.12+ x86-64 Details for alint-0.20.1-py3-none-macosx_10_12_x86_64.whl

Total release size: 39.2 MB

Release files / alint-0.20.1-py3-none-win_amd64.whl

Download URL alint-0.20.1-py3-none-win_amd64.whl
Size 8.4 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
dddd6e1388ec38d9975ef10c75234ac9a9824a4c3d59b9c3332aa5b1cbe88442
BLAKE2b-256 checksum
How to use checksums
baf92e3eb1425b6f1da764ebbb85b6de6fea81fc58702ccd9e1658490039a9e9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

Release files / alint-0.20.1-py3-none-manylinux_2_17_x86_64.musllinux_1_2_x86_64.whl

Download URL alint-0.20.1-py3-none-manylinux_2_17_x86_64.musllinux_1_2_x86_64.whl
Size 8.2 MB
Tags Linux glibc 2.17+ x86-64 Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
39d87dec0138df1d96638f60fa89e66b30afba6a2c5ee29d7a87233b2c45bda7
BLAKE2b-256 checksum
How to use checksums
5ddd34e863a6082a3af3cf2e5db46d4164f0051c46f5bfe1a11c69c41ad0c969
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

Release files / alint-0.20.1-py3-none-manylinux_2_17_aarch64.musllinux_1_2_aarch64.whl

Download URL alint-0.20.1-py3-none-manylinux_2_17_aarch64.musllinux_1_2_aarch64.whl
Size 7.4 MB
Tags Linux glibc 2.17+ ARM64 Linux musl 1.2+ ARM64 Python 3
SHA-256 checksum
How to use checksums
efb56a773f789afde8f5deccf87cb022217120c0d677c768cb1f49cdef9fa5ca
BLAKE2b-256 checksum
How to use checksums
f6d8f2cbf61ca60ab13a27d0132201984b7962b501facb72567be9e1c744f0be
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

Release files / alint-0.20.1-py3-none-macosx_11_0_arm64.whl

Download URL alint-0.20.1-py3-none-macosx_11_0_arm64.whl
Size 7.3 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
a3dd68f90a233aafa5a79fdca2e1bbd7ea556b1e9b616d6b6a7e901f2f67f739
BLAKE2b-256 checksum
How to use checksums
cfa7628365c8423a525e5805d4ed3ed132e7edcfdcf56320a1b377bea2bbce8c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

Release files / alint-0.20.1-py3-none-macosx_10_12_x86_64.whl

Download URL alint-0.20.1-py3-none-macosx_10_12_x86_64.whl
Size 7.9 MB
Tags Python 3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
8bc935ea93ceb7800750b9a37bcaa010ba9ef5d9c662b22d8b0ca8cdda10de1f
BLAKE2b-256 checksum
How to use checksums
6e74a136d7c474a10a3790843bb5d7f43f5844ef872e208f360b757b62a0cc00
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

Security for alint 0.20.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 5 files in this release have been uploaded with provenance metadata.

Select from 5 files

Attestation for alint-0.20.1-py3-none-win_amd64.whl

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page