A modern, extensible proxy configuration conversion tool supporting conversion and multi-source integration between various proxy configuration formats.
- Multi-protocol support: Supports Clash, Sing-box, V2Ray and other proxy configuration formats
- Multi-source integration: Supports integration of multiple input sources with unified interpolation rules ✨
- URL-style source: Source uses standard URL form
<path|url>?type=...&name=...&flag=...for easy extension ✨ - Powerful interpolation system: Advanced template interpolation rules for complex node selection and filtering ✨
- Plugin architecture: Easy to extend new protocol support
- Modern CLI: User-friendly command line interface based on clap 4.x
- Async processing: High-performance async processing based on tokio
- Structured logging: Full logging system using tracing
- Config management: Flexible config file and environment variable support
- Error handling: Robust error handling and user-friendly error messages
- Type safety: Strong type system, compile-time checks
git clone https://github.com/your-username/proxy-convert.git
cd proxy-convert
cargo build --releasecargo install --git https://github.com/your-username/proxy-convert.git# Recommended: URL-style (path/url + query params)
proxy-convert convert --source "./clash.yaml?type=clash"
proxy-convert convert --source "https://example.com/sub?type=clash&name=my&flag=clash" -o config.json
# Legacy: name@type@source or type@source
proxy-convert convert --source "clash1@clash@./clash.yaml" --source "singbox1@sing-box@./singbox.json" -o config.json
# Validate config file
proxy-convert validate config.json
# Generate template
proxy-convert template singbox --output template.json
# Show version info
proxy-convert versionSingle source and multiple sources use exactly the same interpolation rules and input rules!
This ensures consistent user experience regardless of whether you use a single input source or multiple input sources.
<path|url>?type=clash&name=...&flag=...
- Path or URL +
?and query params (standard URL):type(required),name(optional),flag(optional). - Examples:
./config.yaml?type=clash,https://example.com/sub?type=clash&name=my&flag=clash - Config file
sourcesuse the same format (list of such strings).
--source "./clash.yaml?type=clash"
--source "https://example.com/sub?type=clash&name=my&flag=clash"
--source "examples/sources/Eternal Network?type=singbox"
# With config: --config examples/config.yaml -o config.jsonThe project supports powerful template interpolation rules. Each rule ends with ; (can be omitted if there's only one rule).
{
"outbounds": "{{ALL-TAG}}"
}{
"outbounds": "{{ALL-TAG:clash1}}"
}{
"outbounds": "{{ALL-TAG:clash1,singbox1}}"
}{
"outbounds": "{{INCLUDE-TAG:US,JP,SG}}"
}{
"outbounds": "{{INCLUDE-TAG:clash1@US,JP}}"
}{
"outbounds": "{{INCLUDE-TAG:clash1@US,JP,singbox1@SG}}"
}{
"outbounds": "{{EXCLUDE-TAG:CN,AD}}"
}{
"outbounds": "{{EXCLUDE-TAG:clash1@CN}}"
}{
"outbounds": "{{EXCLUDE-TAG:clash1@CN,AD,singbox1@BLOCKED}}"
}When using source-name@tag format, the final node tags automatically include source prefixes:
- Nodes matching
clash1@USwill have tags containingclash1@US - Nodes matching
singbox1@SGwill have tags containingsingbox1@SG
{
"outbounds": [
{
"type": "urltest",
"tag": "Hong Kong Nodes",
"outbounds": "{{ALL-TAG:clash1}}",
"url": "https://www.gstatic.com/generate_204",
"interval": "300s"
},
{
"type": "urltest",
"tag": "US Nodes",
"outbounds": "{{INCLUDE-TAG:US,JP}}",
"url": "https://www.gstatic.com/generate_204",
"interval": "300s"
},
{
"type": "urltest",
"tag": "Singapore Nodes",
"outbounds": "{{INCLUDE-TAG:singbox1@SG}}",
"url": "https://www.gstatic.com/generate_204",
"interval": "300s"
},
{
"type": "urltest",
"tag": "Other Nodes",
"outbounds": "{{EXCLUDE-TAG:US,JP,SG,CN}}",
"url": "https://www.gstatic.com/generate_204",
"interval": "300s"
}
]
}proxy-convert convert [OPTIONS] --source <SOURCE> [--source <SOURCE>...]Arguments:
--source <SOURCE>: Input source:<path|url>?type=...&name=...&flag=...(type required in query)
Options:
-t, --template <TEMPLATE>: Template file path-o, --output <OUTPUT>: Output file path (default: config.json)-f, --format <FORMAT>: Output format (json/pretty/yaml, default: pretty)-F, --force: Force overwrite output file-l, --log-level <LOG_LEVEL>: Log level (default: info)-v, --verbose: Show detailed information
proxy-convert validate <FILE> [OPTIONS]Arguments:
FILE: Config file path
Options:
-f, --format <FORMAT>: Config format
proxy-convert template [OPTIONS]Options:
-o, --output <OUTPUT>: Output file path (default: template.json)-t, --template-type <TEMPLATE_TYPE>: Template type (basic/full/minimal, default: basic)
proxy-convert versionThe program will look for config files in the following order:
config.yamlorconfig.ymlin the current directoryproxy-convert/config.yamlorconfig.ymlin the user config directory
# config.yaml
user_agent: "ProxyConfigConverter/3.0.0"
timeout_seconds: 30
retry_count: 3
cache_ttl_seconds: 3600
log_level: info
output_format: json
default_input_format: clash
default_output_format: singbox
template_dir: ~/.config/proxy-convert/templates
# sources: same format as --source, e.g. ["path?type=clash&name=my"]
# sources:
# - "./clash.yaml?type=clash&name=clash1"
# - "https://example.com/sub?type=singbox&name=sub1"All config items can be overridden by environment variables in the format PROXY_CONVERT_<KEY>:
export PROXY_CONVERT_LOG_LEVEL=debug
export PROXY_CONVERT_TIMEOUT_SECONDS=60
export PROXY_CONVERT_DEFAULT_OUTPUT_FORMAT=v2raysrc/
├── main.rs # Entry point
├── core/ # Domain and core
│ ├── config.rs # Config management
│ ├── error.rs # Global error type
│ ├── logging.rs # Logging init
│ └── source.rs # SourceMeta, SourceProtocol (domain types)
├── protocols/ # Protocol modules
│ ├── mod.rs # Protocol registry, ProxyServer, ProtocolProcessor
│ ├── clash/ # Clash protocol support
│ ├── singbox/ # Sing-box protocol support
│ └── v2ray/ # V2Ray protocol support
├── commands/ # CLI commands
│ ├── cli.rs # CLI definition
│ ├── convert.rs # Convert command
│ ├── validate.rs # Validate command
│ ├── template.rs # Template command
│ └── version.rs # Version command
└── utils/ # Utilities
├── source/ # Source loader and parser
└── template/ # Template interpolation and engine
The project supports multi-source integration with unified interpolation rules:
- Source format:
<path|url>?type=...&name=...&flag=...(same for CLI and config) - Type detection: Automatic detection of clash, sing-box, v2ray formats
- Source naming: Each source has a unique name for template reference
- Unified rules: Single source and multiple sources use identical rules
- Advanced filtering: Support for complex tag-based filtering
- Source-specific rules: Support for
source-name@tagformat - Automatic prefixing: Tags automatically include source prefixes
- Rule parsing: Parse interpolation rule strings
- Node filtering: Filter and select nodes based on rules
- Tag processing: Automatically add source prefixes
- Template rendering: Insert node information into templates
- Tag matching: Filter nodes by tags
- Protocol matching: Filter nodes by protocol type
- Pattern matching: Filter nodes by name patterns
- Quantity limits: Limit the number of selected nodes
- Renaming: Change node names
- Tag operations: Add/remove tags
- Parameter modification: Change node parameters
- Create a new protocol module under
src/protocols/ - Implement the
ProtocolProcessortrait for template processing - Register the processor in
ProtocolRegistry::init()insrc/protocols/mod.rs:registry.register("format_name", Box::new(YourProcessor)); - Add parsing/conversion in the registry for your format if needed (e.g.
parse_content, subscription/plain)
// src/protocols/new_protocol/mod.rs
use crate::core::error::Result;
use crate::protocols::{ProtocolProcessor, ProxyServer};
use crate::utils::template::interpolation_parser::InterpolationRule;
use indexmap::IndexMap;
use crate::utils::source::parser::Source;
pub struct NewProtocolProcessor;
impl ProtocolProcessor for NewProtocolProcessor {
fn process_rule(&self, _rule: &InterpolationRule, _sources: &IndexMap<String, Source>) -> Result<String> {
Ok(String::new())
}
fn get_nodes_for_rule(&self, rule: &InterpolationRule, sources: &IndexMap<String, Source>) -> Result<Vec<ProxyServer>> {
// ...
}
fn set_default_values(&self, template: &str, nodes: &[ProxyServer]) -> Result<String> {
// ...
}
fn append_nodes(&self, template: &str, nodes: &[ProxyServer]) -> Result<String> {
// ...
}
fn create_node_config(&self, node: &ProxyServer) -> String {
// ...
}
}Then in ProtocolRegistry::init():
registry.register("new_protocol", Box::new(new_protocol::NewProtocolProcessor));# Build the project
cargo build
# Run tests
cargo test
# Check code quality
cargo clippy
# Format code
cargo fmtThis project is licensed under the MIT License - see the LICENSE file for details.
- clap - Command line argument parser
- tokio - Async runtime
- serde - Serialization framework
- tracing - Structured logging
Note: This project is for learning and research purposes only. Please comply with local laws and regulations.