Skip to content

feat(registry): add --security flag to include security info in JSON output#9364

Merged
jdx merged 4 commits intomainfrom
registry-security-field
Apr 24, 2026
Merged

feat(registry): add --security flag to include security info in JSON output#9364
jdx merged 4 commits intomainfrom
registry-security-field

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Apr 24, 2026

Summary

  • Adds a --security flag to mise registry (JSON-only, enforced via requires = "json") that includes a security array for each tool.
  • Each tool's security features are collected across all of its backends via backend.security_info().await, then de-duplicated by variant + payload.
  • New field is omitted (skip_serializing_if = "Vec::is_empty") when empty, so the default --json output is byte-identical for tools without security info.

Why

Consumers that want a per-tool security catalog currently have to shell out to mise tool <name> --json once per tool because mise registry --json doesn't expose security. That's ~1000 process spawns per rebuild for e.g. https://github.com/jdx/mise-versions. Having it in the registry JSON collapses that to a single call.

Why opt-in

security_info() for aqua backends reads the per-package aqua registry, which can fetch from GitHub on cold cache. Keeping --security opt-in preserves the default mise registry --json path at its current ~50 ms so existing consumers aren't slowed down.

Local timing (977 tools, warm cache, no rate limit):

  • mise registry --json — ~50 ms (unchanged)
  • mise registry --json --security — ~30 s

Test plan

  • cargo check / cargo clippy --bin mise --no-deps --tests clean
  • cargo fmt --check clean
  • Added e2e assertions in e2e/cli/test_registry:
    • mise registry bun --json --security has a security field
    • mise registry bun --json does not
  • Regenerated mise.usage.kdl + docs/cli/registry.md
  • Manual: target/debug/mise registry --json --security produces ~436/1003 tools with a populated security array on my machine

Notes

  • to_output and display_json become async since security_info() is async; single-name lookup and bulk listing both thread through the same path.
  • De-duplication is structural (same variant + same payload) so e.g. two backends both offering Checksum { algorithm: Some("sha256") } collapse to one entry.

Note

Medium Risk
Adds an opt-in code path that performs per-backend async security_info() resolution (potentially network-bound) and parallelizes it, which could impact performance and error behavior when enabled.

Overview
Adds a new mise registry --security flag (requires --json) that includes a per-tool security array in the JSON output, populated by collecting and de-duplicating SecurityFeature values across all of a tool’s backends.

To support this, registry JSON rendering is made async and, when --security is set, resolves security info concurrently (bounded by Settings::jobs). Documentation/man pages/completions and e2e coverage are updated accordingly, and SecurityFeature now derives PartialEq/Eq to enable structural de-duplication.

Reviewed by Cursor Bugbot for commit 2944156. Bugbot is set up for automated code reviews on this repo. Configure here.

…output

Consumers that want to catalog per-tool security features (e.g. the
mise-versions sync pipeline) currently have to shell out to
`mise tool <name> --json` once per tool because `mise registry --json`
omits the `security` field. At ~1000 tools that's ~1000 mise process
spawns.

Add a `--security` flag (JSON-only, via `requires = "json"`) that
resolves each backend's `security_info()` and emits the de-duplicated
set of `SecurityFeature` values under a new `security` array in the
output. The field is omitted via `skip_serializing_if = "Vec::is_empty"`
when there are no features, so tools without security info produce the
same bytes as before.

The flag is opt-in because `security_info()` for aqua backends reads
the per-package aqua registry, which may fetch from GitHub on cold
cache — keeping the default `--json` path at its current ~50 ms.

Includes updated e2e tests, regenerated usage spec, and regenerated
docs.
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 24, 2026

Greptile Summary

Adds --security (requires --json) to mise registry, fetching security_info() across all backends per tool and de-duplicating features by structural equality. The default --json output is unchanged — the security field is omitted when empty via skip_serializing_if. Both previously flagged issues (wrong backend lookup via BackendArg::new and sequential iteration) have been resolved: collect_security now uses BackendArg::from(full) with a fresh arg_to_backend call per backend, and the --security path parallelises work across tools with a JoinSet gated by a semaphore sized to Settings::get().jobs.

Confidence Score: 5/5

Safe to merge — changes are additive, backward-compatible, and well-tested.

All P1 issues from prior review rounds have been addressed. The backend lookup correctly uses BackendArg::from(full) avoiding the TOOLS-cache collision. The JoinSet+semaphore pattern correctly parallelises security fetches. The default JSON output path is untouched. No remaining blocking findings.

No files require special attention.

Important Files Changed

Filename Overview
src/cli/registry.rs Core implementation: adds --security flag with JoinSet-based parallelism, semaphore throttling, correct BackendArg::from() lookup, and deduplication via PartialEq. Logic is sound.
src/backend/mod.rs Adds PartialEq + Eq derives to SecurityFeature, required for the contains() dedup call in collect_security. Minimal, correct change.
e2e/cli/test_registry Adds two e2e assertions: security field present with --security, absent without. jq expressions are correct given skip_serializing_if = "Vec::is_empty".
docs/cli/registry.md Documentation updated with --security flag description. Accurate and consistent with implementation.
mise.usage.kdl KDL completions/usage updated with --security flag definition and long_help text.
man/man1/mise.1 Man page updated with --security flag documentation. Generated file, looks correct.
xtasks/fig/src/mise.ts Fig completions spec updated with --security option entry. Correct and complete.

Sequence Diagram

sequenceDiagram
    participant CLI as mise registry --json --security
    participant DJ as display_json()
    participant JS as JoinSet (N=jobs)
    participant TO as to_output(tool, true)
    participant CS as collect_security(backends)
    participant BE as backend::arg_to_backend

    CLI->>DJ: display_json().await
    DJ->>DJ: collect filtered RegistryToolOutputArgs
    loop for each tool (semaphore-gated)
        DJ->>JS: jset.spawn(to_output(tool, true))
    end
    loop join_next until empty
        JS->>TO: await
        TO->>CS: collect_security(&backends)
        loop for each backend string
            CS->>BE: BackendArg::from(full) → arg_to_backend
            BE-->>CS: ABackend
            CS->>BE: backend.security_info().await
            BE-->>CS: Vec<SecurityFeature>
        end
        CS-->>TO: deduplicated Vec<SecurityFeature>
        TO-->>JS: RegistryToolOutput
        JS-->>DJ: result
    end
    DJ->>DJ: sort outputs by short name
    DJ-->>CLI: JSON printed
Loading

Reviews (4): Last reviewed commit: "fix(registry): limit security metadata c..." | Re-trigger Greptile

Comment thread src/cli/registry.rs Outdated
Comment thread src/cli/registry.rs
Comment thread src/cli/registry.rs
Comment thread src/cli/registry.rs
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new --security flag to the registry command, enabling the inclusion of security features for tool backends in JSON output. The implementation includes documentation updates, E2E tests, and logic to aggregate and de-duplicate security features across multiple backends. Review feedback identifies a logic error in collect_security where a global cache prevents correct data retrieval for tools with multiple backends, and suggests using backend::arg_to_backend instead. Additionally, there are recommendations to parallelize the sequential tool processing to improve performance and to correct a misleading comment regarding lock management.

Comment thread src/cli/registry.rs Outdated
Comment on lines +181 to +184
let ba = BackendArg::new(short.to_string(), Some(full.to_string()));
let Some(backend) = backend::get(&ba) else {
continue;
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There is a logic error when a tool has multiple backends. backend::get(&ba) uses a global cache keyed by the shorthand name (ba.short). If a tool like gh has both aqua and asdf backends, the first one encountered will be cached, and backend::get will return that same cached backend for all subsequent iterations of the loop. This causes security info to be collected from the same backend multiple times while skipping others. Using backend::arg_to_backend directly avoids this caching issue.

Suggested change
let ba = BackendArg::new(short.to_string(), Some(full.to_string()));
let Some(backend) = backend::get(&ba) else {
continue;
};
let ba = BackendArg::new(short.to_string(), Some(full.to_string()));
let Some(backend) = backend::arg_to_backend(ba) else {
continue;
};

Comment thread src/cli/registry.rs Outdated
Comment on lines +167 to +169
for (short, rt) in tools {
outputs.push(self.to_output(short, rt).await);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This loop processes ~1000 tools sequentially. When --security is enabled, each iteration calls to_output(...).await, which can involve network or file I/O (e.g., for aqua backends). As noted in the PR description, this results in a ~30s execution time. Consider parallelizing these requests using tokio::task::JoinSet or futures::future::join_all to significantly improve performance when the --security flag is used.

Comment thread src/cli/registry.rs Outdated
Comment on lines +159 to +160
// Collect (short, RegistryTool) pairs first so we can drop the
// REGISTRY lock borrow before hitting the async per-tool loop.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment about dropping the REGISTRY lock borrow is misleading because REGISTRY is a static Lazy<BTreeMap> and does not appear to be wrapped in a lock (like a Mutex or RwLock). The collect() call is likely unnecessary for safety if the loop remains sequential, as the references yielded by the iterator are 'static. However, it would be necessary if you decide to parallelize the loop as suggested.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 24, 2026

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.20 x -- echo 23.9 ± 0.6 22.9 26.5 1.00
mise x -- echo 24.1 ± 0.8 23.2 33.3 1.01 ± 0.04

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.20 env 23.0 ± 0.9 22.2 37.1 1.00
mise env 23.5 ± 0.5 22.8 27.8 1.03 ± 0.05

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.20 hook-env 23.6 ± 0.3 23.0 25.9 1.00
mise hook-env 24.1 ± 0.3 23.4 25.2 1.02 ± 0.02

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.20 ls 21.3 ± 0.7 20.7 32.1 1.00
mise ls 21.9 ± 0.3 21.3 23.2 1.03 ± 0.04

xtasks/test/perf

Command mise-2026.4.20 mise Variance
install (cached) 179ms 172ms +4%
ls (cached) 81ms 85ms -4%
bin-paths (cached) 87ms 87ms +0%
task-ls (cached) 816ms 806ms +1%

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7448fa9. Configure here.

Comment thread src/cli/registry.rs
@jdx jdx merged commit d7ab3bd into main Apr 24, 2026
37 checks passed
@jdx jdx deleted the registry-security-field branch April 24, 2026 19:40
mise-en-dev added a commit that referenced this pull request Apr 25, 2026
### 🚀 Features

- **(registry)** add --security flag to include security info in JSON
output by @jdx in [#9364](#9364)

### 🐛 Bug Fixes

- **(config)** limit resolved backend opts to aliases by @risu729 in
[#9315](#9315)
- **(docs)** stack banner message and link on mobile by @jdx in
[#9362](#9362)
- **(github)** prefer shortest asset name as tiebreaker in
auto-detection by @jdx in [#9361](#9361)
- **(java)** newer zulu versions use a different directory structure by
@roele in [#9365](#9365)
- **(prune)** respect tracked lockfiles by @jdx in
[#9373](#9373)
- **(task)** skip tool install for missing naked tasks by @jdx in
[#9374](#9374)
- **(trust)** add untrust command by @jdx in
[#9370](#9370)
- fix - flux-operator-mcp aqua path by @monotek in
[#9357](#9357)

### 📚 Documentation

- update ruby compile msg by @fladson in
[#9338](#9338)

### 📦️ Dependency Updates

- update ubuntu docker tag to v26 by @renovate[bot] in
[#9347](#9347)
- update ghcr.io/jdx/mise:deb docker digest to 1af5a69 by @renovate[bot]
in [#9352](#9352)
- update taiki-e/install-action digest to 787505c by @renovate[bot] in
[#9354](#9354)
- update ghcr.io/jdx/mise:rpm docker digest to 7015ff3 by @renovate[bot]
in [#9353](#9353)
- update ghcr.io/jdx/mise:copr docker digest to da63a0f by
@renovate[bot] in [#9351](#9351)
- update ghcr.io/jdx/mise:alpine docker digest to 461700f by
@renovate[bot] in [#9350](#9350)
- bump communique 1.0.3 → 1.0.4 by @jdx in
[#9378](#9378)

### 📦 Registry

- remove openshift-install by @jdx in
[#9372](#9372)
- remove go-sdk by @jdx in
[#9371](#9371)

### Chore

- **(npm-publish)** use aube publish instead of npm publish by @jdx in
[#9328](#9328)

### New Contributors

- @fladson made their first contribution in
[#9338](#9338)
jdx added a commit to jdx/mise-versions that referenced this pull request Apr 25, 2026
…urity

The `Sync tools to D1` step previously shelled out to `mise tool <name>
--json` once per tool (977 sequential invocations) just to assemble each
tool's GitHub slug, description, and security list. That dominated the
step at ~4 of its ~5 min wall time.

This PR collapses the whole metadata gather into one mise call:

- `mise registry --json --security` (added upstream in mise v2026.4.22
  via jdx/mise#9364) returns `{short, backends, description, aliases,
  security}` for every tool in one JSON array. No per-tool shell-outs,
  no parallel worker pool, no `MISE_TOOL_CONCURRENCY` env knob.

- Falls back to `mise registry --json` (without security) if the
  installed mise predates the flag, so the rest of the manifest still
  syncs.

Local timing on 978 tools: manifest phase ~30 s end-to-end, vs. ~240 s
before any of this work. Code is also dramatically simpler — drops the
`getSecurity` / `fetchSecurityMap` worker plumbing and the
`execFile` + `promisify` async machinery.

Bonus bug fix preserved from the prior version of this PR: the original
script read `description` from `mise tool --json`, which almost always
returned null. With `mise registry --json` providing descriptions, the
count jumps from ~17 tools-with-descriptions to ~975.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
jdx added a commit to jdx/mise-versions that referenced this pull request Apr 25, 2026
…urity

The `Sync tools to D1` step previously shelled out to `mise tool <name>
--json` once per tool (977 sequential invocations) just to assemble each
tool's GitHub slug, description, and security list. That dominated the
step at ~4 of its ~5 min wall time.

This PR collapses the whole metadata gather into one mise call:

- `mise registry --json --security` (added upstream in mise v2026.4.22
  via jdx/mise#9364) returns `{short, backends, description, aliases,
  security}` for every tool in one JSON array. No per-tool shell-outs,
  no parallel worker pool, no `MISE_TOOL_CONCURRENCY` env knob.

- Falls back to `mise registry --json` (without security) if the
  installed mise predates the flag, so the rest of the manifest still
  syncs.

Local timing on 978 tools: manifest phase ~30 s end-to-end, vs. ~240 s
before any of this work. Code is also dramatically simpler — drops the
`getSecurity` / `fetchSecurityMap` worker plumbing and the
`execFile` + `promisify` async machinery.

Bonus bug fix preserved from the prior version of this PR: the original
script read `description` from `mise tool --json`, which almost always
returned null. With `mise registry --json` providing descriptions, the
count jumps from ~17 tools-with-descriptions to ~975.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
jdx added a commit to jdx/mise-versions that referenced this pull request Apr 26, 2026
…urity (#130)

## Summary
- Replace 977 sequential `mise tool <name> --json` shell-outs (~4 min)
with a single `mise registry --json --security` call (~30 s end-to-end
for the entire manifest phase). The `--security` flag landed upstream in
mise v2026.4.22 via
[jdx/mise#9364](jdx/mise#9364) and returns
backends + description + security in one JSON array.
- Drop the now-unneeded `MISE_TOOL_CONCURRENCY` env knob, the
`getSecurity` / `fetchSecurityMap` worker pool, and the `execFile` +
`promisify` async plumbing.
- Add a graceful fallback to `mise registry --json` (no `--security`)
for any environment running pre-4.22 mise — the rest of the manifest
still syncs, just without security info.

## Why
The `Sync tools to D1` step ran for ~5 min every 30 min in the update
workflow. Profiling showed ~4 min was spent in 977 sequential mise
process spawns just to read each tool's `security` array (and a
`description` that was almost always `null` from `mise tool --json`).

mise v2026.4.22 exposes everything we need on `mise registry --json
--security`, so the script no longer has any reason to do per-tool work.

## Measurement
Local, 978 tools (current registry size):
- Before this branch: ~240 s in the manifest phase.
- After: ~30 s — the registry call itself is most of that time, file I/O
and TOML parsing are the remainder.

In CI the relative speedup will be even larger since the previous design
paid 977× the process-spawn + mise-startup cost.

## Bonus bug fix
The previous code read `description` from `mise tool --json`, which
almost always returns `null`. Pulling descriptions from `mise registry
--json` instead bumps tools-with-descriptions from **17** to **975**
locally — purely a side effect of using the right command.

## Test plan
- [x] `bun run test` — 42 tests pass
- [x] `prettier --check` + `tsc --noEmit` clean
- [x] Local dry run with v2026.4.22 mise: manifest builds in ~30 s, 845
tools with GitHub, 975 with description, 978 with backends, 184 with
package URLs
- [x] Local dry run with a wrapper that rejects `--security` (simulating
older mise): warning printed, fallback path runs, manifest still builds
- [ ] First post-merge scheduled run on `main` — verify D1 sync step
duration drops from ~5 min → well under 1 min, and `Upserted: ~977 /
Errors: 0` in the result

## Notes
- The workflow already installs latest mise via `curl https://mise.run |
sh` after the `mise-action` step, so CI will have v2026.4.22+ on every
run.
- Followups not in this PR: kill the second `mise ls-remote --json` call
in `update.sh`'s `generate_toml_file` (different rate-limit issue,
separate fix).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Moderate risk because it changes how tool metadata
(backends/description/security) is sourced and relies on `mise registry
--json --security` output/alias handling; failures would impact the D1
sync payload, though a fallback path is included for older `mise`.
> 
> **Overview**
> Speeds up the D1 tool-manifest build by replacing per-tool `mise tool
<name> --json` shell-outs with a single `mise registry --json
--security` call that provides `backends`, `description`, and `security`
for all tools at once.
> 
> Adds a compatibility fallback to `mise registry --json` when
`--security` is unsupported, and preserves prior alias resolution by
mapping registry `aliases` to the same metadata entry. Metadata
derivation is adjusted to pull GitHub/repo URLs from registry `backends`
(or `owner/repo` tool names) while keeping manual overrides as highest
priority.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
6e28e7d. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant