# Package Management

Dependency management, lockfiles, registries, and supply chain security.

---

## Core Principles

1. **Lockfiles committed** — Every repo commits lockfiles (`Cargo.lock`, `go.sum`, `pnpm-lock.yaml`, `package-lock.json`, `poetry.lock`, `gradle.lockfile`)
2. **Exact versions** — No floating versions (`^`, `~`, `*`, `latest`) in lockfiles
3. **Private registry/proxy** — Mirror all public registries; block typosquatting
4. **SBOM generation** — Every build produces SPDX/CycloneDX SBOM
5. **Vulnerability scanning** — Automated in CI on every PR
4. **Signed artifacts** — Cosign/Sigstore for container images and binaries

---

## Language-Specific Package Managers

### Rust: Cargo
```toml
# Cargo.toml
[package]
name = "my-service"
version = "0.1.0"
edition = "2021"

[workspace]
resolver = "2"
members = ["domain", "application", "infrastructure", "api"]

[dependencies]
# Exact versions in lockfile only; semver ranges here OK
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.38", features = ["full"] }
sqlx = { version = "0.7", features = ["runtime-tokio", "postgres", "uuid"] }
thiserror = "1.0"
anyhow = "1.0"

[dev-dependencies]
mockall = "0.12"
proptest = "1.0"
```

**Commands**: `cargo update`, `cargo audit`, `cargo deny check`, `cargo tree -d`

### Go: Go Modules
```go
// go.mod
module github.com/org/my-service

go 1.22

require (
    github.com/gin-gonic/gin v1.9.1
    github.com/jackc/pgx/v5 v5.5.0
    github.com/redis/go-redis/v9 v9.3.0
    go.uber.org/zap v1.26.0
    golang.org/x/sync v0.5.0
)

require (
    // indirect dependencies - managed by go mod tidy
)
```

```bash
# go.sum committed
go mod tidy
go mod verify
go mod download
govulncheck ./...
```

**Private Modules**
```bash
# ~/.gitconfig or git config
[url "ssh://git@github.com/"]
    insteadOf = https://github.com/

# GOPRIVATE for private modules
export GOPRIVATE=github.com/myorg/*
```

---

### Python: uv (Recommended) / Poetry / PDM
```toml
# pyproject.toml (uv)
[project]
name = "my-service"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
    "fastapi==0.109.0",
    "pydantic==2.6.0",
    "pydantic-settings==2.2.0",
    "sqlalchemy==2.0.25",
    "asyncpg==0.29.0",
    "redis==5.0.1",
    "pydantic[email]==2.6.0",
]

[project.optional-dependencies]
dev = [
    "pytest==8.0.0",
    "pytest-asyncio==0.23.0",
    "pytest-cov==4.1.0",
    "ruff==0.3.0",
    "mypy==1.8.0",
]

[tool.uv]
dev-dependencies = ["dev"]
```

```bash
# uv.lock committed
uv sync
uv lock
uv pip compile --generate-hashes pyproject.toml -o requirements.txt
pip-audit -r requirements.txt
```

### JavaScript/TypeScript: pnpm (Recommended)
```json
// package.json
{
  "name": "my-frontend",
  "version": "1.0.0",
  "private": true,
  "packageManager": "pnpm@9.0.0",
  "engines": { "node": ">=20" },
  "dependencies": {
    "react": "18.3.0",
    "react-dom": "18.3.0",
    "react-router-dom": "6.24.0",
    "zod": "3.23.0",
    "@tanstack/react-query": "5.28.0"
  },
  "devDependencies": {
    "@types/react": "18.3.0",
    "@types/react-dom": "18.3.0",
    "typescript": "5.4.0",
    "vite": "5.3.0",
    "biome": "1.8.0"
  }
}
```

```bash
# pnpm-lock.yaml committed
pnpm install --frozen-lockfile
pnpm audit
pnpm update --interactive
```

**Monorepo (pnpm Workspace)**
```yaml
# pnpm-workspace.yaml
packages:
  - packages/*
  - services/*
  - tools/*
```

```json
// package.json (root)
{
  "private": true,
  "workspaces": ["packages/*", "services/*", "tools/*"],
  "scripts": {
    "build": "turbo run build",
    "test": "turbo run test",
    "lint": "turbo run lint"
  }
}
```

---

### JVM: Gradle (Kotlin DSL) — Recommended
```kotlin
// build.gradle.kts (root)
plugins {
    id("org.springframework.boot") version "3.2.0" apply false
    id("io.spring.dependency-management") version "1.1.4" apply false
    kotlin("jvm") version "1.9.0" apply false
    kotlin("plugin.spring") version "1.9.0" apply false
    kotlin("plugin.serialization") version "1.9.0" apply false
}

dependencyResolutionManagement {
    repositories {
        mavenCentral()
        maven { url = uri("https://maven.pkg.github.com/myorg/*") }
        gradlePluginPortal()
    }
    // Resolution strategy for consistent versions
    resolutionStrategy {
        eachDependency { details ->
            if (details.requested.group == "org.springframework") {
                details.useVersion("6.1.0")
            }
        }
    }
}
```

```kotlin
// settings.gradle.kts
pluginManagement {
    repositories {
        gradlePluginPortal()
        mavenCentral()
    }
    resolutionStrategy {
        eachPlugin { details ->
            if (details.id.id == "org.springframework.boot") {
                details.useVersion("3.2.0")
            }
        }
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        mavenCentral()
        maven { url = uri("https://maven.pkg.github.com/myorg") }
    }
}

rootProject.name = "my-platform"
include("domain", "application", "infrastructure", "api")
```

```kotlin
// api/build.gradle.kts
plugins {
    id("org.springframework.boot")
    id("io.spring.dependency-management")
    kotlin("jvm")
    kotlin("plugin.spring")
    kotlin("plugin.serialization")
}

dependencies {
    implementation(project(":domain"))
    implementation(project(":application"))
    implementation(project(":infrastructure"))
    
    implementation("org.springframework.boot:spring-boot-starter-webflux")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("io.kotest:kotest-runner-junit5:5.8.0")
}
```

```bash
# gradle.lockfile committed (Gradle 7.5+)
./gradlew dependencies --write-locks
./gradlew dependencyUpdates
```

---

## Private Registry / Proxy

### Why Mirror Public Registries?
- **Typosquatting protection** — `lodash` vs `lodashh` blocked
- **Availability** — Public registry outage doesn't break your builds
- **Speed** — Local cache = faster CI
- **Control** — Scan on ingest; approve/deny packages
- **License compliance** — Enforce allowed licenses

### Registry Options

| Type | Tool | Best For |
|---|---|---|
| **NPM** | Verdaccio, Nexus, Artifactory, GitHub Packages | JS/TS |
| **PyPI** | devpi, Nexus, Artifactory, GitHub Packages | Python |
| **Maven** | Nexus, Artifactory, GitHub Packages | JVM |
| **Cargo** | crates.io mirror, private registry (cargo-registries) | Rust |
| **Go** | Athens, GOPROXY, Go Module Mirror | Go |
| **Container** | Harbor, Nexus, Artifactory, GHCR, ECR | All (Docker images) |
| **Universal** | Nexus, Artifactory, JFrog, Cloudsmith | Multi-language |

### Example: Verdaccio (NPM Proxy)
```yaml
# verdaccio/config.yaml
storage: ./storage
auth:
  htpasswd:
    file: ./htpasswd
uplinks:
  npmjs:
    url: https://registry.npmjs.org/
    cache: true
    timeout: 100s
packages:
  '@myorg/*':
    access: $all
    publish: $authenticated
    proxy: npmjs
  '**':
    access: $all
    publish: $authenticated
    proxy: npmjs
```

```bash
# Client config
npm config set registry http://localhost:4873
npm config set @myorg:registry http://localhost:4873
# Or .npmrc in project
registry=http://localhost:4873
@myorg:registry=http://localhost:4873
```

---

## SBOM (Software Bill of Materials)

### Generate at Build
```bash
# Rust
cargo install cargo-cyclonedx
cargo cyclonedx --format json --output sbom.json

# Go
go install github.com/CycloneDX/cyclonedx-go/cmd/cyclonedx-go@latest
cyclonedx-go -mod -json -output sbom.json

# Python
pip install cyclonedx-bom
cyclonedx-py -i requirements.txt -o sbom.json --format json

# JS/TS (pnpm)
pnpm install -g @cyclonedx/bom
cyclonedx-bom -o sbom.json

# JVM (Gradle)
./gradlew cyclonedxBom

# Container (Syft - universal)
syft packages dir:. -o cyclonedx-json=sbom.json
syft packages myorg/my-service:1.0.0 -o cyclonedx-json=sbom.json
```

### SBOM in CI
```yaml
# .github/workflows/sbom.yml
name: Generate SBOM
on: [push, release]
jobs:
  sbom:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Generate SBOM (Syft)
        uses: anchore/sbom-action@v0
        with:
          format: cyclonedx-json
          output-file: sbom.json
      - name: Upload SBOM
        uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.json
      - name: Scan for Vulns
        uses: anchore/scan-action@v3
        with:
          sbom: sbom.json
          severity-cutoff: high
```

---

## Vulnerability Scanning

### In CI Pipeline
```yaml
# Unified scanning with Trivy (works for all)
- name: Trivy Scan
  uses: aquasecurity/trivy-action@master
  with:
    scan-type: 'fs'
    format: 'sarif'
    output: 'trivy-results.sarif'
    severity: 'HIGH,CRITICAL'
    exit-code: '1'
    ignore-unfixed: true

- name: Upload to Code Scanning
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: 'trivy-results.sarif'
```

### Language-Specific
```bash
# Rust
cargo audit
cargo deny check

# Go
govulncheck ./...

# Python
pip-audit -r requirements.txt

# JS/TS
pnpm audit --prod
npm audit --audit-level=high

# JVM
./gradlew dependencyCheckAnalyze
# or OWASP Dependency Check CLI
```

---

## Dependency Update Automation

### Renovate (Recommended — Most Flexible)
```json
// renovate.json
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "config:recommended",
    ":automergePatch",
    ":automergeMinor",
    ":disableUnstable",
    ":prConcurrentLimit10",
    ":scheduleWeekly"
  ],
  "packageRules": [
    {
      "matchFileNames": ["**/Cargo.toml"],
      "groupName": "Rust dependencies",
      "automerge": false
    },
    {
      "matchFileNames": ["**/go.mod"],
      "groupName": "Go dependencies",
      "automerge": true
    },
    {
      "matchFileNames": ["**/pyproject.toml"],
      "groupName": "Python dependencies",
      "automerge": true
    },
    {
      "matchDatasource": "docker",
      "enabled": false
    }
  ],
  "vulnerabilityAlerts": {
    "enabled": true,
    "labels": ["security"],
    "automerge": true
  }
}
```

### Dependabot (GitHub Native)
```yaml
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "cargo"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
    groups:
      rust-deps:
        patterns: ["*"]
        update-types: ["minor", "patch"]
    labels: ["dependencies", "rust"]
  
  - package-ecosystem: "gomod"
    directory: "/"
    schedule:
      interval: "weekly"
    groups:
      go-deps:
        patterns: ["*"]
    labels: ["dependencies", "go"]
  
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "weekly"
    labels: ["dependencies", "python"]
  
  - package-ecosystem: "npm"
    directory: "/frontend"
    schedule:
      interval: "weekly"
    labels: ["dependencies", "npm"]
```

---

## Supply Chain Security Checklist

- [ ] Lockfiles committed for all languages
- [ ] Private registry/proxy configured for all ecosystems
- [ ] SBOM generated at every build (CycloneDX/SPDX)
- [ ] SBOM signed (Cosign) and uploaded as artifact
- [ ] Vulnerability scanning in CI (Trivy, language-specific)
- [ ] Dependency update automation (Renovate/Dependabot)
- [ ] License scanning (allowed/denied lists)
- [ ] Container images signed (Cosign) and verified on deploy
- [ ] Admission control requires signatures + scan pass
- [ ] Secrets detection in pre-commit + CI
- [ ] SLSA provenance generated (SLSA Level 1+)

---

## Related Notes

- [[Tools/Static-Analysis]] — Dependency scanning in CI
- [[Tools/Build-Tools]] — Build-time dependency resolution
- [[Security/Supply-Chain-Security]] — SLSA, signing, admission
- [[Security/Secure-Coding]] — Dependency hygiene
- [[DevOps/CI-CD-Best-Practices]] — Pipeline integration
- [[Languages/*]] — Language-specific package management

---

*Sources: Cargo, Go Modules, uv, pnpm, Gradle, Maven, npm, PyPI, crates.io, GitHub Packages, Verdaccio, Nexus, Artifactory, CycloneDX, SPDX, Syft, Trivy, Renovate, Dependabot, Cosign, Sigstore, SLSA, OWASP Dependency Check*