Files
New Futureandopenhands e9ad33e88b Feat/v5 web mcp (#745)
* feat(rust): add Web dashboard and MCP services

Share configuration, status and synchronization between Web and MCP; add authenticated HTTP, stdio lifecycle support, in-process scheduling and offline regression tests. Exercise native tests on the existing Windows/macOS/Linux CI matrix.

Co-authored-by: openhands <openhands@all-hands.dev>

* ci(rust): allow manual cross-platform validation

Enable workflow_dispatch for feature-branch CI without requiring PR creation. Preserve matrices, permissions, publishing boundaries and trusted-branch cache policy.

Co-authored-by: openhands <openhands@all-hands.dev>

* ci(rust): validate feature branch pushes

Run the existing Rust cross-platform matrix for feat/** pushes while retaining trusted-branch-only cache writes and unchanged release triggers.

Co-authored-by: openhands <openhands@all-hands.dev>

* ci(rust): remove temporary branch trigger

The v5 pull request now exists and runs the complete native matrix, so restore the original trusted push branches while retaining Web/MCP paths and tests.

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-09-12 12:09:26 +08:00
..
2026-09-12 12:09:26 +08:00
2026-09-12 12:09:26 +08:00
2026-09-12 12:09:26 +08:00

DDNS Rust V5

ddns-rs is the Rust implementation for the next major version of NewFuture/DDNS, developed on the v5 integration branch. Python maintenance stays on master / v4; Rust changes target v5, not the stable Python branch.

The current development version is 5.0.0-alpha1, not a published release. The crate stays under rust/ and the command remains ddns-rs; it does not yet replace the stable Python ddns command.

Current V5 scope

  • All canonical Python DNS providers and their documented aliases: Cloudflare, AliDNS/ESA, DNSPod (China, global, and Tencent Cloud), EdgeOne, ClouDNS, DNS.COM, HE.net, Huawei DNS, NameSilo, No-IP, Callback, West.cn, and Debug
  • IPv4 and IPv6
  • Existing CLI options, DDNS_* environment variables, and Schema v4.1 files
  • Local, remote, multiple, JSONC, and restricted Python-literal configurations
  • default, public, numeric, url:, regex:, cmd:, and shell: address rule types
  • Cache, proxy fallback, retries, custom CA files, and existing TLS policies
  • One-shot updates on Linux, macOS, and Windows
  • Embedded Web dashboard, configuration management, and in-process scheduled updates
  • MCP stdio and HTTP transports, with /mcp also available on the Web listener

The task command and OS scheduler migration are not implemented yet. The Linux amd64/arm64 container defaults to one-shot updates; web enables its in-process scheduler. Manually disable old Python/host tasks before enabling Rust scheduled updates: Rust does not detect or take over those tasks. Native artifacts are named ddns-rs-linux-x64, ddns-rs-linux-arm64, ddns-rs-macos-x64, ddns-rs-macos-arm64, and ddns-rs-windows-x64.exe. Linux uses static musl targets so the same names work on glibc and musl distributions.

Release preparation

The Rust CI validates v5 commits and pull requests targeting v5. Prepare Rust V5 builds and verifies native artifacts with SHA-256 checksums and a two-platform OCI archive. Changes to the Cargo version files or this workflow, embedded Web assets/field model, or Rust Dockerfile on v5 automatically start preparation without needing a tag. It also accepts manual preparation on v5 or a canonical v5.* tag matching both Cargo version files. Outputs stay in GitHub Actions artifacts; it does not create a release, upload release assets, or push images to a registry.

No V5 tag or public release is created by this preparation. Before the first public V5 tag, separate Python/Rust release selection in the installers and download service, and explicitly prepare a Rust publication workflow. Until then, use source builds or CI artifacts rather than latest / beta downloads. The retained docs/public/install-rust.sh is future release tooling, not a working V5 download channel. V5 docs builds do not deploy over the stable site.

Build and run

cargo build --manifest-path rust/Cargo.toml --release --locked
rust/target/release/ddns-rs --help
rust/target/release/ddns-rs -c config.json

On Windows, the binary is rust\target\release\ddns-rs.exe.

Existing supported configurations can be reused without changing field names:

ddns-rs --dns cloudflare --token TOKEN --index4 public --ipv4 home.example.com

ssl=auto intentionally preserves the Python client's certificate-validation fallback and is less secure than ssl=true. Remote configurations that contain cmd: or shell: rules execute local commands; only load trusted configuration URLs.

regex: rules use the Rust regex syntax. Python look-around and backreferences are not supported; invalid patterns fail with an explicit compatibility message.

When a reused configuration specifies a custom cache path, ddns-rs writes its versioned format to the sibling <path>.ddns-rs file. The Python cache at the configured path is never modified.

Web and MCP

ddns-rs web -c config.json --host 127.0.0.1 --port 8000 --interval 5 --open
ddns-rs mcp -c config.json
ddns-rs mcp -c config.json --transport http --host 127.0.0.1 --port 8001

web accepts -c FILE, --host HOST, --port PORT, --http-token TOKEN, repeatable --http-origin ORIGIN, --interval MINUTES, and --open (open the browser). Without an explicit subcommand, top-level --interval or a root configuration interval selects Web mode. The in-process scheduler accepts 1..1440 minutes and runs only while the process is alive.

The dashboard reads, validates, and saves the full configuration, manages backups/restores, reports status, and triggers synchronization. Web and MCP use the same service. They require a single local configuration file, not remote or multiple configuration sources. Web validation uses the compatible Rust runtime; it does not promise full Python runtime parity.

mcp defaults to newline-delimited stdio. --transport http serves standalone Streamable HTTP at /mcp; Web exposes the same endpoint on its own listener. Modern MCP uses protocol 2026-07-28; legacy 2025-11-25 is supported over stdio only. Both expose the Python tool names get_ddns_status and update_dns_records.

HTTP listeners default to loopback. A non-loopback bind requires --http-token TOKEN; clients authenticate with Authorization: Bearer TOKEN. Use repeated --http-origin ORIGIN flags for additional allowed browser origins. Origin checks and authentication apply to both Web and MCP HTTP. Do not expose plain HTTP on an untrusted network; use a trusted TLS reverse proxy or tunnel. Status responses exclude credentials, but the authenticated configuration API intentionally returns the full configuration, including provider credentials. Treat listener tokens, configuration files, and backups as secrets. With no listener token on loopback, local access is still a trust boundary. The listener token grants sensitive configuration access and DNS update authority, not read-only monitoring. Web updates execute cmd:/shell: rules from the authenticated local configuration with the process user's permissions; grant access only to trusted administrators.

The binary embeds the existing web/ dashboard assets and ddns/config/field-model.json at compile time; no frontend server or Python runtime is needed to serve them. Rebuild after changing these inputs. Source builds require the repository layout, not an isolated copy of rust/.

Migration limitation: task, OS scheduler detection, and automatic takeover are not implemented. Manually disable old Python/host systemd, cron, launchd, or schtasks jobs before enabling Rust scheduled updates to avoid duplicate runs. The Web scheduler does not install or replace a host service. Existing Rust regex: and separate-cache limits still apply. log_format and log_datefmt are accepted for configuration compatibility, not Python formatting parity.

Validate

cargo fmt --manifest-path rust/Cargo.toml --check
cargo clippy --manifest-path rust/Cargo.toml --locked --all-targets --all-features -- -D warnings
cargo test --manifest-path rust/Cargo.toml --locked

See the Rust development guide for architecture and the feature-parity roadmap.