Skip to content

Adopt Nix/devenv for reproducible, auditable dependency management #27

@gerchowl

Description

@gerchowl

Summary

Replace the current ad-hoc tooling installation (apt + curl | bash + manual version tracking) with Nix/devenv for declarative, reproducible, and cryptographically verified dependency management. This addresses regulatory requirements for medical device software (IEC 62304, FDA cybersecurity guidance) while eliminating the manual synchronization between Containerfile and EXPECTED_VERSIONS in tests.

Key benefits:

  • Single source of truth for all tool versions (flake.nix + flake.lock)
  • Cryptographic hash verification of all dependencies (supply chain security)
  • Air-gapped/offline rebuild capability for regulatory compliance
  • Automatic SBOM generation from dependency graph
  • Reproducible builds years after initial release

Current Pain Points

1. Version tracking is manual and fragmented

Versions are scattered across:

  • Containerfile (implicit "latest" for gh, just, uv via curl)
  • tests/test_image.py EXPECTED_VERSIONS dict (manually maintained)

Updates are reactive—tests fail after rebuild, then we update the test file.

2. No reproducibility guarantee

# Current: fetches "latest" at build time
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin

Building the same Containerfile 3 months later produces different results.

3. Supply chain vulnerabilities

  • curl | bash patterns trust remote scripts
  • apt packages trust Debian mirrors
  • No cryptographic verification of most downloads (gh and uv have manual checksum verification, but just does not)

4. Regulatory gaps

  • No built-in SBOM generation
  • Difficult to prove "rebuild from source" capability
  • No formal approval workflow for dependency updates

Proposed Solution: Nix/devenv

What changes

Current Proposed
apt-get install git curl packages = [ pkgs.git pkgs.curl ]
curl | bash for tools Nix packages with pinned hashes
EXPECTED_VERSIONS in tests Derived from Nix metadata (or eliminated)
Implicit "latest" versions Explicit pins in flake.lock

Example devenv.nix

{ pkgs, ... }:
{
  packages = [
    pkgs.git
    pkgs.curl
    pkgs.gh
    pkgs.just
    pkgs.uv
    pkgs.pre-commit
    pkgs.ruff
  ];

  languages.python = {
    enable = true;
    version = "3.12";
  };
}

flake.lock provides cryptographic pins

{
  "nodes": {
    "nixpkgs": {
      "locked": {
        "narHash": "sha256-abc123...",
        "rev": "def456..."
      }
    }
  }
}

Regulatory Benefits (IEC 62304 / FDA)

1. Air-gapped/offline builds

# Archive all sources for offline rebuild
nix flake archive --json > archive-manifest.json

# Export binary cache
nix copy --to file:///path/to/offline-cache .#devcontainer

Enables "rebuild without internet" for controlled manufacturing environments.

2. Supply chain protection

Every dependency is content-addressed:

  • Source tarballs verified by SHA256
  • Git commits verified by tree hash
  • Build outputs verified by content hash

Hash mismatch = build failure (detects tampering automatically)

3. SBOM generation

nix derivation show .#devcontainer > sbom.json

Complete dependency tree with versions, hashes, and sources.

4. Change control

# Diff between releases shows exactly what changed
diff release-1.0/flake.lock release-1.1/flake.lock

flake.lock becomes a controlled document for regulatory submissions.


Update Workflow Comparison

Update type Current With Nix
Patch Automatic on rebuild (reactive) nix flake update (proactive, controlled)
Minor Automatic, may surprise Explicit, reviewed before merge
Major Manual Containerfile edit + test update Change one line in devenv.nix
Rollback Rebuild from git history, hope repos unchanged git checkout flake.lock && nix build (identical)

Security updates

  • Stable nixpkgs channels backport security fixes
  • Automated PRs via Renovate/Dependabot for flake.lock updates
  • Weekly update cadence recommended

Trade-offs

Costs

  • Learning curve: Team needs Nix familiarity
  • Image size: ~200-400 MB larger (Nix store overhead)
  • Build time: Initial builds slower (Nix evaluation)

Mitigations

  • Start with hybrid approach (Debian base + Nix for tools)
  • Use binary cache to speed up builds
  • Incremental adoption possible

Implementation Steps

  • Evaluate devenv vs pure flake approach
  • Create flake.nix with current tool set
  • Verify all tools available in nixpkgs (gh, just, uv, pre-commit, ruff)
  • Set up binary cache (Cachix or self-hosted)
  • Implement offline archive workflow
  • Update tests to derive expected versions from Nix (or remove version checks)
  • Document regulatory workflows (SBOM generation, approval process)
  • Update CI pipeline

References


cc @c-vigo for discussion

Metadata

Metadata

Labels

featureNew feature or request

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions