Mise Tool Management#
Mise (formerly rtx) is a polyglot runtime and tool version manager used in the opnDossier project to manage development tool dependencies and ensure reproducible builds across local development environments and CI/CD pipelines. The project migrated to mise in January 2026 to replace multiple individual tool setup actions with a unified configuration-driven approach.
The mise.toml configuration file defines 18 development tools including Go (1.26.0), GoReleaser, golangci-lint, security scanners, documentation generators, and pre-commit hooks. All tools are automatically installed via mise install and made available through mise's PATH management and optional shim system. The configuration enables aggressive activation mode to ensure tools activate in all shell contexts.
While mise manages most development tools, the project makes a deliberate exception for cyclonedx-gomod, which is installed via go install due to trust issues with mise shims in GoReleaser subprocesses. This limitation highlights a key architectural constraint when integrating mise with tools that spawn subprocesses expecting direct binary execution.
Mise Architecture and Purpose#
What is Mise?#
Mise is an open-source tool version manager that handles multiple programming language runtimes and CLI tools through a single configuration file. It provides:
- Unified tool management: Single configuration for all development dependencies
- Automatic environment setup: Tools are automatically available when entering project directories
- Version pinning: Ensures consistent tool versions across team members and environments
- Polyglot support: Manages tools from multiple ecosystems (Go, Python, Node.js, system packages)
- Shim system: Creates wrapper executables that automatically route to the correct tool version
Why opnDossier Uses Mise#
The migration to mise (PR #172) addressed several pain points in the original development workflow:
- CI/CD Complexity: Replaced multiple individual GitHub Actions (
setup-go,setup-python,setup-node) with a singlejdx/mise-action - Version Drift: Eliminated inconsistencies between local development and CI environments
- Setup Fragmentation: Consolidated tool installation logic from scattered scripts into one declarative configuration
- Reproducibility: Ensured that
mise installproduces identical environments across machines
Follow-up refinements (PR #173) established the pattern of using mise exec for tool invocations to guarantee tools run within the mise-managed environment.
mise.toml Configuration Reference#
Complete Configuration File#
The mise.toml file defines all managed tools and settings:
# cyclonedx-gomod: installed via go install in CI (mise shim causes trust issues in goreleaser subprocesses)
[tools]
act = "latest"
cosign = "3.0.5"
git-cliff = "2.12.0"
"github:anchore/quill" = "latest"
go = "1.26.0"
"go:github.com/google/go-licenses" = "1.6.0"
"go:github.com/securego/gosec/v2/cmd/gosec" = "latest"
"go:golang.org/x/perf/cmd/benchstat" = "0.0.0-20260409210113-8e83ce0f7b1c"
golangci-lint = "2.11.4"
goreleaser = "latest"
just = "1.46.0"
markdownlint-cli2 = "0.21.0"
"pipx:mdformat" = { version = "0.7.21", uvx_args = "--with mdformat-gfm --with mdformat-front-matters --with mdformat-footnote --with mdformat-simple-breaks --with mdformat-gfm-alerts --with mdformat-toc --with mdformat-wikilink --with mdformat-tables" }
"pipx:mkdocs" = { version = "1.6.1", uvx_args = "--with mkdocs-material --with pymdown-extensions" }
pre-commit = "latest"
python = "latest"
uv = "latest"
# Many of these settings are defaults, but we are explicit in case they change in the future and to make it clear to users what is enabled.
[settings]
activate_aggressive = true
idiomatic_version_file_enable_tools = [ "python", "go", "bun" ]
env_cache = true
exec_auto_install = true
github_attestations = true
lockfile = true
pin = true
slsa = true
[settings.npm]
package_manager = "bun"
[settings.pipx]
uvx = true
Tool Categories#
Core Development Tools#
- go = "1.26.0": Primary programming language, version pinned to match go.mod toolchain directive
- just = "1.46.0": Command runner for development tasks (replaces Make)
- python = "latest": Required for documentation tools and pre-commit hooks (pinned to 3.14.3 in .python-version)
- uv = "latest": Fast Python package installer used by pipx tools
- bun = "1.3.10": JavaScript runtime configured via idiomatic version file
Release and Build Tools#
- goreleaser = "latest": Multi-platform release automation
- git-cliff = "2.12.0": Changelog generation from conventional commits
- cosign = "3.0.5": Artifact signing for SLSA provenance and Sigstore attestations
- github/quill = "latest": macOS code signing utility
- go.com/google/go-licenses = "1.6.0": License compliance tool for generating THIRD_PARTY_NOTICES files
Code Quality Tools#
- golangci-lint = "2.11.4": Meta-linter running 70+ Go linters
- go.com/securego/gosec/v2/cmd/gosec = "latest": Security vulnerability scanner for Go code
- pre-commit = "latest": Git hook framework for automated code quality checks
- markdownlint-cli2 = "0.21.0": Markdown linting for documentation
Documentation Tools#
- pipx = "0.7.21": Markdown formatter with GFM extensions
- pipx = "1.6.1": Static site generator for project documentation
Testing and CI Tools#
- act = "latest": Local GitHub Actions runner for testing workflows
- go.org/x/perf/cmd/benchstat = "0.0.0-20260409210113-8e83ce0f7b1c": Computes and compares statistics about Go benchmark results, used by the
just bench-comparerecipe
Version Pinning Strategy#
The configuration uses a hybrid pinning strategy:
- Exact versions for critical tools (Go, cosign, golangci-lint, git-cliff, go-licenses)
- "latest" for evolving tools (goreleaser, gosec, act, quill, pre-commit, python, uv)
- Idiomatic version files for language runtimes (Go, Python, Bun) via
.go-version,.python-version, and.bun-version
This approach balances reproducibility (pinned versions prevent unexpected breakage) with security updates (latest versions ensure vulnerability patches).
Settings Configuration#
The mise configuration includes several settings to optimize tool management and security:
activate_aggressive#
activate_aggressive = true ensures mise tools activate in all shell contexts, including:
- Non-interactive shells
- Subshells spawned by scripts
- CI/CD environments
- Editor-integrated terminals
idiomatic_version_file_enable_tools#
idiomatic_version_file_enable_tools = ["python", "go", "bun"] enables mise to read language versions from standard version files:
- .go-version: Contains
1.26.0, ensuring mise uses this Go version - .python-version: Contains
3.14.3, pinning Python despitepython = "latest"in mise.toml - .bun-version: Contains
1.3.10for Bun JavaScript runtime
This pattern allows dual-source version management: mise.toml declares "latest" for automatic updates, while idiomatic files pin specific versions for reproducibility. The idiomatic version files take precedence when present.
Other Settings#
env_cache = true: Enables environment variable caching for improved performanceexec_auto_install = true: Automatically installs missing tools when invoked viamise execgithub_attestations = true: Enables GitHub attestation support for tool installationslockfile = true: Generates and uses mise.lock for reproducible installationspin = true: Enables version pinning featuresslsa = true: Enables SLSA verification for tool installations[settings.npm] package_manager = "bun": Configures Bun as the npm package manager[settings.pipx] uvx = true: Enables uv-based pipx (uvx) for faster Python tool installations
As noted in the configuration, many of these are mise defaults but are explicitly declared to ensure stability if defaults change and to clearly document enabled features.
Security Settings and Build Hardening#
While mise itself doesn't provide security features like SLSA verification or attestations, the opnDossier project implements comprehensive security through mise-managed tools:
SLSA Provenance#
SLSA Level 3 provenance is generated using GitHub Actions' native attestation framework during the release workflow. The actions/attest-build-provenance action creates cryptographic attestations linking artifacts to their source code, build environment, and build process.
Artifact Signing with Cosign#
Cosign v3 (managed by mise) provides keyless signing via Sigstore's OIDC authentication. The GoReleaser configuration invokes cosign to sign all release artifacts with .sigstore.json bundles.
Security Scanning#
- gosec: Installed as a Go-based mise tool for local and CI security scans
- golangci-lint: Includes gosec and 70+ other linters
All security tools are version-controlled through mise.toml, ensuring consistent vulnerability detection across environments.
Tool Activation and Environment Management#
Automatic Activation#
Mise automatically activates tools when entering the project directory through:
- Shell Integration: Mise hooks into bash/zsh/fish to detect directory changes
- PATH Manipulation: Prepends mise-managed tool directories to PATH
- Shim System: Creates wrapper executables that route to the correct version
- Aggressive Mode:
activate_aggressive = trueensures activation in all contexts
mise exec Pattern#
The project standardizes on using mise exec for explicit tool invocations, particularly in the justfile:
# Example from justfile
mise_exec := "mise exec --"
install-security-tools:
@{{ mise_exec }} go install github.com/securego/gosec/v2/cmd/gosec@latest
This pattern ensures:
- Tools run with mise-managed versions
- Consistent behavior in different shell environments
- Explicit dependency on mise for clarity
CI/CD Integration#
GitHub Actions workflows use jdx/mise-action@v4.0.0 to set up the mise environment:
- uses: jdx/mise-action@v4.0.0
with:
install: true
cache: true
github_token: ${{ secrets.GITHUB_TOKEN }}
This action:
- Installs mise itself
- Runs
mise installto set up all tools - Caches mise installations for faster CI runs
- Authenticates with GitHub for rate limit increases
Note: As of v4.0.0, mise-action requires Node.js 24 (updated from Node.js 20 in v3.x). Teams managing their own GitHub Actions runner environments must ensure Node.js 24 is available.
Environment Caching and Performance#
The project leverages multiple caching mechanisms for faster builds and installations:
- mise env_cache:
env_cache = truecaches environment variables to reduce shell activation overhead - GitHub Actions Cache: The
jdx/mise-action@v4.0.0withcache: truecaches mise tool installations across CI runs - mise.lock:
lockfile = truegenerates mise.lock to lock exact tool versions and checksums - Go Module Cache: go.mod and go.sum ensure reproducible dependency resolution
- Pre-built Binaries: Mise downloads pre-compiled binaries when available, avoiding build overhead
Idiomatic Version Files#
The project uses idiomatic version files to pin specific language runtime versions, enabled by idiomatic_version_file_enable_tools = ["python", "go", "bun"]:
-
.go-version: Specifies
1.26.0, ensuring consistent Go version across environments. This takes precedence over thego = "1.26.0"declaration in mise.toml and works in conjunction with go.mod'stoolchain go1.26.0directive. -
.python-version: Pins Python to
3.14.3, overridingpython = "latest"in mise.toml. This enables automatic security updates via "latest" while maintaining reproducibility through the version file. -
.bun-version: Pins Bun JavaScript runtime to
1.3.10, configured as the npm package manager via[settings.npm] package_manager = "bun".
This dual-source versioning strategy balances flexibility and reproducibility:
- mise.toml declares "latest" for tools to receive automatic updates
- Idiomatic version files pin specific versions that have been tested and verified
- Version files take precedence, allowing controlled updates by modifying a single file
This approach contrasts with the earlier strategy of explicitly declaring all versions in mise.toml, providing easier version updates while maintaining the benefits of pinned versions.
Package Manager Integrations#
pipx with uvx#
The configuration uses pipx-based tool installation with uvx arguments for Python tools, enabled by [settings.pipx] uvx = true:
[tools."pipx:mdformat"]
version = "0.7.21"
uvx_args = "--with mdformat-gfm --with mdformat-front-matters ..."
Key features:
- uvx (uv-based pipx): Uses the uv tool as a faster alternative to traditional pipx
--with: Installs additional packages alongside the main tool- Python version comes from .python-version (3.14.3)
This pattern allows complex Python tooling with multiple extensions to be declaratively managed in mise.toml.
npm and bun#
The project configures Bun as the npm package manager via [settings.npm] package_manager = "bun", with version pinned in .bun-version (1.3.10).
While the project is primarily Go-based, Bun provides fast JavaScript execution for tools like markdownlint-cli2, which is installed directly through mise without requiring npm configuration.
Known Limitations and Workarounds#
cyclonedx-gomod: Mise Shim Trust Issues#
The most significant limitation is documented in the mise.toml comment:
# cyclonedx-gomod: installed via go install in CI (mise shim causes trust issues in goreleaser subprocesses)
Problem: GoReleaser invokes cyclonedx-gomod as a subprocess for SBOM generation. Mise's shim wrappers create execution or permission issues in this subprocess context.
Root Cause: Mise uses shim executables that wrap managed tools. When GoReleaser spawns subprocesses expecting direct binary execution, these shims cause:
- Path resolution failures
- Environment isolation issues
- Trust/permission errors
Solution: Install cyclonedx-gomod via go install into the standard Go bin directory:
# From justfile
install-security-tools:
@{{ mise_exec }} go install github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@latest
This uses mise-managed Go to install cyclonedx-gomod, but places it in ~/go/bin (standard PATH) rather than under mise's shim system.
Alternative in CI: CI workflows use the CycloneDX GitHub Action instead:
- name: Generate SBOM from binary (CycloneDX)
uses: CycloneDX/gh-gomod-generate-sbom@efc74245d6802c8cefd925620515442756c70d8f # v2
This completely bypasses both mise and local installation, using a containerized action instead.
Tools Not Suitable for Mise#
Based on the cyclonedx-gomod experience, tools that should not be managed by mise include:
- Subprocess-invoked tools: Any tool called as a subprocess by build systems (GoReleaser, Make, etc.)
- Trust-sensitive executables: Tools that perform signing or security-critical operations may fail with shim wrappers
- Path-dependent binaries: Tools that expect to be in specific system locations
For these cases, use go install, cargo install, or system package managers instead.
gosec Installation Pattern#
Similarly, gosec is installed via go install despite being defined in mise.toml. The mise.toml entry go:github.com/securego/gosec/v2/cmd/gosec declares the tool for discoverability, but installation uses go install to avoid shim issues when gosec is invoked by golangci-lint subprocesses.
Usage Examples#
Initial Setup#
# Install mise (if not already installed)
curl https://mise.run | sh
# Clone repository
git clone https://github.com/EvilBit-Labs/opnDossier.git
cd opnDossier
# Install all tools from mise.toml
mise install
# Install additional security tools (cyclonedx-gomod, gosec)
just install-security-tools
# Set up pre-commit hooks
just install
Verifying Tool Versions#
# Check all installed tools
mise list
# Check specific tool version
mise current go
# Output: 1.26.0
# Show all available tools and versions
mise ls-remote go
Running Tools#
# Direct invocation (uses mise-managed versions via PATH/shims)
go version
golangci-lint run
just build
# Explicit mise exec (ensures mise environment)
mise exec -- go build
mise exec -- goreleaser check
Updating Tools#
# Update a specific tool
mise use go@1.27.0
# Update all "latest" tools
mise upgrade
# Pin current "latest" versions to specific versions
mise use goreleaser@$(mise current goreleaser)
CI/CD Workflow Example#
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: jdx/mise-action@v4.0.0
with:
install: true
cache: true
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Build
run: just build
- name: Test
run: just test
Relevant Code Files#
| File Path | Description | Lines | Link |
|---|---|---|---|
mise.toml | Main mise configuration defining 18 tools and settings | ~50 | View |
mise.lock | Lockfile with pinned tool versions and checksums | ~70 | View |
.go-version | Idiomatic version file pinning Go to 1.26.0 | 1 | View |
.python-version | Idiomatic version file pinning Python to 3.14.3 | 1 | View |
.bun-version | Idiomatic version file pinning Bun to 1.3.10 | 1 | View |
justfile | Development task automation using mise-managed tools | 456 | View |
.github/workflows/ci.yml | CI pipeline using jdx/mise-action | ~200 | View |
.github/workflows/release.yml | Release workflow with mise-managed GoReleaser | ~150 | View |
.goreleaser.yaml | GoReleaser configuration invoking cyclonedx-gomod subprocess and go-licenses | 342 | View |
go.mod | Go module file with toolchain directive (go1.26.0) | ~50 | View |
RELEASING.md | Release documentation including tool installation | 128 | View |
THIRD_PARTY_NOTICES | Generated third-party license notices from go-licenses | 2449 | View |
Related Topics#
- GoReleaser: Multi-platform Go release automation
- Sigstore/Cosign: Keyless artifact signing
- SLSA: Supply chain security framework
- CycloneDX: Software Bill of Materials standard
- just: Command runner alternative to Make
- uv: Fast Python package installer
- GitHub Actions: CI/CD platform