Skip to content

Releases: fastrevmd-lab/rustjunosmcp

v0.8.0 — unified Junos/SRX server and operational controls

Choose a tag to compare

@fastrevmd-lab fastrevmd-lab released this 17 Jul 16:55
521c2f7

v0.8.0 combines Junos and SRX into one server and adds the operational controls needed to run that endpoint safely. Default builds expose the complete 26-tool Junos/SRX surface; Junos-only builds retain the 17-tool surface with --no-default-features.

Highlights

  • One Junos/SRX server — one binary, endpoint, process, inventory, device manager, destructive-operation lease manager, auth surface, and SIGHUP reload path.
  • Resource controls — per-router concurrency limits, per-token session caps, per-token RPS limits, and strict global session admission.
  • Operations and observability — bounded Prometheus metrics and optional native journald audit fan-out.
  • Security and packaging — scoped router-list results, cross-process destructive-operation leases, non-root container SCP support, process healthchecks, and expanded package/container security gates.

Breaking deployment change

The standalone rust-srxmcp executable, rust-srxmcp.service, and 127.0.0.1:30032/mcp endpoint are retired. Package upgrades remove the old binary, unit, and enabled-service link while preserving support bundles under /var/lib/jmcp/srx-staging/bundles.

Use the unified rust-junosmcp endpoint at the configured listener (packaged default 127.0.0.1:30030/mcp). Existing JMCP_SRX_* names are deprecated aliases for v0.8.0 only. JMCP_SRX_HTTP_PORT is ignored because there is no second listener.

Artifacts

  • rust-junosmcp_0.8.0_amd64.tar.gz
  • rust-junosmcp_0.8.0_amd64.tar.gz.sha256
  • ghcr.io/fastrevmd-lab/rust-junosmcp:0.8.0
  • ghcr.io/fastrevmd-lab/rust-junosmcp:0.8
  • ghcr.io/fastrevmd-lab/rust-junosmcp:latest

Known tracked dependency risk

Issue #110 remains open for prerelease RustCrypto dependencies inherited through russh 0.61.2. The currently available stable russh 0.62.2 still carries RC crypto dependencies, so this release retains the audited and previously exercised dependency set.

Full changelog: https://github.com/fastrevmd-lab/rustjunosmcp/blob/v0.8.0/CHANGELOG.md

Diff: v0.7.0...v0.8.0

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.