Skip to content

Components, offline and mirrors

poly’s engines ship as signed, downloadable components: bundles of engines that a core binary fetches the first time a run needs them. The engine remains the unit you configure; a component only decides where an engine’s code lives.

Flavor How it is built What it does
bundled Default features (all-engines and bundled-mcp) Every engine family and the MCP server are linked in. Nothing is downloaded.
core cargo build --no-default-features Config, discovery, cache, runner, hooks and the tree-sitter tier only. The engines are components. 7.6 MiB at the size profile (fat LTO, opt-level = "z").

The release archives are bundled builds. All but the musl one also embed the release’s component lock; the musl build is fully bundled and embeds no lock, so it downloads nothing. A core build is produced from source. poly doctor has a components section with the flavor, the cache root and what the current directory needs.

On a bundled build poly components install only prints a note: there is nothing to fetch.

Component Engines
web JavaScript, TypeScript, JSON (oxc), YAML, TOML, Markdown, CSS (malva), HTML and the other markup languages, adrlint
python ruff, and ty for type checking
sql sqruff
php mago
biome biome CSS and GraphQL lint
ruby rubyfmt
nix alejandra
astgrep ast-grep and the built-in rule pack
misc dockerfile, dotenv, hcl, graphql
typos typos (opt-in, see below)
mcp the MCP server, a frontend component

poly components list prints the authoritative list from the index compiled into the binary. Presets name groups: all (every engine component except typos and mcp), and language names such as python, ts, markdown or css.

A run needs the components for the languages it discovers and for the [engines.*] tables you configured. Tune that with [components]:

[components]
enable = "auto" # or ["python", "web"]
disable = ["ruby"]
install = "auto" # "auto" | "never"
on_missing = "fail" # "fail" | "skip"
Key Effect
enable "auto" derives components from the discovered files and configured engines. A list names components or presets explicitly.
disable Components never to use, even when auto would select them. Configuring an engine whose component is disabled is a config error.
install "auto" downloads a missing component when a run needs it. "never" forbids downloads.
on_missing "fail" exits 2 when a needed component is unavailable. "skip" skips the files it would have handled and charges them to the skip budget (--deny-skips, --max-skips).

--on-missing fail|skip overrides on_missing for one run. poly never falls back to the tree-sitter tier for a language whose component is missing: output would differ between machines.

typos is its own component and is off unless you turn it on:

[engines.typos]
enabled = true

Only these commands may use the network for components:

  • a poly lint or poly fmt run that discovers files for a missing component (with install = "auto");
  • poly components install and poly components sync;
  • poly components import, from a file you supply (no network).

poly components list, status, config validate, config show and doctor never download. Components are fetched before a run is planned, never while files are being checked. Every download is verified against the lock’s checksum before it is loaded.

Terminal window
poly components status . # what this directory needs, and whether it is usable
poly components status . --verify # also re-check installed archives against the lock
poly components sync . # install everything this directory needs
poly components install python web # or: install all
poly components uninstall ruby

--offline (or POLY_OFFLINE=1) stops every network access: components, tree-sitter grammars, catalog-tool installs, remote extends and hook sources. Anything that would need the network is a skip or an error according to on_missing, never an attempt.

Terminal window
poly --offline components sync . # a check only: exits 2 if something is missing
poly --offline lint .

To seed a host with no network access, download the component archives from the release on a connected machine and import them:

Terminal window
poly components import <archive>

The archive is verified against the lock the binary carries before it is installed.

Variable Effect
POLY_COMPONENT_MIRROR Base URL that replaces the origin (scheme://host[:port]) of each component download. The original path is kept, so mirror the release’s directory layout. Integrity never depends on the URL, because the lock pins each archive’s SHA-256.
POLY_COMPONENT_CACHE Component cache root. Default: components/ inside poly’s cache home (POLY_CACHE_HOME, else the per-user cache directory).
POLY_OFFLINE Same as --offline.
Terminal window
POLY_COMPONENT_MIRROR=https://artifacts.example.com/poly poly components sync .

poly components prune removes cached archives the lock no longer references:

Terminal window
poly components prune --dry-run # list exactly what a real prune removes
poly components prune --keep-days 7 # keep unreferenced archives used in the last 7 days
poly components prune --all # remove every archive

By default it keeps archives referenced by the lock for any target, and archives used within the last 30 days. It also clears staging directories that an interrupted install left behind, and reports a file that a loaded library still holds (Windows) as skipped for a later run.

The GitHub Action installs components before the first run and caches them by version and lock, so a second run downloads nothing:

- uses: Goldziher/poly@v0
with:
version: from-config # the version required_version in poly.toml names
components: auto # or all, none, or "sql,php"
- run: poly lint .

Pin required_version in poly.toml to fix the poly version, and with it every component: components are versioned in lock-step with poly, so there is no separate component lock to commit. See ACTION_USAGE.md for the inputs.