Skip to content

Supply chain posture

OpenCodeHub ships under Apache-2.0 with a hard rule: every transitive runtime dependency must sit on a permissive-license allowlist. This page documents what we ship, the CI gates that prove it, and the narrow set of tools we invoke as subprocesses rather than link against.

Every release produces a signed bundle of artifacts attached to the GitHub Release plus a tree of always-on files in main:

ArtifactFormatTrust anchor
opencodehub-pack.tar.gzDeterministic 9-item code-pack BOM (100k-token budget).cosign keyless .sig.bundle
SBOM.cdx.jsonCycloneDX 1.5 SBOM produced by @cyclonedx/cdxgen.cosign keyless .sig.bundle
och-scan.sarifOCH self-scan output at the released SHA.cosign keyless .sig.bundle
opencodehub-<tag>.intoto.jsonlSLSA Level 3 provenance covering all subjects.slsa-verifier

In-tree files:

  • SBOM.cdx.json — CycloneDX v1.5 SBOM, regenerated on every release.
  • THIRD_PARTY_LICENSES.md — human-readable third-party inventory.
  • NOTICE — Apache-2.0 NOTICE file.
  • CHANGELOG.md — generated by release-please from Conventional Commits.

docs/RELEASE.md is the operator-facing runbook for the release pipeline; it spells out the workflow split (release-please.yml, pre-release-gate.yml, release.yml), the cosign + SLSA contract, and the consumer verification commands.

Every production dependency must be on this list:

Apache-2.0
MIT
BSD-2-Clause
BSD-3-Clause
ISC
CC0-1.0
BlueOak-1.0.0
0BSD

The check is enforced by license-checker-rseidelsohn on every PR and as part of mise run check:full. See IP hygiene / License allowlist for the exact command and the note on the one known acceptance-script inconsistency.

BSL, BUSL, PolyForm, Commons Clause, GPL, and AGPL are rejected upfront. Source-available engines (e.g. LanceDB’s former license, Elastic) were considered and rejected in ADR 0001 specifically because preserving Apache-2.0 distribution rights is load-bearing.

GateToolTrigger
OSV scanosv-scanner scan source --lockfile pnpm-lock.yamlEvery CI run + mise run check:full
CodeQL.github/workflows/codeql.ymlEvery push + weekly schedule
OpenSSF Scorecard.github/workflows/scorecard.ymlWeekly + push to main
SARIF schemamise run sarif:validate + acceptance gate 13Every scanner run

Release gate policy: zero open CVEs on the lockfile at release time. If a bump is blocked (upstream has not shipped a fix, or the fix requires a breaking change), the PR must document the CVE, the reason, and a due date before release-please cuts the version.

All scanner outputs are uploaded as SARIF to the GitHub Security tab, so the org-wide view is one dashboard.

Some scanners that end users may want to run through codehub scan — hadolint (GPL-3.0), tflint (MPL-2.0 / BUSL depending on vendor build) — are not on the permissive allowlist. We still expose them. The trick is how: we invoke them as subprocesses, we never import them, never link them in, and never redistribute the binaries.

Concretely:

  • packages/scanners/src/ is a thin shell-out layer. Each scanner runner spawns the binary, captures stdout as SARIF, and emits findings into the graph.
  • The scanner binaries are a user-provided runtime dependency. Users install them separately (via brew, apt, choco, the vendor-published Docker image, etc.). OpenCodeHub does not ship them, bundle them, or require them at install time.
  • Scanner license obligations flow to the user running the scanner, not to OpenCodeHub.

This is the same pattern GitHub CodeQL uses with third-party SARIF producers, and it is the reason OBJECTIVES.md can commit to an Apache-2.0-end-to-end posture without crippling the scan surface.

The SCIP indexers OCH uses (scip-typescript, scip-python, scip-go, rust-analyzer, scip-java, scip-dotnet, scip-clang, scip-kotlin, scip-ruby) follow the same subprocess-only rule. They are installed via their language’s native package manager (npm install -g, go install, rustup component add, coursier install, dotnet tool install, etc.) and invoked via subprocess. ADR 0006 pins the canonical versions and documents the install channel per language.

  • pnpm-lock.yaml is committed.
  • Every install uses --frozen-lockfile.
  • Dependency bumps are Conventional Commits under build(deps): ... (or chore(deps): ... for devDependencies).
  • Dependabot or manual bumps go through the same osv + license gates as any other PR.

To verify a downloaded release end-to-end:

  1. cosign verify each blob (code-pack, SBOM, SARIF) against its .sig.bundle and the release.yml workflow identity.
  2. slsa-verifier the intoto.jsonl provenance covering the release subjects.
  3. License audit — confirm every component in SBOM.cdx.json is on the allowlist; cross-check against THIRD_PARTY_LICENSES.md.
  4. CVE check — run osv-scanner against the tag’s lockfile locally.

The exact commands live in docs/RELEASE.md §3 (Verification commands). The SBOM is deterministic — two regenerations at the same commit produce the same bytes. That is an extension of the determinism contract to the supply-chain layer.