# License compliance audit report — Phase 6 **This report is a strong first pass, not a legal opinion.** It should be reviewed by actual legal counsel before the project is publicly released, pitched to customers, or used as the basis for any compliance claim. Nothing in this document should be represented to a third party as legal advice or a certified compliance determination. ## Scope and goal Every component in the monorepo — `/agent`, `/transport`, `/ingest`, `/storage`, `/api`, `/web`, `/cli`, `/deploy`, and `enterprise/` — is licensed AGPLv3, with no separate commercial-license carve-out anywhere in the project. This audit inventories every third-party dependency across every language ecosystem, classifies each for AGPLv3 compatibility, resolves or explicitly flags anything that doesn't resolve cleanly, and stands up CI enforcement so this doesn't need to be manually re-audited from scratch every time a dependency changes. This is a compatibility audit, not a "replace every permissively licensed library" exercise. MIT, Apache-2.0, BSD, and ISC dependencies are all fine to depend on from AGPLv3 code. The concern is dependencies with licenses that are genuinely incompatible, impose unaccounted-for obligations, or aren't open source at all (source-available licenses like BSL/SSPL, anything with a field-of-use or non-compete restriction). ## Methodology 1. **Inventory** every dependency, direct and transitive, in every language ecosystem present in the repo: - **Rust** (`agent` workspace: `cairnobs-agent`, `cairnobs-parser`; `search`): `cargo-deny` (`cargo deny list --format tsv`), installed fresh for this audit (`cargo install cargo-deny --locked`). - **Go** (`api`, `ingest`, `alerting`, `enterprise`, `deploy/operator`, `terraform`, `proto`, `hack/benchmark-fixture`, `hack/windows-fixture` — every module with real third-party dependencies; `cli`, `hack/webhook-sink`, and `hack/alert-load-test` are stdlib-only, confirmed by inspecting their `go.mod` files, not scanned): `go-licenses` (`google/go-licenses`, installed via `go install ...@latest`), `go-licenses csv ./...` per module. - **npm** (`web`): `license-checker` (`npx license-checker --json`). - **Vendored/non-manifest content**: a repo-wide grep for copy/attribution markers ("adapted from", "copied from", "stackoverflow", stray copyright headers, "vendored") — zero hits. A separate pass for binary/asset files (fonts, icons, images) outside `node_modules`/build output — two font files and one SVG found, see below. Docker base images referenced in `docker-compose.yml` were pulled out and reviewed manually, since they're not a language-ecosystem dependency any of the three scanners above would see. 2. **Classify** every distinct license found into: (a) clearly compatible as a dependency, (b) requires a closer look, (c) actually incompatible or non-open-source. Every (b)/(c) result is cited with real reasoning below, not asserted. 3. **Named risk areas** (Redpanda's licensing history, ClickHouse client libraries, Phase 5 font/asset files, Kubernetes Operator tooling) checked explicitly, against primary sources (actual license files at the actual pinned versions/tags), not general recollection. 4. **Remediation** options recorded for every (c)/unresolved-(b) item — fix, isolate, or flag for a business decision. Nothing was silently swapped. 5. **Own license declarations audited**: root `LICENSE` file, per-ecosystem manifest `license` fields, and a repo-wide check for any file still claiming a license other than AGPLv3. 6. **`enterprise/` relicensed** to AGPLv3, closing the former commercial-license carve-out project-wide. 7. **CI enforcement** stood up so this audit's findings don't silently go stale. ## Inventory summary Full machine-readable inventory: `/docs/compliance/license-inventory.csv` and `.json` (columns: `component`, `dependency`, `version`, `license`, `direct_or_transitive`, `ecosystem`, `flagged`, `flag_reason`, `classification`). | Ecosystem | Component-scoped rows | Unique dependencies | Flagged (unique) | |---|---|---|---| | Rust (cargo) | 440 | — | 4 | | Go | 255 | — | 9 (`segmentio/asm` + 8 HashiCorp MPL-2.0 packages) | | npm | 75 | — | 3 | | Docker base images | 3 | 3 | 1 (Redpanda) | | Font/vendored assets | 3 | 3 | 1 (favicon) | | **Total** | **776** | **502** | **23** (~4.6%) | Rows double-count dependencies shared across multiple components by design (e.g. `pgx` appears once per Go module that imports it) — that's what makes the `component` column meaningful. All 502 unique dependencies resolved to classification (a) except one: Redpanda, classification (c), recorded below with remediation options rather than resolved unilaterally. ## Classification results **774 of 776 rows (all but Redpanda and the favicon asset) classify as (a): clearly compatible.** No dependency in this project's tree required a genuine (b)-category deep-dive that didn't resolve cleanly — every item that looked ambiguous at first pass (dual-licensed with a copyleft option, a non-standard license file, an unfamiliar SPDX identifier) turned out, on actual inspection, to resolve to (a) once read carefully. That's a real result of doing the reading, not an assumption going in — recorded below per item so the reasoning is checkable. ### Dual/multi-licensed crates electing a permissive branch (Rust) Four Rust crates carry an SPDX OR expression that includes a copyleft/less-common option alongside a permissive one. Standard practice for a disjunctive license grant is that the downstream user elects whichever listed option they prefer — we elect the permissive branch in every case, incurring zero copyleft obligation: | Crate | License expression | Elected | Note | |---|---|---|---| | `r-efi` | `MIT OR Apache-2.0 OR LGPL-2.1-or-later` | MIT/Apache-2.0 | Build-dependency only (via `tonic-build`→`prost-build`→`tempfile`→`getrandom`), for a UEFI target this project doesn't build for — doesn't even ship in a release artifact. | | `fastdivide` | `MIT OR zlib-acknowledgement` | MIT | | | `htmlescape` | `Apache-2.0 / MIT / MPL-2.0` | Apache-2.0/MIT | MPL-2.0 would also have been fine on its own merits (see below). | | `ryu` | `Apache-2.0 OR BSL-1.0` | Apache-2.0 | `BSL-1.0` here is the Boost Software License — permissive, unrelated to Redpanda's Business Source License below despite the shared abbreviation. | ### MPL-2.0 (Go, npm, and one Rust option above) MPL-2.0 is file-level (weak) copyleft: modifications to MPL-covered *files* must stay available under MPL if distributed, but combining MPL-covered code into a larger differently-licensed work — including an AGPLv3 work — does not require the larger work to relicense (MPL 2.0 §3.3, "Distribution of a Larger Work"). Pre-cleared as category (a) per this audit's own scope definition. Found in: - **Go** (`/terraform` only, HashiCorp's Terraform provider SDK and its own dependencies): `go-plugin`, `go-uuid`, `terraform-plugin-framework`, `terraform-plugin-go`, `terraform-plugin-log`, `terraform-registry-address`, `terraform-svchost`, `yamux`. Lower risk still than the general case: `/terraform` is its own standalone Go module producing a Terraform provider plugin binary, not linked into any core service. - **npm**: `axe-core` (dev-only, used for the Phase 5 accessibility sweep, never shipped), `lightningcss` + its platform-specific native binary (a transitive dependency of Vite's CSS pipeline — build-time only, never bundled into `web`'s shipped static output). ### `segmentio/asm` — tool detection gap, not a real license question (Go) `go-licenses` reported `Unknown` for every sub-package of `github.com/segmentio/asm` (a transitive dependency via the ClickHouse Go driver). Its actual `LICENSE` file (read directly from the module cache) is headed "MIT No Attribution" — SPDX `MIT-0`, a permissive MIT variant that drops the attribution requirement. The auto-detector's regex didn't recognize that non-standard heading text. Confirmed by reading the file, not assumed; carried as an explicit CI ignore with this citation (see the policy doc) rather than silently added to the general allow-list. ### Named risk areas (task 3) - **ClickHouse client libraries** (`github.com/ClickHouse/clickhouse-go/v2`, `github.com/ClickHouse/ch-go`): Apache-2.0, matching the server itself. No divergence. - **Kubernetes Operator tooling** (`sigs.k8s.io/controller-runtime`, `k8s.io/client-go`, `k8s.io/apimachinery`): Apache-2.0 (one forked sub-package, `apimachinery/third_party/forked/golang`, is BSD-3-Clause — also fine). The concern about "generated boilerplate's license headers" turned out not to apply: `deploy/operator/api/v1alpha1/ zz_generated.deepcopy.go` is, despite its name, **hand-written**, not actually produced by `controller-gen` (no kubebuilder/controller-gen binary was available when it was built — disclosed in the file's own doc comment and in `/deploy/README.md`). There's no real upstream-generated boilerplate to check for header drift against. - **Phase 5 font/asset files**: see below. ## Redpanda — classification (c), recorded for a business decision **The pinned Redpanda image (`docker.redpanda.com/redpandadata/redpanda:v24.2.7`, `docker-compose.yml` and `transport/`) ships under BSL 1.1 (Business Source License), confirmed against the actual `licenses/bsl.md` file at that tag** (`github.com/redpanda-data/redpanda`, tag `v24.2.7`) — not assumed from general familiarity with Redpanda's licensing history, which the audit brief specifically warned has shifted over time. Key facts, verified against primary sources: - **Not OSI-approved open source.** BSL is explicitly source-available, matching this audit's own category-(c) definition. - **Change Date**: 4 years from each version's release date, after which that version's `Licensed Work` converts to Apache-2.0. v24.2.7 was released 2024-10-11 (confirmed via the GitHub Releases API) — its Change Date is ~2028-10-11. As of this audit, it has **not** yet converted. - **The restriction is narrow**: BSL's Additional Use Grant permits any use except offering the Licensed Work as a "Streaming or Queuing Service" to third parties (defined as a commercial offering letting third parties create topics in the Licensed Work, e.g. a hosted Kafka broker product). This project's `docker-compose.yml` uses only plaintext core Kafka-protocol functionality — no RCL-gated enterprise features, no tiered storage, no SASL/RBAC — squarely within the permitted grant as an internal transport layer. - **No AGPL linking/compatibility issue.** Cairn OBS never links against Redpanda's code; it's consumed purely over the Kafka wire protocol, the same relationship as ClickHouse and Postgres. AGPLv3's copyleft doesn't reach across a network-protocol boundary to unrelated, separately-licensed software you merely talk to. - **The genuinely open question**: Phase 6 relicenses `enterprise/` to AGPLv3 specifically so that anyone, including competitors, can legally self-host or fork Cairn OBS — including offering it as a network service, per AGPLv3's own terms. If a third party does that using the bundled `docker-compose.yml` (which pulls this BSL-licensed Redpanda image), does *their* deployment trip BSL's Streaming-or-Queuing-Service restriction? Cairn OBS's ingest pipeline creates fixed internal topics, not per-end-user topics exposed for direct third-party production or consumption — so this is very likely **not** a Streaming-or-Queuing-Service under BSL's own definition. But this is a business/redistribution judgment call about a hypothetical third party's use, not a pure technical compatibility question this audit can close unilaterally. ### Remediation options (recorded per task 4's requirement) 1. **Accept as-is.** Document the reasoning above; Cairn OBS's own use is clearly within BSL's permitted grant, and the third-party-SaaS scenario is a reasonable-but-unverified reading, not a known violation. Lowest effort, zero functional change. 2. **Swap to Apache Kafka** (Apache-2.0, genuinely OSI open source). `apache/kafka` (KRaft mode, no ZooKeeper needed as of Kafka 3.x) is wire-protocol-compatible with everything `transport`/`ingest`/`search` already speak. Real tradeoff: Redpanda was originally chosen partly for its lightweight single-binary footprint (`docker-compose.yml` runs it with `--smp=1 --memory=1G --overprovisioned`, tuned for a resource-constrained local/homelab deployment per `CLAUDE.md`'s stated deployment targets); Kafka's JVM-based broker has a materially larger minimum memory/startup footprint. This is a real regression for the project's stated "docker-compose for local/homelab" use case, not a drop-in swap with no cost. 3. **Stop bundling a pinned broker image at all.** `transport/` already has no application code of its own — it's a thin `docker-compose` wrapper and topic-provisioning script. Document Kafka-API compatibility as the requirement and let the operator supply their own broker (self-installed Apache Kafka, their own separately-licensed Redpanda, or anything else wire-compatible). This moves the redistribution question out of this project's own `docker-compose.yml` entirely, at the cost of a rougher out-of-the-box local dev experience (an extra manual setup step instead of `docker compose up`). **Decision recorded 2026-08-16: option 1, accept as-is.** No code or deployment change was made as a result — Redpanda stays pinned at v24.2.7 in `docker-compose.yml`/`transport/`, under BSL 1.1, as a disclosed and accepted risk rather than an unresolved one. This decision should be revisited if the project's redistribution posture changes materially (e.g. an official hosted/managed offering of Cairn OBS itself, which would make the third-party-SaaS reading in this section Cairn OBS's *own* situation rather than a hypothetical third party's). ## Non-license finding: `favicon.svg` `web/src/lib/assets/favicon.svg` was SvelteKit's own default project scaffold logo (`