github.com/bomly-dev/bomly-sdk is the contract module for building Bomly
components: detectors, matchers, auditors, and analyzers. It is four packages,
split by the question each answers:
| Package | Answers | Import it when |
|---|---|---|
model |
What the data is: the dependency graph, packages and the registry, vulnerabilities and findings, the vocabularies, and the normalization, merge, and policy rules they share. | Always; every component reads and returns these. |
plugin |
What a component is: the Detector, Matcher, Auditor, and Analyzer interfaces, their descriptors and request/response types, the Base* defaults, and Module/HostContext. |
Implementing a component, embedded or as a plugin. |
runtime |
How a component runs out of process: ServeModule for a plugin binary's main, and Client/HandshakeConfig/ClientPluginMap for the host that launches it, over the go-plugin gRPC transport. |
A plugin binary's main, or hosting plugins. |
httpkit |
Outbound HTTP with Bomly's proxy and CA policy. | Rarely directly; a component gets it from HostContext.HTTPClient(). |
The module root imports nothing and declares nothing; its package doc is this map.
go get github.com/bomly-dev/bomly-sdk@latestA Bomly plugin is a component packaged as a plugin.Module and served from
main by the runtime:
package main
import (
"github.com/bomly-dev/bomly-sdk/plugin"
"github.com/bomly-dev/bomly-sdk/runtime"
)
func main() {
runtime.ServeModule(plugin.Module{
Kind: plugin.PluginKindDetector,
Detector: &plugin.DetectorModule{Descriptor: descriptor, Support: support, New: newDetector},
})
}The same Module value registers embedded in the host; a component never
learns which mode it runs in, because it reaches the host only through
plugin.HostContext. See the
Bomly plugin documentation
for the full authoring guide, packaging layout (bomly-plugin.json), and
installation flow.
Embed the Base* types (plugin.BaseDetector, plugin.BaseMatcher,
plugin.BaseAuditor, plugin.BaseAnalyzer) in your implementation so future
additions to the component interfaces do not break your build.
The SDK ships shared helper subpackages so component modules and external plugins reuse the same implementations Bomly's built-ins use:
system— bounded filesystem reads plus exec, path, and environment wrappers.filecache— TTL-based on-disk JSON cache with typedGet/Sethelpers.logkit— secret-safe subprocess logging: argument/URL sanitizers, command fields, stderr counter.detectorkit— detector helpers: manifest metadata, source positions, remediation hints, subgraphs, build-tool readiness and timeouts.matcherkit— matcher helpers: registry package seeding and license normalization.testkit— test helpers: fuzz graph invariants, typed-node constructors, Go binary builders, lockfile position assertions.purlkit— the single home for package-URL behavior: parsing, building, canonicalizing, the purl-type mapping table, and the per-ecosystem name split, over packageurl-go and go-pep440-version.spdxkit— the single home for SPDX license behavior: expression validation, classification, deprecated-identifier canonicalization, and deterministicLicenseRefminting, containing go-spdx's panics on untrusted input.conformance— the reusable plugin-contract test suite: run it against yourplugin.Modulefor descriptor validity, JSON round-trip stability, host-context construction, the Ready/Applicable lifecycle, role capabilities, and optionally a transport probe of the built binary.
Within each package a file is named for the concept it owns and every test
file pairs with the source file of the same stem; AGENTS.md carries the map.
v0.13.0 dissolved the root package into model, plugin, and runtime,
and moved the HTTP client provider to httpkit. The wire protocol is
unchanged; every identifier keeps its name and moves to the package that
owns it:
Before (sdk.) |
After |
|---|---|
| Graph, node, package, registry, vulnerability, finding, vocabulary, scope, origin, digest, contact, document, normalization, merge, and policy types and functions | model. (same names) |
Detector, Matcher, Auditor, Analyzer, Base*, *Descriptor, *Request/*Result/*Response, Module, *Module, HostContext, RuntimeInfo, Validate*, ConfigSchemaFor, PluginKind*, Consolidated*, ExecutionTarget, Subproject, FilterDetectionResultByScope |
plugin. (same names) |
ServeModule, Serve*, Served*, Client, HandshakeConfig, ClientPluginMap, EnvVerbosity, EnvPluginConfigFile, EnvPluginID, RawPluginConfigFromEnv, DecodePluginConfigFromEnv |
runtime. (same names) |
HTTPClientProvider, HTTPClientConfig, NewHTTPClientProvider, NewHTTPClientProviderFromEnv, HTTPClientConfigFromEnv, NewHTTPClient, EnvHTTP* |
httpkit.ClientProvider, httpkit.ClientConfig, httpkit.NewClientProvider, httpkit.NewClientProviderFromEnv, httpkit.ClientConfigFromEnv, httpkit.NewClient, httpkit.EnvHTTP* |
Two spellings changed besides the package: containsControlChar is now
model.ContainsControlChar, and the component-name bound is
model.MaxComponentNameLength. HostContext.HTTPClient() returns
*httpkit.ClientProvider. A file that already imported the root as model
changes only its import path. A consumer package named plugin imports
github.com/bomly-dev/bomly-sdk/plugin under an alias, or renames itself.
sbom— the SBOM codec: projects a graph into SPDX 2.3 or CycloneDX JSON and reads such a document back into a graph, with the document model, the strict ingest preflight, and the assertions a document carries about itself.graphview— what a document may say about a node: the package URL it publishes, which of its children a document can name, and which nodes count as top-level parents.
Both come from the CLI's internal/sbom and internal/graphview
(bomly-cli ADR-0045). The CLI and the Syft and Grype plugins adopt this
package from the release that carries it, in that order -- the plugins
first, then the CLI, which pins both -- and delete their copies as they do.
Two independent compatibility axes govern this module:
- In-process (Go API) — the component interfaces and types consumed by
embedders. Signature changes require a recompile. Embedding the
Base*defaults insulates implementations from most interface growth. - Wire (managed-plugin protocol
bomly.plugin.v1) — JSON payloads exchanged with external plugin binaries. Within protocol v1, changes are strictly additive: new optional (omitempty) fields and new optional RPCs only. Hosts treat unimplemented RPCs as feature fall-backs; unknown JSON fields are ignored by both sides. Fields and RPCs are never removed, renamed, or repurposed within v1. A breaking wire change would ship as a newbomly.plugin.v2service negotiated alongside v1 — old binaries keep speaking v1.
Plugin binaries built against an older SDK release keep working against newer hosts (and vice versa) as long as both speak protocol v1.
Releases are plain semver tags (vX.Y.Z) cut from main. While the module is
v0, minor releases may adjust the in-process Go API (the wire contract stays
additive regardless); patch releases are always safe. Consumers — Bomly itself
and plugin repositories — should pin released versions, never commits or
branches.
Release ordering when the contract changes: this module tags first, plugin repositories adopt the new tag, then Bomly updates its pin.
Apache-2.0. See LICENSE.