v2026.9.13

jdx/misev2026.9.13Sep 24, 2026by mise-en-dev

Upgrading?

AI Summary

Tasks now export OpenTelemetry traces, and experimental daemon providers allow sharing PostgreSQL or NATS servers. The `mise backends switch` command allows manual migration of lockfiles when registries move tools.

Key Highlights

  • OpenTelemetry export for tasks (experimental).
  • Shared daemon providers for PostgreSQL, CockroachDB, and NATS.
  • `mise backends switch` command to handle registry backend moves.
  • Declarative dotfile removal via `mode = 'absent'`.

Breaking Changes

  • Experimental `pkgx:` backend is removed.
  • Locked tools keep their locked backend (no auto-switch).
  • TOML inline commands replace file tasks.
  • `mise generate tool-stub --lock` requires a project config.
  • Dotfiles `include`/`exclude` need current mise on every machine.

New Features

  • `mise search` checks package registries.
  • `mise ls` by backend with `-b` flag.
  • .miserc.local.toml for per-checkout early config.
  • `backend` and `install_path` available in `MISE_INSTALLED_TOOLS`.
  • Windows user service fixes for shell metacharacters.

Full Release Notes

`mise run` can now export OpenTelemetry traces and logs (experimental), and experimental daemon providers let several projects and worktrees share one PostgreSQL, CockroachDB, or NATS server, each with its own database or account. Lockfiles no longer switch backends on their own when the registry moves a tool: the new `mise backends switch` command does it when you ask. `[dotfiles]` and `[bootstrap.files]` can now remove files and manage permissions, and `mise bootstrap unapply` removes what a module set up. The experimental `pkgx:` backend has been removed.

## Highlights
- **Observability and shared services (experimental):** task runs export OTLP traces and, if you opt in, task output as logs. Global `[daemon_providers]` run long-lived servers, and projects attach to them with an isolated database or NATS account per checkout.
- **Safer lockfiles:** locked tools stay on their locked backend, `mise lock --bump` checks remote versions and fails when it can't, lockfiles no longer record versions that were never confirmed, and tool stubs lock into the project's `mise.lock`.
- **Declarative cleanup:** `mode = "absent"`, `remove_empty` templates, permissions-only entries, removal of empty directories mise created, and `mise bootstrap unapply` let a config describe what should *not* be on a machine.

## Added

### Tasks
- **OpenTelemetry export for `mise run` (experimental).** Each run becomes one trace, with a span per task (grouped by monorepo package) that carries its exit code and redacted args. W3C `TRACEPARENT` is read from the environment and passed to each task, so nested `mise run` calls and instrumented tools appear in the same trace. Nothing is exported unless `otel.enabled = true` **and** an OTLP endpoint is set. Offline mode disables export, and each export times out after 3s by default. A separate `otel.logs = true` setting exports task stdout (INFO) and stderr (WARN) as log records linked to their spans, with redactions applied first. With `otel.logs` on, tasks in `interleave`/`quiet` modes no longer get a TTY; use `--raw` for tasks that need one. [#13557](https://github.com/jdx/mise/pull/13557), [#13558](https://github.com/jdx/mise/pull/13558), [#13559](https://github.com/jdx/mise/pull/13559) (built on work by @MatthiasGrandl and @zeitlinger)

  ```toml
  [settings]
  otel.enabled = true
  otel.logs = true   # optional; exports task output too
  ```
  ```sh
  export OTEL_EXPORTER_OTLP_ENDPOINT=<your OTLP/HTTP collector URL>
  mise run build ::: test
  ```

### Daemons (experimental)
- **Shared server providers.** Declare long-lived PostgreSQL, CockroachDB, or NATS servers in global config under `[daemon_providers]` and manage them with `mise daemons providers ls|start|stop|restart`. Providers have their own tools, ports, and persistent data. They run in an isolated environment and are not tied to any checkout. [#13534](https://github.com/jdx/mise/pull/13534)
- **Per-checkout databases and accounts on a shared server.** A project daemon with `provider = "..."` gets its own database (PostgreSQL/CockroachDB) or its own NATS account with separate subjects and JetStream data. Each checkout path gets a stable name, so worktrees share the server but not the data. Give several daemons the same `resource` name to share data on purpose. Connection env vars point at the right database, and NATS gets an authenticated `NATS_URL`. [#13536](https://github.com/jdx/mise/pull/13536), [#13537](https://github.com/jdx/mise/pull/13537)

  ```toml
  # ~/.config/mise/config.toml
  [daemon_providers.local-postgres]
  preset = "postgres"
  version = "18"
  port = "auto"

  # project mise.toml
  [daemons.db]
  provider = "local-postgres"
  # resource = "shared_app"   # opt in to sharing data
  ```

### Lockfiles and backends
- **`mise backends switch`.** When the registry moves a tool to a new backend (as happened with hk and communique moving to `packslip:`), a tool locked to the old backend now stays there. `mise install` and `mise lock` print a warning that points to the new command, which moves lock entries to the registry's backend at the same versions, relocks their platforms, and reinstalls. It supports `--dry-run`, `--global`, and `TOOL@VERSION`. If any relock fails, every lockfile it changed is restored. [#13543](https://github.com/jdx/mise/pull/13543)
- **Tool stubs lock into the project's `mise.lock`.** `mise generate tool-stub --lock` now records the stub in the nearest project lockfile (listed under `tool-stubs`), so installs verify the recorded checksums and `--locked`/`MISE_LOCKED=1` accept stubs. Previously the `[lock]` section written into the stub was never used, so checksums were never checked. [#13502](https://github.com/jdx/mise/pull/13502)
- **Install from a local archive.** The `http:` backend accepts `file://` URLs. It copies the archive instead of downloading it, still verifies `checksum`, and works offline. [#13574](https://github.com/jdx/mise/pull/13574)

  ```toml
  [tools]
  "http:my-tool" = { version = "1.0.0", url = "file:///opt/archives/my-tool-v1.0.0-linux-x64.tar.gz", checksum = "sha256:..." }
  ```
- **Checksum mismatch hints for re-uploaded GitHub assets.** When a `github:` or `aqua:` install fails a checksum check, mise asks GitHub for the asset's current digest. If that digest matches the download, the error says the maintainer probably re-uploaded the asset. The install still fails. [#13512](https://github.com/jdx/mise/pull/13512)
- **vfox `BackendUninstall` hook.** Backend plugins can define `hooks/backend_uninstall.lua` to clean up outside the install directory. It runs before removal on uninstall, upgrade, and prune. If the hook errors, the install directory is kept. [#13522](https://github.com/jdx/mise/pull/13522)

### CLI
- **`mise search` checks package registries.** Add a prefix to search npm, crates.io, RubyGems, or NuGet (`mise search npm:typescript-language`, `cargo:`, `gem:`, `dotnet:`). `--all` searches every source at once. Plain searches and shell completion still make no registry requests, and `MISE_OFFLINE=1` skips them. [#13550](https://github.com/jdx/mise/pull/13550)
- **`mise ls` by backend.** `-b/--backend` (repeatable) filters by backend and also works with `--json`. `--grouped` prints one section per backend. [#13530](https://github.com/jdx/mise/pull/13530)
- **Key completion for `mise config get`/`set`.** Tab completes dotted keys, with descriptions, from the schema and from the target file. `--file`, `--global`, and `--system` are respected. [#13551](https://github.com/jdx/mise/pull/13551)
- **Coloured help and a logo.** `mise --help` is now coloured on terminals (and respects `NO_COLOR`), wraps at the terminal's real width, and shows the mise logo on `mise`/`mise --help` when there's room. [#13449](https://github.com/jdx/mise/pull/13449)
- **Project URLs in the registry.** Registry entries can set a `url`, which appears in `mise tool` (and `mise tool <name> --url`) and in `mise registry --json`. [#13533](https://github.com/jdx/mise/pull/13533)

### Configuration and hooks
- **`.miserc.local.toml`.** Sets per-checkout early config, such as `env = ["native"]`, without editing the shared `.miserc.toml`. At each directory level it is read before `.miserc.toml`. CLI flags and `MISE_ENV` still take precedence. [#13440](https://github.com/jdx/mise/pull/13440)
- **`backend` and `install_path` in `MISE_INSTALLED_TOOLS`.** Postinstall hooks can now see where each tool came from and exactly where it was installed. [#13421](https://github.com/jdx/mise/pull/13421) (@garysassano)
- **A configured pnpm overrides Node's bundled pnpm**, whichever order the tools are listed in. [#13498](https://github.com/jdx/mise/pull/13498) (@EMcCormack)

### Dotfiles
- **Choose what a tracked directory saves.** `exclude` and `include` lists on `mode = "track"` entries. Exclusions always win. `include = []` selects nothing. Narrowing a list does not delete the files on other machines. [#13418](https://github.com/jdx/mise/pull/13418), [#13432](https://github.com/jdx/mise/pull/13432)

  ```toml
  [dotfiles]
  "~/.codex" = { mode = "track", include = ["config.toml", "rules/**"], exclude = ["*.log"] }
  ```
- **Preview before tracking.** `mise dot track --dry-run` and `mise dot paths --preview` show file counts, sizes, exclusions, and skipped nested repositories. Large trees get a warning. [#13417](https://github.com/jdx/mise/pull/13417)
- **`mode = "absent"`** removes a file or symlink at the target, with support for OS `variants`. Directories and special files are refused, even with `--force`. [#13513](https://github.com/jdx/mise/pull/13513)
- **`permissions` key.** Overrides the mode of copy, template, and content entries, or manages only the permissions of an existing file such as `~/.ssh/config`. Status, diff, and apply report and fix drift. The key is ignored on Windows. [#13514](https://github.com/jdx/mise/pull/13514)
- **`remove_empty = true` on templates** removes the target when the template renders empty. A file you have edited since mise last wrote it is kept unless you pass `--force`. [#13515](https://github.com/jdx/mise/pull/13515)
- **Empty parent directories mise created** are removed along with their target on apply and unapply. This only applies inside `$HOME` and never to directories that already existed. [#13518](https://github.com/jdx/mise/pull/13518)
- **Warnings from background captures**, such as credential-named files saved in plaintext, are now shown by the next `mise dot` command or `mise bootstrap`. Previously they only went to the watcher logs. [#13483](https://github.com/jdx/mise/pull/13483)

### Bootstrap
- **`mise bootstrap unapply <ENV>...`** removes the files, directories, user services, and dotfile entries a module added after you deselect it. Anything another environment still declares is kept. Supports `--dry-run` and `--force`. [#13441](https://github.com/jdx/mise/pull/13441)
- **Permissions-only `[bootstrap.files]` entries.** Declare only `mode`/`owner`/`group` to manage a file's metadata without taking over its contents. [#13511](https://github.com/jdx/mise/pull/13511)
- **`remove_empty = true`** on templated `[bootstrap.files]` removes the target when the template renders empty. [#13510](https://github.com/jdx/mise/pull/13510)
- **More systemd directives:** `before`, `binds_to`, `part_of`, `conflicts`, `exec_start_pre`, `exec_start_post`, and `exec_stop_post`. `~` now expands after exec prefixes such as `-~/bin/check`. [#13526](https://github.com/jdx/mise/pull/13526)

### Registry
- Added `mole` ([#13363](https://github.com/jdx/mise/pull/13363), @casparbreloh), `reviewdog` ([#13562](https://github.com/jdx/mise/pull/13562), @takumin), and `nim` ([#13461](https://github.com/jdx/mise/pull/13461), @elijahr). `aube` now points at `aubepkg/aube` ([#13541](https://github.com/jdx/mise/pull/13541)).

## Fixed

### Tools, installs, and lockfiles
- `mise cache prune` could delete files from installed tools: it followed symlinks out of the cache into install directories and left npm cache entries half-empty. It now never follows symlinks and removes stale entries as a whole. `cache_prune_age = "0s"` now also turns off `mise cache prune`. [#13424](https://github.com/jdx/mise/pull/13424)
- `mise lock --bump` now checks remote versions for every selector (for example `"6"`, not only `latest`) and fails when the version list can't be fetched, where it used to exit 0 with stale versions. Packslip registry tools no longer call `api.github.com` in normal use, which avoids rate-limit errors. [#13544](https://github.com/jdx/mise/pull/13544)
- `mise lock` refuses to record an aqua version that only resolved to its own request string because the version list failed to load. When such an install fails, the error now says why. [#13552](https://github.com/jdx/mise/pull/13552)
- `mise lock --global` no longer skips global tools that the project config shadows, and no longer overwrites a global pin with the project's version. [#13547](https://github.com/jdx/mise/pull/13547)
- `mise lock` no longer tries to lock `3.9.6~aube~<digest>`-style install directory names for npm and pipx tools. [#13542](https://github.com/jdx/mise/pull/13542)
- `mise upgrade --bump tool@selector` now saves the selector to the config, as `mise use` does. [#13179](https://github.com/jdx/mise/pull/13179) (@zeitlinger)
- npm packages whose lifecycle scripts call back into the package manager through `npm_execpath` (such as `re2`) now run through aube, not mise's task runner. [#13484](https://github.com/jdx/mise/pull/13484)
- Command wrappers such as `[wrappers.cargo] command = "mbx"` now install the missing provider tool before running it. [#13532](https://github.com/jdx/mise/pull/13532)
- A GitHub, GitLab, or Forgejo token containing a newline or other invalid header character now gives a redacted error, where mise used to crash. Tokens read from `*_tokens.toml` are trimmed. [#13488](https://github.com/jdx/mise/pull/13488)
- `.tar.zst` archives compressed with a long window now extract. [#13566](https://github.com/jdx/mise/pull/13566)
- aqua creates `.mise-bins` for registry files listed by name only ([#13525](https://github.com/jdx/mise/pull/13525)), and reports only the provenance and signature checks mise actually performs ([#13549](https://github.com/jdx/mise/pull/13549)).
- Renaming a raw Windows download with `bin` keeps the `.exe` extension. [#13529](https://github.com/jdx/mise/pull/13529)
- `brew-cask` percent-decodes artifact filenames taken from cask URLs. [#13431](https://github.com/jdx/mise/pull/13431)
- `mise self-update` fails before downloading when it can't write to the install directory. [#13453](https://github.com/jdx/mise/pull/13453)
- Per-tool progress bars line up in interactive installs. [#13494](https://github.com/jdx/mise/pull/13494)

### Tasks
- A metadata-only `[tasks.hello]` block no longer creates an empty task that hides `mise-tasks/hello.sh`. It now configures the script, and dependency groups keep their `depends` when another config layer adds metadata. [#13448](https://github.com/jdx/mise/pull/13448)
- A `run` under a file task's name now replaces the script. [#13458](https://github.com/jdx/mise/pull/13458) (see Breaking Changes)
- Dependencies that use optional usage flags or args in templates are no longer dropped when those values are omitted. [#13569](https://github.com/jdx/mise/pull/13569) (@nettlesh)
- When parallel tasks fail together, only the task that caused the stop prints its error chain. [#13556](https://github.com/jdx/mise/pull/13556)

### Shell, CLI, and platforms
- The bash `mise` function now embeds the path to the mise binary, so it keeps working in shells that copied the function but not `$__MISE_EXE` (for example Claude Code's Bash tool on Windows). [#13491](https://github.com/jdx/mise/pull/13491)
- Windows release builds no longer abort with "panic in a function that cannot unwind" when a vfox plugin hits a Lua error. [#13503](https://github.com/jdx/mise/pull/13503)
- `cargo install mise` for Windows targets works again. [#13573](https://github.com/jdx/mise/pull/13573)
- Help and completion output exit quietly when the reader closes the pipe. [#13575](https://github.com/jdx/mise/pull/13575), [#13527](https://github.com/jdx/mise/pull/13527)

### Dotfiles, history, and bootstrap
- On Windows, user services that set `environment` no longer run through `cmd.exe` behind a console window that killed the service when closed, and shell metacharacters in the environment are no longer rejected. The history watcher also runs without a console window. [#13429](https://github.com/jdx/mise/pull/13429), [#13428](https://github.com/jdx/mise/pull/13428)
- `mise bootstrap` now runs `[history.reload]` commands after its dotfiles phase writes matching files, as `mise dot apply` does. [#13509](https://github.com/jdx/mise/pull/13509)
- Nested Git repositories inside tracked directories are skipped and reported, not saved as commit pointers. Track the repository's root directly to capture its files. [#13416](https://github.com/jdx/mise/pull/13416)
- Global history exclusions apply consistently to tracked paths ([#13427](https://github.com/jdx/mise/pull/13427)), and files left out by credential filtering are reported ([#13415](https://github.com/jdx/mise/pull/13415)).
- A postgres daemon that would start as root now fails early with a clear error. [#13567](https://github.com/jdx/mise/pull/13567)

## Security
- When `[bootstrap.files]` and `[bootstrap.directories]` changes run as root, mise no longer follows a symlink in the path that another user could have planted (CWE-59). Status and dry-run show these paths as `unknown`, and apply refuses them. Symlinks inside root-owned directories that no one else can write, such as `/etc` on macOS, still work. [#13539](https://github.com/jdx/mise/pull/13539), [#13546](https://github.com/jdx/mise/pull/13546)

## Breaking Changes
- **The experimental `pkgx:` backend is removed.** Entries like `"pkgx:stedolan.github.io/jq"` no longer resolve; switch to the registry shorthand (`jq`) or `aqua:`/`github:`. Lockfiles with pkgx sections still load, and those sections are dropped the next time mise writes the file. The `pkgx` registry entry for the pkgx CLI itself is unchanged. [#13555](https://github.com/jdx/mise/pull/13555)
- **Locked tools keep their locked backend.** A short-name tool no longer follows the registry to a new backend if `mise.lock` records a different one. Run `mise backends switch` to move it. [#13543](https://github.com/jdx/mise/pull/13543)
- **TOML commands replace file tasks.** `[tasks."hello.sh"] run = ...` used to be ignored and now runs. `[tasks.hello] run = ...` no longer leaves `hello.sh` available as a separate task. To keep both, give the inline command its own name. [#13458](https://github.com/jdx/mise/pull/13458)
- **`mise generate tool-stub --lock` needs a project config above the stub.** It writes to that project's `mise.lock` and no longer pins the stub's `version`. Any old `[lock]` section is ignored and removed. Older mise releases drop `tool-stubs` from `mise.lock`. [#13502](https://github.com/jdx/mise/pull/13502)
- **Dotfiles `include`/`exclude` need current mise on every machine.** Upgrade every machine that shares the dotfiles setup before using `include` lists. New checkpoints use schema version 2, which older clients can't roll back. [#13432](https://github.com/jdx/mise/pull/13432)
- **Recursive `[bootstrap.directories]` removals** always run as root, so a path that crosses a symlink in a user-writable directory is now refused, even under `$HOME`. Declare the resolved path instead. [#13546](https://github.com/jdx/mise/pull/13546)

## New Contributors
* @EMcCormack made their first contribution in [#13498](https://github.com/jdx/mise/pull/13498)
* @elijahr made their first contribution in [#13461](https://github.com/jdx/mise/pull/13461)
* @takumin made their first contribution in [#13562](https://github.com/jdx/mise/pull/13562)

**Full Changelog**: https://github.com/jdx/mise/compare/v2026.9.12...v2026.9.13

## 💚 Sponsor mise

mise is built and maintained by [@jdx](https://github.com/jdx), an open source developer at [**entire.io**](https://entire.io/), the title sponsor of his open source work.

If mise saves you or your team time, please consider becoming an [individual or company sponsor](https://jdx.dev/sponsors.html). Your support funds ongoing development and helps keep mise fast, free, and independent.

Related Repositories