Search

Search the docs, blog, and marketplace.

How To Implement A Detector Plugin

Last updated July 30, 2026View source (v0.21.1)

A detector plugin turns project evidence into a Bomly dependency graph. Use a detector when Bomly needs a new way to read dependency data, such as a new package manager, a specialized manifest format, or an internal dependency source.

External detector plugins are served with sdk.ServeDetector.

The Bomly SDK API reference documents the sdk.ServeDetector entrypoint, sdk.ServedDetector interface, sdk.DetectionRequest, sdk.DetectionResult, graph helpers, coordinates, and package-manager support types used below.

Minimum Shape

Create a Go main package that imports the Bomly SDK:

package main

import (
    "context"

    "github.com/bomly-dev/bomly-cli/sdk"
)

const pluginID = "bomly.examples.detector.bun-lock"

type detector struct{}

func (d *detector) Descriptor(context.Context) (*sdk.DetectorDescriptor, error) {
    return &sdk.DetectorDescriptor{
        Name:        pluginID,
        DisplayName: "Bun Lock Detector",
        Aliases:     []string{"bun-lock"},
        Tags:        []string{"dependency-detection", "bun"},
    }, nil
}

func (d *detector) PackageManagerSupport(context.Context) ([]sdk.PackageManagerSupport, error) {
    return []sdk.PackageManagerSupport{
        sdk.Support(sdk.PackageManagerOther, "bun.lock", "bun.lockb", "package.json"),
    }, nil
}

func (d *detector) Ready(context.Context, *sdk.DetectRequest) (*sdk.ReadyResponse, error) {
    return &sdk.ReadyResponse{Ready: true}, nil
}

func (d *detector) Applicable(context.Context, *sdk.DetectRequest) (*sdk.ApplicableResponse, error) {
    return &sdk.ApplicableResponse{Applicable: true}, nil
}

func (d *detector) Detect(ctx context.Context, req *sdk.DetectRequest) (*sdk.DetectResponse, error) {
    graph := sdk.New()
    dep := sdk.NewDependency(sdk.Dependency{
        Coordinates: sdk.Coordinates{
            Ecosystem: sdk.EcosystemNPM,
            Name:      "is-odd",
            Version:   "3.0.1",
            PURL:      "pkg:npm/is-odd@3.0.1",
        },
        FoundBy: pluginID,
    })
    if err := graph.AddNode(dep); err != nil {
        return nil, err
    }
    return &sdk.DetectResponse{
        SubprojectInfo:      req.Subproject,
        RootExecutionTarget: req.ExecutionTarget,
        Graphs: &sdk.GraphContainer{
            Entries: []sdk.GraphEntry{{
                Manifest: sdk.ManifestMetadata{Path: "package.json", Kind: sdk.ManifestKind("package.json")},
                Graph:    graph,
            }},
        },
    }, nil
}

func main() {
    sdk.ServeDetector(&detector{})
}

The working example repo is bomly-plugin-bun-lock-detector. It demonstrates sdk.PackageManagerOther for a package manager Bomly does not model directly yet.

What Each Hook Does

  • Descriptor describes the component identity, display name, aliases, tags, support, and detector behavior.
  • PackageManagerSupport tells Bomly which package managers and evidence patterns can plan this detector.
  • Ready reports whether the plugin can run in the current environment.
  • Applicable reports whether the plugin should run for the current request.
  • Detect reads the request and returns a sdk.DetectResponse containing one or more manifest-scoped graphs.

Detector plugins may also implement:

func (d *detector) Install(context.Context, *sdk.DetectRequest) (*sdk.InstallResponse, error)

Use Install only for install-first detectors that prepare dependencies before graph resolution. Do not install package managers themselves; Bomly assumes required package managers already exist.

Add optional remediation hints

If the detector knows package-manager strategies, advertise them in its descriptor:

RemediationCapabilities: []sdk.RemediationCapability{{
    SupportedManagers: []sdk.PackageManager{sdk.PackageManagerNPM},
    Actions: []sdk.RemediationAction{
        sdk.RemediationActionDirectBump,
        sdk.RemediationActionTransitiveOverride,
    },
}},

Then optionally implement:

func (d *detector) RemediationHints(
    ctx context.Context,
    req *sdk.RemediationHintRequest,
) (*sdk.RemediationHintResponse, error)

Return hints only for dependency IDs and manifest paths produced by that detector. Hints may list supported strategies and plain-language package-manager advice. They must not choose a fix version, edit files, run commands, or make network calls. Bomly validates the hints and chooses the final action. If the capability is absent, Bomly does not call this method.

Build The Graph

Use SDK graph helpers instead of constructing graph internals by hand:

parent := sdk.NewDependency(sdk.Dependency{
    Coordinates: sdk.Coordinates{Name: "app", Version: "0.0.0", PURL: "pkg:generic/app@0.0.0"},
})
child := sdk.NewDependency(sdk.Dependency{
    Coordinates: sdk.Coordinates{Name: "lodash", Version: "4.17.21", PURL: "pkg:npm/lodash@4.17.21"},
})

graph := sdk.New()
if err := graph.AddNode(parent); err != nil {
    return nil, err
}
if err := graph.AddNode(child); err != nil {
    return nil, err
}
if err := graph.AddEdge(parent.ID, child.ID); err != nil {
    return nil, err
}

Return req.Subproject and req.ExecutionTarget in the response so Bomly can keep the result tied to the planned scan target.

Package And Install

For development, build and install the binary directly:

go build -o ./bin/bomly-plugin-bun-lock-detector .
bomly plugins install ./bin/bomly-plugin-bun-lock-detector --dev
bomly plugins enable bomly.examples.detector.bun-lock

For distribution, package a package-only bomly-plugin.json manifest with the binary:

bomly-plugin.json
bin/
  bomly-plugin-bun-lock-detector
README.md

The manifest contains package and install fields only: ID, kind, version, runtime, API version, Bomly version constraint, entrypoint, source, homepage, description, and license. Bomly probes the binary at install time, verifies descriptor.name == manifest.id, and writes its own internal descriptor snapshot for plugin list, selectors, verification, and runtime registration.

Test It

Check installation and runtime readiness:

bomly plugins verify bomly.examples.detector.bun-lock
bomly plugins test bomly.examples.detector.bun-lock
bomly plugins doctor bomly.examples.detector.bun-lock

Run only this detector:

bomly scan --path ./my-project --detectors bomly.examples.detector.bun-lock --json

Or add it to the default detector set:

bomly scan --path ./my-project --detectors +bomly.examples.detector.bun-lock

Implementation Checklist

  • Declare accurate package-manager support and evidence patterns.
  • Wrap errors with useful context.
  • Avoid panics in normal flow.
  • Do not log secrets, tokens, or credentials.
  • Keep network calls explicit and explain them in the plugin README.
  • Keep remediation hints read-only and within the advertised capabilities.
  • Add local unit tests for parsing and graph construction.