Skip to content

feat: embed linked Rust dependency tree (cargo-auditable) in release binaries #2686

Description

@alangou

Problem Statement

We publish three images to ghcr.io/nvidia/openshell (gateway, supervisor, ci). None carries an SBOM, and nothing in the repo describes what is inside them.

The existing pipeline (tasks/sbom.toml) runs syft dir:. — a source-tree scan named openshell-source-<version>.cdx.json, answering a license compliance question, invoked by no workflow. It does not describe a shipped artifact, and it over-declares: it reads Cargo.lock, so it lists crates our feature flags eliminate (bundled-z3, bundled-ca-roots, --no-default-features).

The gap that matters is the binary. Both Dockerfiles only COPY a prebuilt binary onto distroless or alpine — there is no in-image compilation, so image content is "base packages + one binary". Syft, Grype and osv-scanner cannot enumerate crates from a stripped Rust binary, so even a correct image scan returns OS packages only.

For a product whose purpose is sandboxing untrusted agent workloads, "which crates are in the gateway I am running" should be answerable from the artifact itself.

Proposed Design

Build release binaries with cargo-auditable, which embeds the linked dependency tree as zlib-compressed JSON in a .dep-v0 ELF section.

This is the prerequisite layer, and it delivers value standalone: the data is read natively by tools users already run — Syft ≥ 1.15 (we pin 1.44.0), Grype ≥ 0.83, osv-scanner ≥ 2.0.1. Publishing an image SBOM as a signed attestation is a separate follow-up.

Scope

Release paths only. PR and e2e builds keep current behavior. The chain is already shaped for this — rust-native-build.yml is a workflow_call with a single caller:

release-dev.yml:58,65    ─┐
release-tag.yml:76,85    ─┼─→ docker-build.yml:172 ─→ rust-native-build.yml
branch-e2e.yml:85,96,107 ─┘

Changes

  1. Add an auditable boolean input (default false) to rust-native-build.yml, relayed through docker-build.yml.
  2. Set auditable: true at the release-dev.yml and release-tag.yml call sites; branch-e2e.yml unchanged.
  3. Inject the subcommand in rust-native-build.yml after the zigbuild branch (~line 242):
    cargo_cmd=("${cargo_cmd[0]}" auditable "${cargo_cmd[@]:1}")
  4. Neutralize sccache at job level when enabled, matching the precedent at release-vm-kernel.yml:131:
    env:
      RUSTC_WRAPPER: ${{ inputs.auditable && '' || 'sccache' }}
  5. Mirror in tasks/scripts/stage-prebuilt-binaries.sh (~line 188) behind an env var, so local matches CI.
  6. Pin cargo-auditable >= 0.7.3 in mise.toml.

The sccache constraint

mise.toml:64 sets RUSTC_WRAPPER = "sccache" globally; cargo-auditable installs as RUSTC_WORKSPACE_WRAPPER. Cargo does not choose between the two slots — it chains them as sccache cargo-auditable rustc. sccache then treats cargo-auditable as its compiler and fails: Compiler not supported: "Failed to invoke rustc!".

Disabling sccache globally is not an option — CI wires it deliberately (GHA backend and per-(component, arch) partitioning in rust-native-build.yml:79,100-103). Hence the per-input scoping above.

Alternatives Considered

Image SBOM without cargo-auditable. Yields base-OS packages only; the Rust tree stays invisible. That is the least interesting half for a distroless image holding one binary.

BuildKit --attest type=sbom. Orthogonal — it changes where an SBOM is stored, not whether the binary is legible. It also has separate obstacles here: --provenance=false is hardcoded (docker-build-image.sh:194, docker-build-ci.sh:34), and the podman path strips provenance flags (container-engine.sh:282-283), so local and CI would diverge. Worth revisiting independently.

Keep relying on syft dir:.. Over-declares and describes the source tree, not the artifact. The two SBOMs answer different questions; both should exist.

Related: #2671 (cargo-deny for supply-chain vulnerability scanning) covers the same CI surface from the advisory side.

Checklist

  • I've reviewed existing issues and the architecture docs
  • This is a design proposal, not a "please build this" request

Activity

  1. self-assigned this
    on Aug 11, 2026
  2. added
    github_actionsPull requests that update GitHub Actions code
    state:acceptedA maintainer decided OpenShell should pursue this issue
    on Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:buildRelated to CI/CD and buildsgithub_actionsPull requests that update GitHub Actions codestate:acceptedA maintainer decided OpenShell should pursue this issuestate:in-progressWork is currently in progress

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions