<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Go Client (bomhort-go) on BOMHort Documentation</title><link>https://docs.bomhort.dev/docs/ecosystem/go-client/</link><description>Recent content in Go Client (bomhort-go) on BOMHort Documentation</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://docs.bomhort.dev/docs/ecosystem/go-client/index.xml" rel="self" type="application/rss+xml"/><item><title>Overview</title><link>https://docs.bomhort.dev/docs/ecosystem/go-client/overview/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://docs.bomhort.dev/docs/ecosystem/go-client/overview/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Maintained in &lt;a href="https://github.com/seebom-labs/bomhort-go/blob/main/README.md"&gt;seebom-labs/bomhort-go&lt;/a&gt; — edit it there.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/seebom-labs/bomhort-go/actions/workflows/ci.yml"&gt;&lt;img src="https://github.com/seebom-labs/bomhort-go/actions/workflows/ci.yml/badge.svg" alt="CI"&gt;&lt;/a&gt;
&lt;a href="https://github.com/seebom-labs/bomhort-go/actions/workflows/e2e.yml"&gt;&lt;img src="https://github.com/seebom-labs/bomhort-go/actions/workflows/e2e.yml/badge.svg" alt="E2E"&gt;&lt;/a&gt;
&lt;a href="https://pkg.go.dev/github.com/seebom-labs/bomhort-go"&gt;&lt;img src="https://pkg.go.dev/badge/github.com/seebom-labs/bomhort-go.svg" alt="Go Reference"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The Go client for the &lt;a href="https://github.com/seebom-labs/BOMHort"&gt;BOMHort&lt;/a&gt; REST API.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Complete:&lt;/strong&gt; every public endpoint of the BOMHort api-gateway — SBOMs, findings, dependency trees, licenses, VEX, projects, clusters, namespaces, fleet, statistics, search, push-model uploads and source attribution (&lt;a href="https://docs.bomhort.dev/docs/ecosystem/go-client/api-coverage/"&gt;coverage&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero dependencies:&lt;/strong&gt; standard library only.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Production behaviour built in:&lt;/strong&gt; API key / service token auth, client-side rate limiting, 429 retries honouring &lt;code&gt;Retry-After&lt;/code&gt;, typed errors, iterator-based pagination, streaming downloads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testable:&lt;/strong&gt; &lt;a href="https://github.com/seebom-labs/bomhort-go/blob/main/bomhorttest"&gt;&lt;code&gt;bomhorttest&lt;/code&gt;&lt;/a&gt; is an in-memory fake gateway with BOMHort&amp;rsquo;s real upload, VEX scoping and matching semantics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verified against real BOMHort:&lt;/strong&gt; every PR runs the integration suite with strict decoding against the pinned BOMHort release; a weekly job also runs it against BOMHort &lt;code&gt;main&lt;/code&gt; to catch API drift.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Versioned with BOMHort:&lt;/strong&gt; &lt;code&gt;bomhort-go v0.8.x&lt;/code&gt; is the client for BOMHort 0.8.x (&lt;a href="https://docs.bomhort.dev/docs/ecosystem/go-client/compatibility/"&gt;compatibility&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Extracted from &lt;a href="https://github.com/seebom-labs/VEXViper"&gt;VEXViper&lt;/a&gt;&amp;rsquo;s &lt;code&gt;internal/bomhort&lt;/code&gt; (&lt;a href="https://github.com/seebom-labs/VEXViper/blob/main/docs/INTEGRATION.md#8-should-the-bomhort-go-client-be-its-own-module"&gt;why&lt;/a&gt;).&lt;/p&gt;</description></item><item><title>How to: Upload VEX</title><link>https://docs.bomhort.dev/docs/ecosystem/go-client/how-to-upload-vex/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://docs.bomhort.dev/docs/ecosystem/go-client/how-to-upload-vex/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Maintained in &lt;a href="https://github.com/seebom-labs/bomhort-go/blob/main/docs/VEX.md"&gt;seebom-labs/bomhort-go&lt;/a&gt; — edit it there.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the contract between a VEX producer and BOMHort ≥ 0.7.0, as implemented in BOMHort&amp;rsquo;s parser (&lt;code&gt;internal/vex/parser.go&lt;/code&gt;), parsing worker (&lt;code&gt;cmd/parsing-worker/vex_scope.go&lt;/code&gt;, &lt;code&gt;vex_rescue.go&lt;/code&gt;) and queries (&lt;code&gt;internal/clickhouse&lt;/code&gt;). The E2E suite verifies each rule below against a real BOMHort, and &lt;code&gt;bomhorttest&lt;/code&gt; implements the same rules.&lt;/p&gt;
&lt;h2 id="1-every-statement-belongs-to-exactly-one-sbom"&gt;1. Every statement belongs to exactly one SBOM&lt;/h2&gt;
&lt;p&gt;BOMHort has no fleet-wide VEX. A statement only ever affects findings of the SBOM it is &lt;strong&gt;scoped&lt;/strong&gt; to:&lt;/p&gt;</description></item><item><title>How to: Migrate from VEXViper's client</title><link>https://docs.bomhort.dev/docs/ecosystem/go-client/how-to-migrate-from-vexviper/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://docs.bomhort.dev/docs/ecosystem/go-client/how-to-migrate-from-vexviper/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Maintained in &lt;a href="https://github.com/seebom-labs/bomhort-go/blob/main/docs/MIGRATING-FROM-VEXVIPER.md"&gt;seebom-labs/bomhort-go&lt;/a&gt; — edit it there.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;bomhort-go started as VEXViper&amp;rsquo;s &lt;code&gt;internal/bomhort&lt;/code&gt; package and its &lt;code&gt;bomhorttest&lt;/code&gt; fake. It keeps the same shape (functional options, &lt;code&gt;APIError&lt;/code&gt;, &lt;code&gt;UploadVEX(ctx, filename, doc, sbomID)&lt;/code&gt;, sliding-window &lt;code&gt;RateLimiter&lt;/code&gt;), but extends it to the full BOMHort API, which required a few breaking changes.&lt;/p&gt;
&lt;h2 id="imports"&gt;Imports&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;-import &amp;#34;github.com/seebom-labs/vexviper/internal/bomhort&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;-import &amp;#34;github.com/seebom-labs/vexviper/internal/bomhort/bomhorttest&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;+import bomhort &amp;#34;github.com/seebom-labs/bomhort-go&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;+import &amp;#34;github.com/seebom-labs/bomhort-go/bomhorttest&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="client"&gt;Client&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;VEXViper&lt;/th&gt;
 &lt;th&gt;bomhort-go&lt;/th&gt;
 &lt;th&gt;Notes&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ListSBOMs(ctx, page, pageSize, search)&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ListSBOMs(ctx, &amp;amp;bomhort.SBOMListOptions{ListOptions: bomhort.ListOptions{Page: page, PageSize: pageSize}, Search: search})&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;options struct; &lt;code&gt;nil&lt;/code&gt; = server defaults&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;AllSBOMs(ctx)&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;AllSBOMs(ctx, nil)&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;or iterate lazily with &lt;code&gt;SBOMs(ctx, nil)&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;VEXStatements(ctx, page, pageSize)&lt;/code&gt; (one page)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ListVEXStatements(ctx, &amp;amp;bomhort.ListOptions{Page: page, PageSize: pageSize})&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;VEXStatements&lt;/code&gt; is now the iterator&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;AllVEXStatements(ctx)&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;unchanged&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;FindSBOM(ctx, ref)&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;unchanged&lt;/td&gt;
 &lt;td&gt;now also matches uploaded filenames (&lt;code&gt;pushed/&amp;lt;uuid&amp;gt;-&amp;lt;name&amp;gt;&lt;/code&gt;) and uses &lt;code&gt;?search=&lt;/code&gt; first&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;Vulnerabilities(ctx, id)&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Vulnerabilities(ctx, id, nil)&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;&amp;amp;bomhort.VulnerabilityOptions{Scope: bomhort.ScopeDirect}&lt;/code&gt; filters by dependency scope (BOMHort ≥ 0.8)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;Healthy&lt;/code&gt;, &lt;code&gt;Dependencies&lt;/code&gt;, &lt;code&gt;DownloadSBOM&lt;/code&gt;, &lt;code&gt;UploadVEX&lt;/code&gt;, &lt;code&gt;BaseURL&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;unchanged&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;WithAPIKey&lt;/code&gt;, &lt;code&gt;WithServiceToken&lt;/code&gt;, &lt;code&gt;WithHTTPClient&lt;/code&gt;, &lt;code&gt;WithRateLimit&lt;/code&gt;, &lt;code&gt;WithMaxRetries&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;unchanged&lt;/td&gt;
 &lt;td&gt;new: &lt;code&gt;WithRateLimiter&lt;/code&gt;, &lt;code&gt;WithUserAgent&lt;/code&gt;, &lt;code&gt;WithStrictDecoding&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;IsNotFound(err)&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;unchanged&lt;/td&gt;
 &lt;td&gt;new: &lt;code&gt;IsBadRequest&lt;/code&gt;, &lt;code&gt;IsUnauthorized&lt;/code&gt;, &lt;code&gt;IsForbidden&lt;/code&gt;, &lt;code&gt;IsRateLimited&lt;/code&gt;, &lt;code&gt;IsUnavailable&lt;/code&gt;, &lt;code&gt;StatusCode&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Behaviour changes:&lt;/p&gt;</description></item><item><title>Testing</title><link>https://docs.bomhort.dev/docs/ecosystem/go-client/testing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://docs.bomhort.dev/docs/ecosystem/go-client/testing/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Maintained in &lt;a href="https://github.com/seebom-labs/bomhort-go/blob/main/docs/TESTING.md"&gt;seebom-labs/bomhort-go&lt;/a&gt; — edit it there.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;bomhort-go is tested at four levels. The first three run without network access or Docker.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Level&lt;/th&gt;
 &lt;th&gt;Where&lt;/th&gt;
 &lt;th&gt;Command&lt;/th&gt;
 &lt;th&gt;Runs in CI&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Unit / wire contract&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;*_test.go&lt;/code&gt; in the root package&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;make test-race&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;every push/PR (&lt;code&gt;ci.yml&lt;/code&gt;)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Fake gateway&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;bomhorttest/server_test.go&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;make test-race&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;every push/PR&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Examples&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;example_test.go&lt;/code&gt; (&lt;code&gt;// Output:&lt;/code&gt; checked)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;make test&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;every push/PR&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Integration / E2E&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;test/integration&lt;/code&gt; (&lt;code&gt;-tags integration&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;make test-integration&lt;/code&gt; / &lt;code&gt;make e2e&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;every push/PR against the pinned BOMHort, weekly also against BOMHort &lt;code&gt;main&lt;/code&gt; (&lt;code&gt;e2e.yml&lt;/code&gt;)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="unit-tests"&gt;Unit tests&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;endpoints_test.go&lt;/code&gt; is a table with one row per endpoint. Each row asserts method, escaped path, query string, headers, request body and the decoded response. Add a row for every new method.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;client_test.go&lt;/code&gt; covers auth headers, retries on 429 (&lt;code&gt;Retry-After&lt;/code&gt; and back-off; the sleep function is injected), context cancellation, error decoding and strict decoding.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;misc_test.go&lt;/code&gt; covers upload validation, pagination helpers, &lt;code&gt;FindSBOM&lt;/code&gt; and streaming downloads; &lt;code&gt;ratelimit_test.go&lt;/code&gt; covers the limiter.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;make cover&lt;/code&gt; enforces a minimum total coverage (&lt;code&gt;COVER_MIN&lt;/code&gt;, default 85 %).&lt;/p&gt;</description></item><item><title>API Coverage</title><link>https://docs.bomhort.dev/docs/ecosystem/go-client/api-coverage/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://docs.bomhort.dev/docs/ecosystem/go-client/api-coverage/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Maintained in &lt;a href="https://github.com/seebom-labs/bomhort-go/blob/main/docs/API-COVERAGE.md"&gt;seebom-labs/bomhort-go&lt;/a&gt; — edit it there.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Every route of the BOMHort api-gateway (&lt;code&gt;backend/cmd/api-gateway/main.go&lt;/code&gt;) and the client method that calls it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Since&lt;/strong&gt; is the first BOMHort release with the route (≤ 0.6 = present in 0.6.1, the oldest release checked). Routes newer than the server answer &lt;code&gt;404&lt;/code&gt; (&lt;code&gt;IsNotFound&lt;/code&gt;); newer query parameters are ignored and newer fields stay empty (&lt;a href="https://docs.bomhort.dev/docs/ecosystem/go-client/compatibility/"&gt;COMPATIBILITY.md&lt;/a&gt;). Pagination column: ✓ = &lt;code&gt;ListX&lt;/code&gt; page call + &lt;code&gt;X&lt;/code&gt; iterator (+ &lt;code&gt;AllX&lt;/code&gt; where useful).&lt;/p&gt;</description></item><item><title>Compatibility &amp; Versioning</title><link>https://docs.bomhort.dev/docs/ecosystem/go-client/compatibility/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://docs.bomhort.dev/docs/ecosystem/go-client/compatibility/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Maintained in &lt;a href="https://github.com/seebom-labs/bomhort-go/blob/main/docs/COMPATIBILITY.md"&gt;seebom-labs/bomhort-go&lt;/a&gt; — edit it there.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="versions-follow-bomhort"&gt;Versions follow BOMHort&lt;/h2&gt;
&lt;p&gt;bomhort-go is released in &lt;strong&gt;lockstep with BOMHort&lt;/strong&gt;: &lt;code&gt;bomhort-go vX.Y.*&lt;/code&gt; is the client for &lt;code&gt;BOMHort X.Y.*&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Major.minor&lt;/strong&gt; is BOMHort&amp;rsquo;s. &lt;code&gt;v0.8.0&lt;/code&gt; ships when BOMHort 0.8.0 is final and covers its complete public API; a BOMHort minor without API changes still gets a (trivial) bomhort-go minor so the mapping stays obvious.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Patch&lt;/strong&gt; is ours: client fixes, &lt;code&gt;bomhorttest&lt;/code&gt; improvements, docs. &lt;code&gt;v0.8.3&lt;/code&gt; still targets BOMHort 0.8.x.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pre-releases&lt;/strong&gt; (&lt;code&gt;v0.8.0-rc.N&lt;/code&gt;) track BOMHort release candidates when a consumer needs them early.&lt;/li&gt;
&lt;li&gt;A newer client works against the previous BOMHort minor for everything that minor has (new routes answer &lt;code&gt;404&lt;/code&gt; → &lt;code&gt;IsNotFound&lt;/code&gt;, new fields are empty, new query parameters are ignored). An older client works against a newer BOMHort because decoding is lenient by default.&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;bomhort-go&lt;/th&gt;
 &lt;th&gt;Go&lt;/th&gt;
 &lt;th&gt;BOMHort&lt;/th&gt;
 &lt;th&gt;Tested in CI against&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;v0.8.x&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;1.25+ (&lt;code&gt;go.mod&lt;/code&gt;) and latest stable&lt;/td&gt;
 &lt;td&gt;0.8.x&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;v0.8.0&lt;/strong&gt; (pinned, every PR) · &lt;strong&gt;&lt;code&gt;main&lt;/code&gt;&lt;/strong&gt; (weekly)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The pinned BOMHort ref lives in &lt;code&gt;.github/workflows/e2e.yml&lt;/code&gt; (&lt;code&gt;BOMHORT_PINNED_REF&lt;/code&gt;). Bump it — and this table — when a new BOMHort release (or RC) ships; see &lt;a href="https://github.com/seebom-labs/bomhort-go/blob/main/docs/RELEASING.md"&gt;RELEASING.md&lt;/a&gt;.&lt;/p&gt;</description></item></channel></rss>