Skip to content

Releases: fastrevmd-lab/RustJunosMCP

v0.7.0 — candidate-safety tools + rmcp 2.0 / quick-xml 0.41

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 04 Jul 03:06
b6ae5e5

First tagged rust-junosmcp release since v0.6.3. Tool surface 15 → 17.

Added

  • commit_check_config (#95) — non-destructive commit check: loads a candidate, returns {success, diff, error?}, then discards it. Never activates config. Own token scope (least-privilege). 15 → 16.
  • discard_candidate (#107) — discard uncommitted candidate changes (rollback 0) to recover a candidate left dirty ("configuration database modified"). Never changes the running config. Own token scope. 16 → 17.
  • junos_config_diff (#108) — when the on-box config won't parse for the current mode (e.g. after a chassis-cluster change), the raw parse error now carries an actionable hint instead of leaving the caller blind.

Security

  • rmcp 0.8.5 → 2.0.0, closing RUSTSEC-2026-0189 (DNS rebinding in the Streamable HTTP transport). The transport now enforces a Host allowlist (default: loopback only). New flags --allowed-host <HOST> (repeatable) and --disable-host-check. Off-loopback deployments MUST pass --allowed-host for their LAN authority or clients receive HTTP 403.
  • quick-xml 0.36 → 0.41 (+ rustez 0.12.1 / rustnetconf 0.12.3), closing RUSTSEC-2026-0194 / RUSTSEC-2026-0195 (quick-xml DoS). JTAC-bundle redaction now suppresses quick-xml 0.41 GeneralRef entity events inside redacted elements — a bare version bump would have leaked entity fragments of secrets.

Full changelog: https://github.com/fastrevmd-lab/RustJunosMCP/blob/main/CHANGELOG.md
Diff: v0.6.3...v0.7.0

srxmcp-v0.3.0 — Phase 3 cluster-health + JTAC support-bundle

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 27 May 21:14
a9d4295

srxmcp-v0.3.0 — Phase 3

Two new MCP tools (surface 8 → 10):

  • validate_chassis_cluster_health — runs 8 cluster-scoped RPCs, applies 7 ordered checks (red_led, disabled_secondary, control_link_failure, major_alarm, minor_alarm, recent_reboot, version_skew), rolls up to a single pass | warn | fail verdict. Standalone devices short-circuit to SrxState::NotConfigured.

  • collect_jtac_support_bundle — per-router serialized via in-process Semaphore keyed on (router, "support_bundle"). problem_type=generic produces an on-device tarball (BundleLocation::Device); per-type paths capture baseline + per-type RPCs (deduped) and tar them on LXC (BundleLocation::LxcStaging) with SHA-256.

Six new typed errors with the locked [code=<snake>] router=<name>: <detail> convention.

Live-lab smokes (LXC 601:30032)

  • validate_chassis_cluster_health against vSRX-test19-20state=active, verdict=fail, surfaced real lab issues (Red LED, disabled RG members, control-link transitions).
  • validate_chassis_cluster_health against vSRX-test10state=not_configured (standalone short-circuit).
  • collect_jtac_support_bundle problem_type=generic against vSRX-test10BundleLocation::Device with next_step hint.
  • collect_jtac_support_bundle problem_type=[routing] against vSRX-test10BundleLocation::LxcStaging, 6.8 KB tarball, sha256 verified, baseline + routing RPCs captured.

Deploy footnote

/etc/systemd/system/rust-srxmcp.service ReadWritePaths= widened to include /var/lib/rust-srxmcp for the LXC staging dir (/var/lib/rust-srxmcp/staging/bundles/).

Known follow-ups (cosmetic / v0.3.1)

  • Tarball filename double-prefixes srxmcp- (request_id already has it): srxmcp-srxmcp-<uuid>.tgz.
  • Logs path stubbed in v0.3.0 (manifest records "not implemented in v0.3.0").
  • v0.3.1 plan: consolidate per-type path onto on-device request support information once per-RPC scoping is verified.

Verification

  • 644 workspace tests green (cargo fmt / build / clippy -D warnings / test)
  • Fixture-based tests caught and fixed a real parser bug in check_control_link_failure (<transition-reason> vs <redundancy-group-transition-reason>)

PR: #78 · Commit: a9d4295

rust-srxmcp v0.2.1 — Phase 2 AppID signature-package lifecycle

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 26 May 19:54
a11201a

What's new

manage_appid_signature_package — full Application Identification signature-package lifecycle on Juniper SRX, sibling of manage_idp_security_package shipped in v0.2.0:

  • check_server — query installed + latest application-package version from signatures.juniper.net
  • download_and_install — confirmation-gated, supports explicit version pinning, downloads and installs the latest (or pinned) AppID package + protocol bundle
  • uninstall — confirmation-gated removal of the currently-installed application package
  • Cluster-aware: synchronizes both nodes on chassis-cluster devices
  • Two-call confirmation protocol + per-router transfer locks (reused from v0.2.0 IDP primitives)

RPC contract (live-captured against vSRX-test3, Junos 24.4R1)

The Phase 2 design doc's AppID RPC shapes were a best-guess from CLI namespaces; v0.2.1 corrects them against the live wire format:

  • All AppID RPCs are flat single-element (no composite parent + child like IDP).
  • Names use the request-appid-application-package-* prefix (NOT request-services-application-identification-*, which does not exist as an RPC).
  • Check-server envelope is <apppack-server-status> with <apppack-server-status-detail>, distinct from the <apppack-download-status> envelope used by the download workflow.
  • Async-status responses use plain-English tokens (Downloaded/Installed/Uninstalled for success; substring failed for failure) — NOT IDP's Done;/Failed; markers.
  • get-appid-package-version reports <version-detail>0</...> post-uninstall on Junos 24.4R1 — normalize_version_text treats "0", "", and "N/A" as equivalent absence markers.

Validation

5/5 destructive live smokes pass on vSRX-test3 against LXC 601:30032:

  • appid_check_server_returns_latest_version
  • appid_download_and_install_call1_returns_plan
  • appid_uninstall_call1_returns_plan
  • appid_uninstall_call2_succeeds ✅ (real destructive uninstall — confirmed package 3910 removed)
  • appid_cluster_install_syncs_both_nodes ✅ (graceful-degrade — accepts license_inactive until lab heals)

Lab gaps (documented, not blocking)

  • vSRX-test3 cannot reach signatures.juniper.net from the homelab; check_server and the destructive download path emit signatures_server_unreachable until egress is fixed. Smokes graceful-degrade to accept that error.
  • The cluster smoke (vSRX-test19-20) requires a clustered+AppID-licensed pair the lab does not currently have; the smoke accepts a license_inactive or transport error in the interim.

Tool surface

8 srxmcp tools total (up from 7 in v0.2.0):

  • srxmcp_status, check_srx_feature_license, vpn_lifecycle_report, get_chassis_cluster_status, get_srx_security_services_status
  • manage_idp_security_package (v0.2.0)
  • manage_appid_signature_package (new in this release)

Deployed to LXC 601:30032.

rust-srxmcp v0.2.0 — Phase 2 IDP signature-package lifecycle

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 26 May 17:44
f6bd675

What's new

manage_idp_security_package — full IDP signature-package lifecycle on Juniper SRX:

  • check_server — query latest available signature version from signatures.juniper.net
  • download_and_install — confirmation-gated, idempotent (already_at_target short-circuit), supports explicit version pinning
  • rollback — restores previously installed signature version
  • Cluster-aware: synchronizes both nodes on chassis-cluster devices

Fixes shipped with this release (#73)

  • IDP composite RPC shapescheck-server, download-status, and install-status are composite XML (parent + empty child), not flat hyphenated names. Same failure mode as Phase 1B #68.
  • Junos 24.4R0 license-schema tolerance — parser now accepts both legacy (<licenses-installed>/<licenses-used>/<licenses-needed>/<license-type>) and 24.4R0+ live (<licensed>/<used-licensed>/<needed>/<validity-type>) element names. Previously, counts read as zero on 24.4R0 devices, tripping the preflight defence-in-depth check on devices that clearly had the license installed.

Validation

5/7 destructive live smokes pass on vSRX-test3 against LXC 601:30032:

  • idp_check_server_returns_latest_version
  • idp_download_and_install_call1_returns_plan
  • idp_download_and_install_call2_succeeds ✅ (237s — real ~300 MB pull from signatures.juniper.net)
  • idp_already_at_target_short_circuits
  • idp_version_pin_accepts_explicit
  • idp_rollback_after_install_restores_previous — lab precondition (no prior IDP package on test3)
  • idp_cluster_install_syncs_both_nodes — known lab gap (no IDP-licensed cluster pair)

Tool surface

7 srxmcp tools total (up from 6 in v0.1.2):

  • srxmcp_status, check_srx_feature_license, vpn_lifecycle_report, get_chassis_cluster_status, get_srx_security_services_status
  • manage_idp_security_package (new in this release)

Deployed to LXC 601:30032.

v0.5.8 — upgrade_junos self-deadlock fix

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 19 May 15:40
2f978d0

What changed

  • fix(upgrade_junos): remove self-deadlocking transfer-lock acquire in run() (#52 / #51)

Pre-v0.5.8, every real call to upgrade_junos with confirm=true hung indefinitely at Phase 2 (transfer) the moment preflight returned Proceed. Root cause: run() acquired the per-router transfer permit, then transfer_file::handle() tried to re-acquire the same permit on the same task — a self-deadlock.

The fix removes the redundant early acquire in run(). transfer_file::handle() is the sole owner of the per-router transfer-lock for the actual SCP critical section.

A same_task_reacquire_deadlocks regression test was added to lock in the expected (deadlock-producing) shape of TransferLocks.

Verification

  • Live-tested on vSRX-test16 (2026-05-19): lock_acquire_prelock_acquire_post completes in microseconds; full Phase 2 transfer flow proceeds normally.
  • cargo test --workspace --release: 444 passed, 0 failed.
  • cargo audit: 0 vulnerabilities.

Compatibility

  • No API surface changes vs v0.5.7.
  • No new env vars or config fields.
  • Wire-protocol compatible.

v0.4.1 — security & hardening

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 15 May 19:18
0172fb6

Security + hardening release. No tool API changes; one server-side response-header change for unauthenticated requests, plus two new response fields on list_staged_files.

Security

  • RFC 6750 bearer challenges on every 401 — the streamable-HTTP endpoint now always returns a WWW-Authenticate: Bearer ... header on 401 Unauthorized. Wrong-token rejections include error=\"invalid_token\" per RFC 6750 §3.1 so clients can distinguish bearer rejection from an OAuth-discovery prompt. (#27, #28)
  • transfer_file source-path allowlist tightened — now restricts to [A-Za-z0-9._-], rejecting NUL bytes, ASCII control chars, shell metacharacters, RTL overrides, and homoglyph scripts that the prior `..` + slash + 255-byte checks missed. (#26 L2, #30)
  • scp stderr scrubbed in ScpFailed errors — absolute paths and IPv4 addresses are redacted to `` / `` before reaching the MCP caller. (#26 L1, #31)

Reliability

  • list_staged_files capped at 256 entries — caps at `STAGING_DIR_MAX_ENTRIES = 256`, deterministic name-sorted truncation, skips sha256 on excess files. Response gains `staged_files_truncated: bool` and `staged_files_total_found: usize`. (#26 L5, #32)
  • Per-router serialization for transfer_file — new `TransferLocks` process-wide map of `Arc<Semaphore(1)>` keyed by router. Different routers proceed in parallel; same router serializes. (#26 L4, #33)

Operability

  • Actionable EACCES message on tokens.json — surfaces file owner uid + mode and running process uid plus a `sudo -u ` / `chown` hint when the daemon can't read the tokens file. README gained a service-user note in the "Mint a token" section. (#22, #23, #29)

Compatibility

  • No tool name / argument changes.
  • `list_staged_files` response gains two new fields (`staged_files_truncated`, `staged_files_total_found`). Existing fields unchanged.
  • `401 Unauthorized` responses now always include `WWW-Authenticate: Bearer ...`. Clients that ignore this header are unaffected.

See CHANGELOG.md for the full entry.

v0.2.2 — add_device + reload_devices (full upstream parity)

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 06 May 15:40
ed001a1

Sub-project #4 PR #7 / 2 — closes the upstream parity gap. Tool surface now matches Juniper/junos-mcp-server exactly (11 tools).

Highlights

  • add_device — add a Junos device to the in-memory inventory and persist to devices.json. Atomic write (tempfile::NamedTempFile::persist + fsync), preserves _blocklist_defaults, per-device blocklist, and any other top-level keys via serde_json::Value round-trip with preserve_order. SHA-256 TOCTOU guard rejects calls that race with external edits.
  • reload_devices — re-read the current --device-mapping (no args) or swap to a new inventory file (file_name). Reports added / removed / changed device names plus previous_router_count, new_router_count, and inventory_path.
  • rmcp elicitation pass-through with structured args fallback. Missing required fields surface as JmcpError::MissingArguments.

Architecture notes

  • DeviceManager switched to Arc<ArcSwap<Inventory>> for lock-free hot-swap. Per-request reads still snapshot at handler entry. Writes serialized through a tokio::sync::Mutex on DeviceManager.inventory_write_lock.
  • JmcpHandler no longer holds a stale Arc<Inventory> snapshot field; get_router_list reads live from DeviceManager.inventory() so post-mutation router list reflects reality immediately.
  • SIGHUP now reloads inventory in addition to the token store. Inventory reload runs first; the token-store reload then refreshes its known router list from DeviceManager.inventory().names().
  • KNOWN_TOOLS (rust-junosmcp-auth) extended with "add_device" and "reload_devices" — 11 entries.

New CLI flags

  • --inventory-readonly — reject add_device and reload_devices unconditionally (independent of token scopes).
  • --allow-password-auth-add — permit auth.type=password in add_device. Off by default. Mutually exclusive with --inventory-readonly.

Documented sharp edge

add_device does not modify the token store. If a token has --routers 'edge-*' and you add core-3, the existing token will not see the new router. Mint a new token or rotate scopes after add_device.

Gating order (unchanged)

transport → AuthLayer → CallerCtx → tool scope → router scope → blocklist. The two new tools are tool-scope-gated only — they don't take a router_name argument.

Install

cargo install --git https://github.com/fastrevmd-lab/RustJunosMCP --tag v0.2.2

Or build from source — see README.md. LXC tarball is rust-junosmcp_0.2.2_amd64.tar.gz.

Plan & spec

  • Spec: docs/superpowers/specs/2026-05-05-templates-inventory-design.md
  • Plan: docs/superpowers/plans/2026-05-05-inventory-mutation.md (16 tasks, all complete)
  • PR: #7

Diff

v0.2.1...v0.2.2. 244 tests passing / 9 ignored (real-device, gated on JMCP_TEST_HOST/USER/PASS).

v0.2.1 — PFE + batch tools

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 06 May 01:00

Sub-project #3 follow-up to v0.2.0. Two new MCP tools and an independent PFE blocklist list. Stdio + remote transport + auth paths unchanged from v0.2.0.

Highlights

  • execute_junos_pfe_command — single PFE-shell call against an explicit FPC target. Wraps as request pfe execute target <fpc> command "<cmd>". Rejects literal " in the input (JmcpError::BadPfeCommand).
  • execute_junos_command_batch — N routers x M operational CLI commands. Parallel across routers (cap = max_concurrent_routers, default 16) via tokio::sync::Semaphore + tokio::task::JoinSet. Sequential per router. Per-command command_timeout (default 360s) and optional whole-batch batch_timeout. Continue-on-error after pre-flight; per-router error rows preserve commands.len() invariant. Result order matches input.
  • Independent pfe_commands blocklist list — under _blocklist_defaults and per-device blocklist. A deny on commands does NOT gate PFE and vice versa. See devices-template.json.

Architecture notes

  • BatchRunner / RouterSession traits (via async-trait) provide a testing seam — production runner wraps rustez::Device; tests use a stub.
  • batch_timeout wraps the JoinSet collect future and synthesizes "batch timeout" rows for routers that didn't report.
  • KNOWN_TOOLS (rust-junosmcp-auth) extended with the two new names. Token files referencing them load only on a 0.2.1+ binary; older binaries reject the file at parse time.

Gating order (unchanged from v0.2.0)

transport → AuthLayer → CallerCtx → tool scope → router scope → blocklist. For batch, router-scope is enforced in a for loop with first-failure short-circuit BEFORE any device I/O.

Install

cargo install --git https://github.com/fastrevmd-lab/RustJunosMCP --tag v0.2.1

Or build from source — see README.md. Docker tag is now 0.2; LXC tarball is rust-junosmcp_0.2.1_amd64.tar.gz.

Plan & spec

  • Spec: docs/superpowers/specs/2026-05-05-pfe-batch-design.md
  • Plan: docs/superpowers/plans/2026-05-05-pfe-batch.md (17 tasks, all complete)
  • PR: #5

Diff

v0.2.0...v0.2.1. 177 tests passing / 7 ignored (real-device, gated on JMCP_TEST_HOST/USER/PASS and JMCP_TEST_FPC).

v0.2.0 — remote transport + bearer-token auth

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 05 May 19:56

First public release with remote MCP transport. v0.1 features remain unchanged on the stdio path.

Highlights

  • Streamable-HTTP transport (rmcp 0.8.5) — --transport streamable-http -H <host> -p <port>
  • Bearer-token auth with per-token router/tool scopes — token add/list/revoke/rotate subcommand
  • Optional rustls TLS (default-on tls feature, ring crypto provider, no aws-lc-rs)
  • SIGHUP hot-reload of the token store via Arc<ArcSwap<TokenStore>>
  • CLI refusal matrix — refuses --allow-no-auth off-loopback; refuses plain HTTP off-loopback without --allow-insecure-bind
  • Stdio path is bit-for-bit unchanged from v0.1

Security

  • subtle::ConstantTimeEq for hash verify
  • Token plaintext shown exactly once on token add/rotate; never logged, no Debug impl on Secret
  • Atomic TokenStoreFile::save via NamedTempFile::persist — crash during rotate cannot lose tokens
  • Blocklist guardrails (v0.1.x) still gate every tool call regardless of token scope

Install

cargo install --git https://github.com/fastrevmd-lab/RustJunosMCP --tag v0.2.0

Or build from source — see README.md. Docker image and LXC tarball follow the same workflow as v0.1, with version tags now 0.2.

Operator quick-start

# Mint a token
cargo run -- token add --tokens-file tokens.json --name ops \
  --routers '*' --tools execute_junos_command,gather_device_facts

# Run with auth on loopback
cargo run -- --device-mapping devices.json --transport streamable-http \
  -H 127.0.0.1 -p 8765 --tokens-file tokens.json

# Hot-reload after rotate (sends SIGHUP automatically)
cargo run -- token rotate --tokens-file tokens.json --name ops --server-pid <pid>

See README's "Remote transport + auth (v0.2)" section for the full refusal matrix and TLS setup.

Plan & spec

  • Spec: docs/superpowers/specs/2026-05-05-remote-transport-auth-design.md
  • Plan: docs/superpowers/plans/2026-05-05-remote-transport-auth.md (16 tasks, all complete)
  • PR: #4

Diff

v0.1.0...v0.2.0: 27 files changed, +7227 / -67. 145 tests passing.