Skip to content

Search

Search the docs, blog, and marketplace.

How it works

Plugins

Install, enable, verify, and build external detectors, matchers, auditors, and analyzers.

Last updated October 25, 2026View source (v0.30.0)

Plugins add detectors, matchers, auditors, and analyzers to Bomly without changing the Bomly binary. This page is for people who install and run plugins: how to install one, decide to trust it, select it in a scan, and configure it. If you want to write a plugin, start with Plugin authoring.

A plugin can add one of four component kinds:

KindWhat it doesRuns during
DetectorTurns project files into a dependency graphEvery scan
MatcherAdds vulnerability, license, lifecycle, or other data to packages--enrich
AuditorTurns graph and package data into findings--audit
AnalyzerMarks vulnerabilities as reachable or not from your code--analyze

Official plugin repositories:

The Bomly marketplace lists every component that can take part in a scan, built in or external, filterable by kind and ecosystem. Each entry says who wrote the code: Core components ship in the binary, Bundled components are third-party tools compiled in (Syft, Grype), and External plugins are installed at runtime from their own repositories. To list a plugin there, open a submission from the marketplace page; entries are reviewed and added by hand.

Quick start

Install a plugin from a GitHub release, check it, enable it, and use it:

bomly plugins install github:bomly-dev/bomly-plugin-clearlydefined-matcher@v0.3.0
bomly plugins verify clearlydefined-license-matcher
bomly plugins enable clearlydefined-license-matcher
bomly scan --enrich --matchers +clearlydefined-license-matcher

Installed plugins are disabled until you enable them. Enabling a plugin is the trust decision: see Trust and security before you do it.

How plugins run

A plugin is a Go binary built on Bomly's public SDK. Bomly starts each enabled plugin as a separate process and talks to it over a versioned protocol. During one command, Bomly keeps one process per enabled plugin, starts it on first use, and stops it when the command ends. If the process dies, Bomly restarts it once; a second death disables the plugin for the rest of the command with a warning. Plugin processes never outlive the Bomly command.

When the command ends, Bomly closes each plugin's connection and gives it a short grace period before stopping the process, so cleanups the plugin registered with host.OnCleanup run, all plugins at once. A component compiled into Bomly gets the same lifetime: it is built under the command's context and its cleanups run when the command ends.

Each matcher and analyzer call receives a copy of the dependency graph and package registry, and Bomly keeps a call's result only when it succeeds. A plugin that fails or is cancelled partway leaves the scan exactly as the previous component left it. Changes a plugin makes to the graph or the target dependency are never kept: return enrichment as a registry or as package updates.

Bomly owns installing plugin packages, validating their manifest, checking recorded checksums, storing plugins under ~/.bomly/plugins, enabling and disabling them, and loading enabled plugins at the start of a command. Plugins have no install hooks or post-install scripts, and Bomly never runs a plugin it finds in a repository checkout.

Install sources

SourceCommandIntegrity check
GitHub releasebomly plugins install github:<owner>/<repo>@<tag>Verified against the release's SHA256SUMS when present. Preferred
Local archivebomly plugins install ./dist/plugin.tar.gz --checksum sha256:...The checksum ties the installed binary to the archive
Direct URLbomly plugins install https://example.com/plugin.tar.gz --checksum sha256:...The checksum ties the download to the declared identity. Always pass --checksum
Direct URL, unchecked... --insecure-skip-checksumNone. The binary may differ from the declared source
Local binarybomly plugins install ./bin/plugin --devNone. Only for binaries you built yourself

For a GitHub release, Bomly reads the release metadata, picks the asset for your operating system and architecture, and verifies it against SHA256SUMS when the release publishes one. For a private release, set a token first:

export BOMLY_GITHUB_TOKEN=<token-with-release-access>   # also accepted: GITHUB_TOKEN, GH_TOKEN, GITHUB_AUTH_TOKEN
bomly plugins install github:bomly-dev/bomly-plugin-bun-lock-detector@v0.1.0

Bomly attaches the token only to github: metadata, checksum, and asset downloads. Direct URL installs never receive it.

Plugin archives have fixed size limits so a bad download cannot fill local storage:

LimitValue
Remote download256 MiB
GitHub release metadata4 MiB
Archive entries4,096
One expanded file256 MiB
All expanded files512 MiB
Plugin manifest1 MiB
Descriptor snapshot1 MiB
Installed plugin database16 MiB

Local archives skip the download limit but keep the extraction limits. Zip archives are checked before any file is extracted; tar archives are checked entry by entry. The JSON limits apply at install time and every time Bomly loads an installed plugin.

Installed plugins live under ~/.bomly/plugins/, with installed.json and one store/<plugin-id>/<version>/ directory per installed version.

Manage plugins

TaskCommand
List built-in and external componentsbomly plugins list (filter with --external, --detectors, --matchers, --auditors, --analyzers, --json)
Show one plugin, including its config keysbomly plugins info <plugin-id>
Check manifest, checksum, binary, and descriptorbomly plugins verify <plugin-id>
Check that it starts and reports readybomly plugins test <plugin-id>
Both checksbomly plugins doctor <plugin-id>
Turn on or offbomly plugins enable <plugin-id>, bomly plugins disable <plugin-id>
Removebomly plugins uninstall <plugin-id>

Select plugins in a scan

Plugin components use the same selectors as built-in ones (see Selector grammar): a bare name replaces the default set, +name adds to it, -name removes from it.

bomly scan --detectors bomly.examples.detector.bun-lock          # only this detector
bomly scan --enrich --matchers +clearlydefined-license-matcher   # add a matcher
bomly scan --audit --auditors bomly.examples.auditor.meme-deps   # only this auditor
bomly scan --enrich --analyze --analyzers +my-reach-analyzer     # add an analyzer

A detector plugin takes part in project discovery like a built-in detector. It declares which package managers and files it handles, which directories to skip, and whether it expands workspace modules itself; see Monorepos.

Component prerequisites

A component can declare the inputs it needs before it can do useful work. Bomly checks these declarations before it calls the component. When the declarations pass, Bomly still runs the component's own readiness and applicability checks. A component without declarations goes straight to those checks.

RequirementMet when
source-tree-v1The target is a directory (--path) or a Git repository (--url), not an SBOM document or a container image
dependency-graph-v1Detection found at least one dependency. A detector cannot require it
vulnerability-records-v1At least one package carries a vulnerability record when analyzers and auditors run. This usually needs --enrich with a vulnerability matcher

What Bomly does with an unmet requirement depends on why the component is in the run:

SituationResult
You selected the component by name, and the run can never meet the requirementThe command stops with exit code 4 before any stage runs
The component runs by defaultBomly skips it and shows a warning under that stage
The requirement depends on what the scan found, such as no dependencies or no vulnerability recordsBomly skips it and shows a warning, however it was selected

"By name" means a plain selector list such as --analyzers my-reach-analyzer, or +name for a component that is off by default. A run can never meet a requirement in these cases:

  • the component requires source-tree-v1 and the target is an SBOM document or a container image;
  • a detector requires dependency-graph-v1 or vulnerability-records-v1, which only later stages produce;
  • a matcher requires vulnerability-records-v1, which only analyzers and auditors can require;
  • this version of Bomly does not know the requirement.

A component selected by name in one of these cases stops the command. A component that runs by default is skipped with a warning instead.

The error names the component, the requirement, and what would meet it. This run selects an analyzer that needs a source tree while it reads an SBOM file:

bomly scan --sbom --path ./bom.cdx.json --enrich --analyze --analyzers my-reach-analyzer

Error: selected components cannot run: analyzer my-reach-analyzer requires source-tree-v1: the target is an SBOM document, not a source tree; scan a directory with --path or a Git repository with --url

bomly plugins info <plugin-id> lists the requirements a component declares. Requirements name kinds of data, never other plugins, and Bomly never changes the order of the stages to meet one.

Configure a plugin

Plugin configuration is grouped by component kind under plugins.<kind>.<name>:

plugins:
  matchers:
    clearlydefined-license-matcher:
      api_base: https://api.clearlydefined.io
  analyzers:
    my-reach-analyzer:
      max_depth: 10

bomly plugins info <plugin-id> prints the keys, descriptions, and defaults a plugin declares. The older flat form plugins.<plugin-id>: still works but is deprecated and prints a warning; the kind-scoped block wins when both exist.

Bomly passes its proxy settings to every plugin, so a plugin that makes HTTP calls follows the same network.proxy and network.ca_cert_file configuration as Bomly itself. See Network and privacy and the Config reference.

Trust and security

An enabled plugin runs as a normal process with your user's privileges. It can read and write files, open network connections, start child processes, and read environment variables. Bomly does not sandbox it. The plugin protocol limits what Bomly accepts as a result, but it cannot limit what the process does on the host.

Before it runs a plugin, Bomly checks:

  • the manifest has the required fields (ID, version, kind, runtime, API version);
  • the plugin's API version is compatible with the running Bomly;
  • the entrypoint binary exists at the recorded path;
  • the SHA-256 checksum matches the installed record, when one was recorded;
  • the descriptor the binary returns matches the manifest identity, kind, API version, and the snapshot taken at install time.

Bomly cannot restrict a plugin's file or network access, stop it reading credentials from the environment, stop it starting child processes, or guarantee that an unchecked binary matches its declared source.

Declared access

A plugin's manifest can include an access block: the publisher's own statement of the files, network destinations, environment variables, and commands the plugin uses. Bomly shows it when you install the plugin, and in bomly plugins info, verify, and doctor. Plugins install disabled, so read it at install or with bomly plugins info before you enable the plugin. bomly plugins enable shows it again, but it does not ask for confirmation. The --json forms of list, info, verify, and doctor carry it as access, with accessDisclosure set to declared or unknown.

This is how bomly plugins enable shows a declared block:

Declared access (the publisher's claim; Bomly does not verify or enforce it):
  Filesystem read  : cache, project
  Filesystem write : cache
  Network          : api.osv.dev
  Environment      : none declared
  Subprocesses     : none declared
enabled acme.matcher.example@1.0.0

Read the block as a description, not a limit:

  • Bomly does not check that the plugin behaves as declared, and it does not stop the plugin from doing more. A checksum proves the file is the one published, not that its behavior is safe.
  • A manifest without an access block shows Declared access: unknown. Treat that plugin as able to use everything your user account can.
  • An access block with every list empty is the publisher's claim that the plugin needs none of that access. An omitted list means the same as an empty one, and Bomly shows it as none declared.
  • Bomly shows environment variable names only, never their values.
  • Bomly refuses to install a manifest whose access block is malformed or has an unknown key. The install error names the problem.

Recommended practice:

  • Prefer github:owner/repo@tag installs from releases that publish SHA256SUMS.
  • Always pass --checksum for direct URL installs.
  • Run bomly plugins verify <id> before you enable a plugin from an external source, and compare its declared access with the plugin's own documentation.
  • Do not enable plugins you did not build or obtain from a source you control.

Security and trust boundaries describes the wider trust model.

Compatibility

Plugin compatibility has two independent parts:

  • The Go API a plugin compiles against follows the SDK module's semantic version. Upgrading the SDK pin may need code changes when you rebuild the plugin.
  • The wire protocol between a built plugin and a Bomly binary is versioned separately (bomly.plugin.v1). A plugin built against an older SDK keeps working with newer Bomly binaries as long as both speak the same protocol major version.

Within protocol v1 every change is additive: new fields and calls are optional, nothing is removed or renamed, and features that need both sides are negotiated through advertised capabilities. A change that cannot be made additively would ship as a new protocol version negotiated alongside v1, and existing v1 plugins would keep running. Details for authors are in Plugin authoring.

See also