rneter
rneter is a Rust library for managing SSH connections to network devices with intelligent state machine handling. It provides a high-level API for connecting to network devices (routers, switches, etc.), executing commands, and managing device states with automatic prompt detection and mode switching.
Features
- Connection Pooling: Automatically caches and reuses SSH connections for better performance
- State Machine Management: Intelligent device state tracking and automatic transitions
- Prompt Detection: Automatic prompt recognition and handling across different device types
- Mode Switching: Seamless transitions between device modes (user mode, enable mode, config mode, etc.)
- SFTP File Uploads: Upload local files to remote hosts that expose the SSH
sftpsubsystem - Built-in Copy Flow Templates: Reuse structured templates for Cisco-like interactive
copyworkflows - Maximum Compatibility: Supports a wide range of SSH algorithms including legacy protocols for older devices
- Async/Await: Built on Tokio for high-performance asynchronous operations
- Error Handling: Comprehensive error types with detailed context
Installation
Add this to your Cargo.toml:
[]
= "0.3"
Quick Start
use ;
use templates;
async
Linux Server Management
rneter supports Linux server management with flexible privilege escalation:
use ;
use ;
async
LinuxTemplateConfig.shell_flavor defaults to DeviceShellFlavor::Posix. If the remote login shell is fish, set it explicitly to DeviceShellFlavor::Fish.
Custom Configuration:
use DeviceShellFlavor;
use ;
// Use sudo -s instead of sudo -i
let config = LinuxTemplateConfig ;
let handler = linux_with_config?;
// Custom prompt patterns
let config = LinuxTemplateConfig ;
let handler = linux_with_config?;
// Force fish-compatible exit-status capture
let config = LinuxTemplateConfig ;
let handler = linux_with_config?;
File Uploads
If the remote host enables the SSH sftp subsystem, rneter can upload local files over the
same authenticated SSH connection:
use ;
use templates;
async
This path requires SFTP support on the remote host. For devices that only expose CLI-driven
transfer commands such as copy scp: or copy tftp:, build a transfer flow from templates
and execute it through the generic command-flow API.
Network Device SCP/TFTP Transfers
For Cisco-like CLIs, rneter ships a built-in reusable copy template. Render it with runtime
variables, then execute the resulting CommandFlow through the generic command-flow API:
use ;
use ;
use json;
async
This built-in template matches the prompt style used by cisco, arista, chaitin, maipu,
and venustech. If a vendor wizard differs, build another CommandFlowTemplate on top of the
same abstraction.
Structured Command-Flow Templates
If you want a less hard-coded workflow, build a reusable CommandFlowTemplate in Rust.
It keeps the same declarative shape as the earlier TOML design: vars, steps,
prompts, default_mode, and conditional branches.
use ;
use json;
let template = new
.with_default_mode
.with_vars;
let flow = template.to_command_flow?;
The built-in cisco_like_copy_template() is implemented with the same abstraction, so future
http, ftp, or vendor-specific copy wizards can stay in one structured template layer instead
of adding more one-off Rust structs.
Custom Interactive Command Flows
If a device workflow needs multiple commands or prompt patterns that are not baked into a template,
build a CommandFlow directly and attach runtime PromptResponseRules to each step:
use ;
use templates;
async
Runtime prompt-response rules are evaluated before template static input rules, so new SCP/TFTP/HTTP style wizards can usually be added without changing the underlying template definition.
Security Levels
rneter now supports secure defaults and configurable SSH security levels when connecting:
use ;
use templates;
// Secure by default (uses known_hosts verification + strict algorithms)
let _sender = MANAGER
.get_with_context
.await?;
// Explicitly choose a security profile
let _sender = MANAGER
.get_with_context
.await?;
Session Recording and Replay
use ;
use templates;
let = MANAGER
.get_with_recording_level_and_context
.await?;
// Subscribe to future recorder events in real time
let mut rx = recorder.subscribe;
spawn;
// Or record key events only (no raw shell chunks)
let = MANAGER
.get_with_recording_level_and_context
.await?;
// ...send CmdJob through `sender`...
// Export recording as JSONL
let jsonl = recorder.to_jsonl?;
// Restore and replay offline
let restored = from_jsonl?;
let mut replayer = from_recorder;
let replayed_output = replayer.replay_next?;
println!;
// Offline command-flow testing without real SSH
let script = vec!;
let outputs = replayer.replay_script?;
assert_eq!;
Transactional Command Blocks
For configuration commands, you can execute a block with commit-or-rollback behavior:
use ;
use ;
let block = TxBlock ;
let result = MANAGER
.execute_tx_block_with_context
.await?;
println!;
TxStep::new(...) now accepts any SessionOperation, so a workflow step can be a single
command, a multi-step CommandFlow, or a reusable template invocation:
let copy_step = new;
let summary = copy_step.run.summary?;
println!;
For multi-block all-or-nothing workflows (for example addresses -> services -> policy):
use ;
let workflow = TxWorkflow ;
let workflow_result: TxWorkflowResult = MANAGER
.execute_tx_workflow_with_context
.await?;
for block in &workflow_result.block_results
You can also build blocks from template strategies:
let cmds = vec!;
let block = build_tx_block?;
For CI-style offline tests, you can store JSONL recordings under tests/fixtures/
and replay them in integration tests (see tests/replay_fixtures.rs).
To normalize noisy online recordings into stable fixtures:
Template and State-Machine Ecosystem
You can manage built-in templates as a catalog and run state-graph diagnostics:
use templates;
let names = available_templates;
assert!;
let _handler = by_name?; // case-insensitive
let report = diagnose_template?;
println!;
println!;
let catalog = template_catalog;
println!;
let all_json = diagnose_all_templates_json?;
println!;
You can also export a built-in template configuration, extend it, and build your own handler:
use prompt_rule;
use templates;
let mut config = by_name_config?;
config
.prompt
.push;
let handler = config.build?;
assert!;
New recording/replay capabilities:
- Prompt tracking: each
command_outputnow records bothprompt_before/prompt_after - FSM prompt tracking: each event can include
fsm_prompt_before/fsm_prompt_after - Output prompt: command/replay results now include
Output.prompt - Transaction lifecycle recording:
tx_block_started,tx_step_succeeded,tx_step_failed,tx_rollback_started,tx_rollback_step_succeeded,tx_rollback_step_failed,tx_block_finished - Schema compatibility: legacy
connection_establishedfields (prompt/state) remain readable - Fixture quality workflow:
tests/fixtures/includes success/failure/state-switch samples and snapshot checks intests/replay_fixtures.rs
Example command_output event shape:
Example transaction lifecycle event shape:
Architecture
Connection Management
The SshConnectionManager provides a singleton connection pool accessible via the MANAGER constant. It automatically:
- Caches connections for 5 minutes of inactivity
- Reconnects on connection failure
- Manages up to 100 concurrent connections
State Machine
The DeviceHandler implements a finite state machine that:
- Tracks the current device state using regex patterns
- Finds optimal paths between states using BFS
- Handles automatic state transitions
- Supports system-specific states (e.g., different VRFs or contexts)
Design Rationale
The state machine is designed around two stable facts in network-device automation:
- Prompts are more reliable than command text for identifying current mode.
- Transition paths vary by vendor/model, so pathfinding must be data-driven.
Core design choices:
- Normalize states to lowercase and map prompt regex matches to state indexes for fast lookups.
- Separate prompt detection (
read_prompt) from state update (read) to keep command loops predictable. - Model transitions as a directed graph (
edges) and use BFS to find shortest valid mode switch path. - Keep dynamic input handling (
read_need_write) independent from command logic, so password/confirm flows are reusable. - Track both CLI prompt text and FSM prompt (state name) to support online diagnostics and offline replay assertions.
Benefits:
- Better portability: vendor-specific behavior is mostly data configuration, not hard-coded branches.
- Better resilience: command execution relies on prompt/state convergence instead of fixed output formats.
- Better testability: record/replay can validate state transitions and prompt evolution without real SSH sessions.
State Transition Model
flowchart LR
O["Output"] --> L["Login Prompt"]
L -->|enable| E["Enable Prompt"]
E -->|configure terminal| C["Config Prompt"]
C -->|exit| E
E -->|exit| L
E -->|show ...| E
C -->|show ... / set ...| C
Command Execution Flow (State-Aware)
flowchart TD
A["Receive Command(mode, command, timeout)"] --> B["Read current FSM prompt/state"]
B --> C["BFS transition planning: trans_state_write(target_mode)"]
C --> D["Execute transition commands sequentially"]
D --> E["Execute target command"]
E --> F["Read stream chunks -> update handler.read(line)"]
F --> G{"Prompt matched?"}
G -->|No| F
G -->|Yes| H["Build Output(success, content, all, prompt)"]
H --> I["Record event: prompt_before/after + fsm_prompt_before/after"]
Command Execution
Commands are executed through an async channel-based architecture:
- Submit a
CmdJobto the connection sender - The library automatically transitions to the target state if needed
- Executes the command and waits for the prompt
- Returns the output with success status
Supported Device Types
The library is designed to work with any SSH-enabled network device and Linux servers. It's particularly well-suited for:
Network Devices:
- Cisco IOS/IOS-XE/IOS-XR devices
- Juniper JunOS devices
- Arista EOS devices
- Huawei VRP devices
- H3C Comware devices
- Hillstone SG devices
- Array Networks APV devices
- Fortinet FortiGate firewalls
- Palo Alto Networks PA firewalls
- Check Point Security Gateway
- Topsec NGFW firewalls
- Venustech USG devices
- DPTech firewall devices
- Chaitin SafeLine gateways
- QiAnXin NSG gateways
- Maipu network devices
Linux Servers:
- Generic Linux distributions (Ubuntu, Debian, CentOS, RHEL, etc.)
- Supports multiple privilege escalation methods (sudo -i, sudo -s, su, direct root)
- Intelligent prompt detection with customizable patterns
- Transaction-based configuration management with rollback support
Configuration
SSH Algorithm Support
rneter includes comprehensive SSH algorithm support in the config module:
- Key exchange: Curve25519, DH groups, ECDH
- Ciphers: AES (CTR/CBC/GCM), ChaCha20-Poly1305
- MAC: HMAC-SHA1/256/512 with ETM variants
- Host keys: Ed25519, ECDSA, RSA, DSA (for legacy devices)
This ensures maximum compatibility with both modern and legacy network equipment.
Error Handling
The library provides detailed error types through ConnectError:
UnreachableState: Target state cannot be reached from current stateTargetStateNotExistError: Requested state doesn't exist in configurationChannelDisconnectError: SSH channel disconnected unexpectedlyExecTimeout: Command execution exceeded timeout- And more...
For operation-level APIs such as execute_operation_with_context(...), failures now
return SessionOperationExecutionError, which preserves partial_output() for
already completed child steps.
Documentation
For detailed API documentation, visit docs.rs/rneter.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Author
demohiiiii