| Crates.io | magneto-serge |
| lib.rs | magneto-serge |
| version | 0.7.0 |
| created_at | 2025-10-11 09:56:40.871189+00 |
| updated_at | 2025-10-27 21:46:49.599344+00 |
| description | Multi-language HTTP/WebSocket testing library with record/replay capabilities - like VCR for the modern web |
| homepage | https://github.com/taciclei/magneto-serge |
| repository | https://github.com/taciclei/magneto-serge |
| max_upload_size | |
| id | 1878046 |
| size | 1,843,557 |
Multi-language HTTP/WebSocket proxy library with record/replay capabilities
VCR for the modern web - Record HTTP/HTTPS and WebSocket traffic, replay it deterministically
Features • Installation • Quick Start • Documentation • Examples
|
🔒 HTTP/HTTPS Proxy
|
🔌 WebSocket Support
|
🌍 Multi-Language
|
|
📝 Dynamic Templates 🆕
|
🎯 Test Integrations 🆕
|
⚡ High Performance
|
| Feature | Magnéto-Serge | VCR (Ruby) | Polly (JS) |
|---|---|---|---|
| Multi-language | ✅ Rust + JS ready | ❌ Ruby only | ❌ JS only |
| WebSocket | ✅ Full support | ❌ No | ⚠️ Limited |
| Performance | ⚡ Rust-powered | 🐌 Ruby | 🐌 JS |
| HTTPS MITM | ✅ Auto certs | ⚠️ Manual | ⚠️ Manual |
| Zero config | ✅ Auto mode | ❌ | ❌ |
# Add the tap
brew tap taciclei/tap https://github.com/taciclei/magneto-serge
# Install
brew install magneto-serge
# Verify
magneto --version
curl -sSL https://raw.githubusercontent.com/taciclei/magneto-serge/main/install.sh | bash
Download from GitHub Releases:
magneto-macos-amd64.tar.gz (Intel) or magneto-macos-arm64.tar.gz (Apple Silicon)magneto-linux-amd64.tar.gz or magneto-linux-arm64.tar.gzmagneto-windows-amd64.exe.zip# Example for macOS ARM64
curl -LO https://github.com/taciclei/magneto-serge/releases/latest/download/magneto-macos-arm64.tar.gz
tar xzf magneto-macos-arm64.tar.gz
chmod +x magneto
mv magneto /usr/local/bin/
[dependencies]
magneto-serge = "0.6.0"
Or install the CLI:
# From crates.io (pending)
cargo install magneto-serge --features cli
# Or from GitHub (current)
cargo install --git https://github.com/taciclei/magneto-serge --branch main --features cli
# Via npm (GitHub Packages)
npm install @taciclei/magneto-serge
const { MagnetoProxy, ProxyMode } = require('@taciclei/magneto-serge');
Multi-language bindings are in development. See ROADMAP.md for status.
The fastest way to get started with the complete ecosystem:
# Check dependencies
make help # Show all available commands
./scripts/check-deps.sh # Verify dependencies
# Quick setup (install + build)
make quick # Install deps + build Rust
# Install everything (Rust + Node.js backends + Angular clients)
make install # Install all dependencies
make build-all # Build everything
# Start complete stack (API + Backend + Frontend)
make dev # Launch in tmux (automatic)
make dev-manual # Get manual instructions
# Individual services
make run-api # Start Magneto API (port 8889)
make run-backend # Start Node.js backend (port 3000)
make run-client-simple # Start Angular client (port 4201)
make run-client-hydra # Start Angular Hydra demo (port 4200)
# CLI examples
make example-record # Record HTTP requests
make example-replay # Replay from cassette
make example-auto # Auto mode (smart)
# Utilities
make status # Check running services
make ports # Show used ports
make clean-all # Clean everything
Complete Makefile reference: Run make help for all 50+ commands.
use magneto_serge::{MagnetoProxy, ProxyMode};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create proxy with auto mode (record if missing, else replay)
let proxy = MagnetoProxy::new_internal("./cassettes")?
.with_port(8888)
.with_mode(ProxyMode::Auto);
// Start recording
proxy.start_recording_internal("my-api-test".to_string())?;
// Configure your HTTP client to use proxy localhost:8888
// Make your API requests here...
// Stop and save cassette
proxy.stop_recording_internal()?;
proxy.shutdown_internal()?;
Ok(())
}
const { MagnetoProxy, ProxyMode } = require('@taciclei/magneto-serge');
// Create proxy instance
const proxy = new MagnetoProxy('./cassettes');
proxy.setPort(8888);
proxy.setMode(ProxyMode.Auto);
// Start recording
proxy.startRecording('my-api-test');
// Configure your HTTP client to proxy through localhost:8888
// Make your API requests...
// Stop recording
proxy.stopRecording();
proxy.shutdown();
graph LR
A[Your App] -->|HTTP Request| B[Magnéto-Serge<br/>Proxy :8888]
B -->|Record Mode| C[Real API]
B -->|Replay Mode| D[Cassette]
C -->|Response| B
D -->|Cached| B
B -->|Response| A
B -->|Save| D
3 Modes:
const { MagnetoProxy, ProxyMode } = require('@taciclei/magneto-serge');
const axios = require('axios');
async function testWithMagneto() {
const proxy = new MagnetoProxy('./cassettes');
proxy.setPort(8888);
proxy.setMode(ProxyMode.Auto);
proxy.startRecording('github-api-test');
// Configure axios to use proxy
const client = axios.create({
proxy: {
host: 'localhost',
port: 8888
}
});
try {
// First run: records from real API
// Second run: replays from cassette
const response = await client.get('https://api.github.com/users/octocat');
console.log('User:', response.data.login);
} finally {
proxy.stopRecording();
proxy.shutdown();
}
}
testWithMagneto();
use magneto_serge::{MagnetoProxy, ProxyMode};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let proxy = MagnetoProxy::new_internal("./cassettes")?
.with_port(8888)
.with_mode(ProxyMode::Auto);
proxy.start_recording_internal("github-api-test".to_string())?;
// Configure reqwest to use proxy
let client = reqwest::Client::builder()
.proxy(reqwest::Proxy::all("http://localhost:8888")?)
.build()?;
let response = client
.get("https://api.github.com/users/octocat")
.send()
.await?;
println!("Status: {}", response.status());
proxy.stop_recording_internal()?;
proxy.shutdown_internal()?;
Ok(())
}
#[cfg(test)]
mod tests {
use magneto_serge::{MagnetoProxy, ProxyMode};
#[test]
fn test_api_integration() {
let proxy = MagnetoProxy::new_internal("./test-cassettes")
.expect("Failed to create proxy")
.with_port(9999)
.with_mode(ProxyMode::Auto);
proxy.start_recording_internal("integration-test".to_string())
.expect("Failed to start recording");
// Your test code here
// Configure HTTP client to use localhost:9999
proxy.stop_recording_internal()
.expect("Failed to stop recording");
}
}
Enable dynamic content generation during replay with Handlebars templates:
use magneto_serge::Player;
use std::path::Path;
// Compile with templates feature
// cargo build --features templates
// Set environment variable
std::env::set_var("API_TOKEN", "sk-test-1234567890");
// Load cassette with templates
let player = Player::load(Path::new("./cassettes"), "api-test")?;
// Templates are automatically rendered during replay
let interaction = player.get_interaction(0)?;
Example Cassette with Templates:
{
"response": {
"body": "{\"token\":\"{{ env \\\"API_TOKEN\\\" }}\",\"issued_at\":\"{{ now }}\",\"request_id\":\"{{ uuid }}\",\"user\":\"{{ request.headers.x-user-id }}\"}"
}
}
Built-in Helpers:
| Helper | Description | Example Output |
|---|---|---|
{{ env "VAR" }} |
Environment variable | sk-test-1234567890 |
{{ now }} |
ISO 8601 timestamp | 2025-10-26T08:30:45Z |
{{ now_timestamp }} |
Unix epoch | 1729930245 |
{{ uuid }} |
UUID v4 | a1b2c3d4-e5f6-... |
{{ request.method }} |
HTTP method | POST |
{{ request.url }} |
Request URL | https://api.example.com/... |
{{ request.headers.xxx }} |
Request header | Header value |
Custom Helpers:
let mut player = Player::load(path, cassette)?;
player.template_engine_mut().register_helper("random_id", || {
format!("id_{}", rand::random::<u32>())
});
// Use in cassette: {"id":"{{ random_id }}"}
Learn More:
Magneto-Serge provides a complete REST API with Hydra/JSON-LD and OpenAPI 3.0 support for remote proxy control.
# Start the API server (with Hydra hypermedia support)
magneto serve
# Opens: http://127.0.0.1:8889
# REST API: http://127.0.0.1:8889/cassettes
# Hydra API: http://127.0.0.1:8889/api/cassettes
# Or legacy API command
magneto api
# With authentication
magneto api --auth --api-key your-secret-key
# Custom host/port
magneto serve --host 0.0.0.0 --port 8889
/openapi.json# Start proxy via API
curl -X POST http://localhost:8889/proxy/start \
-H "Content-Type: application/json" \
-d '{
"mode": "auto",
"cassette_name": "my-test",
"port": 8888
}'
# Check status
curl http://localhost:8889/proxy/status
# Stop proxy
curl -X POST http://localhost:8889/proxy/stop
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
API root with Hydra links |
GET |
/openapi.json |
OpenAPI 3.0 specification |
GET |
/health |
Health check |
POST |
/proxy/start |
Start proxy (auto/record/replay/passthrough) |
POST |
/proxy/stop |
Stop proxy |
GET |
/proxy/status |
Get proxy status |
GET |
/proxy/stats |
Get statistics |
GET |
/cassettes |
List all cassettes |
GET |
/cassettes/{name} |
Get cassette content |
DELETE |
/cassettes/{name} |
Delete cassette |
import requests
api = "http://localhost:8889"
# Start proxy
response = requests.post(f"{api}/proxy/start", json={
"mode": "auto",
"cassette_name": "test",
"port": 8888
})
# Get status
status = requests.get(f"{api}/proxy/status").json()
print(f"Running: {status['data']['running']}")
# Follow Hydra links
links = status.get('hydra:link', [])
for link in links:
print(f"→ {link['title']}: {link['hydra:target']}")
const fetch = require('node-fetch');
const api = 'http://localhost:8889';
// Start proxy
await fetch(`${api}/proxy/start`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
mode: 'auto',
cassette_name: 'test',
port: 8888
})
});
// Get status with authentication
const status = await fetch(`${api}/proxy/status`, {
headers: { 'Authorization': 'Bearer your-key' }
}).then(r => r.json());
console.log('Running:', status.data.running);
#!/bin/bash
API="http://localhost:8889"
# List cassettes
curl $API/cassettes | jq '.data[].name'
# Start proxy with authentication
curl -X POST $API/proxy/start \
-H "Authorization: Bearer your-key" \
-H "Content-Type: application/json" \
-d '{"mode": "record", "cassette_name": "test"}'
# Get OpenAPI spec
curl $API/openapi.json | jq '.info'
Every API response includes Hydra links for discoverability:
{
"@context": "https://www.w3.org/ns/hydra/core",
"@type": "hydra:Resource",
"success": true,
"data": { "message": "Proxy started successfully" },
"hydra:link": [
{
"@type": "hydra:Link",
"hydra:target": "http://localhost:8889/proxy/status",
"title": "Check Proxy Status",
"hydra:operation": [{
"@type": "http://schema.org/ViewAction",
"method": "GET"
}]
}
]
}
Clients can discover and navigate the API dynamically without hardcoding URLs!
See docs/API.md for complete reference including:
Magneto-Serge includes a complete web stack with multiple frontend/backend architectures.
|
1. CLI Only ⚡
→ Perfect for scripts, CI/CD |
2. Production Stack 🏭
→ Recommended for production |
3. Hydra Demo 🔬
→ Hypermedia demonstration |
# Automatic (with tmux)
make dev
# Or manual (3 terminals)
make dev-manual
Opens:
Location: examples/nodejs-backend/
Alcaeus wrapper exposing simplified REST API:
cd examples/nodejs-backend
npm install
npm start
# → http://localhost:3000
Features:
Docs: nodejs-backend/README.md | ARCHITECTURE.md
Location: frontend/
Production Angular 17 client with Hydra hypermedia navigation:
cd frontend
npm install
# Development server (with workaround for Angular 17 + Vite)
./dev-server.sh
# → http://localhost:4201
# Or using npm scripts
npm run build:dev
npm run serve:built
Features:
Stack:
Docs: frontend/DEVELOPMENT.md | PHASE-3-COMPLETE 🆕 | PHASE-2-COMPLETE.md
Note: Due to Angular 17 + Vite dev server issue, use dev-server.sh script which builds and serves with http-server + auto-rebuild. See PHASE-2.4-TESTING.md for details.
Location: examples/angular-simple-client/
Alternative lightweight Angular client using the Node.js backend:
cd examples/angular-simple-client
npm install
npm start
# → http://localhost:4202
Features:
Docs: angular-simple-client/README.md
Location: examples/angular-client/
Educational demonstration of Hydra/JSON-LD navigation with Alcaeus in browser:
cd examples/angular-client
npm install
npm start
# → http://localhost:4200
Features:
Docs: angular-client/README.md
| Aspect | CLI | Frontend (Hydra) | Simple Client + Backend | Hydra Demo |
|---|---|---|---|---|
| Use Case | Scripts, CI/CD | Production web app | Lightweight web app | Education |
| Complexity | ✅ Simple | ✅ Medium | ✅ Medium | ⚠️ Complex |
| Build Size | N/A | ⚠️ ~4.2MB (dev) | ✅ ~50kb | ⚠️ ~150kb |
| Features | Full CLI | Hypermedia + NgRx | REST API | Alcaeus demo |
| Dependencies | Rust only | Angular + Alcaeus | Node + Angular | Alcaeus + RDF |
| Performance | ✅ Maximum | ✅ Client-side state | ✅ Server cache | ⚠️ Client parsing |
| Production | ✅ Yes | ✅ Recommended | ✅ Yes | ⚠️ Demo only |
Cassettes are language-agnostic JSON files - record in Rust, replay in JavaScript!
{
"version": "1.0",
"name": "my-api-test",
"recorded_at": "2025-10-10T14:30:00Z",
"interactions": [
{
"type": "Http",
"request": {
"method": "GET",
"url": "https://api.example.com/users",
"headers": {"accept": "application/json"},
"body": null
},
"response": {
"status": 200,
"headers": {"content-type": "application/json"},
"body": [...]
}
},
{
"type": "WebSocket",
"url": "wss://stream.example.com",
"messages": [
{"direction": "Sent", "timestamp_ms": 0, "msg_type": "Text", "data": "..."},
{"direction": "Received", "timestamp_ms": 120, "msg_type": "Text", "data": "..."}
]
}
]
}
Format features:
msgpack feature)graph TB
A[MagnetoProxy API] --> B[HTTP Handler]
A --> C[WebSocket Interceptor]
B --> D[Recorder/Player]
C --> D
D --> E[Cassette Storage JSON]
B --> F[Hudsucker MITM]
C --> G[tokio-tungstenite]
F --> H[TLS Certificate Manager]
Core components:
// Record real API once, replay thousands of times
// ✅ No network flakiness
// ✅ Instant test execution (no API calls)
// ✅ Offline development
// ✅ Deterministic tests in CI/CD
// Capture production traffic
// Replay locally for investigation
// Inspect every request/response
// Mock external APIs during development
// Work offline with cached responses
// Consistent test fixtures across team
# Clone repository
git clone https://github.com/taciclei/magneto-serge.git
cd magneto-serge
# Check dependencies
./scripts/check-deps.sh
# Quick setup
make quick # Install + build in one command
# Or step by step
make install # Install all dependencies
make build-all # Build everything
make test # Run all tests
# Development workflow
make dev # Start complete stack (tmux)
make status # Check services status
make clean-all # Clean everything
# CI/CD checks
make ci # Run fmt, clippy, tests
# See all commands
make help
# Build Rust library
cargo build --release
# Run all tests (68 tests)
cargo test --all-features
# Run integration tests
cargo test --test integration_test
# Lint
cargo clippy --all-features -- -D warnings
# Format
cargo fmt --all
# Build JavaScript bindings
cd bindings/javascript
npm install
npm run build
# Build Angular clients
cd examples/angular-simple-client && npm install && npm run build
cd examples/angular-client && npm install && npm run build
# Build Node.js backend
cd examples/nodejs-backend && npm install
# Rust unit tests (47 tests)
cargo test --lib
# Integration tests (9 tests)
cargo test --test integration_test
# WebSocket tests (5 tests)
cargo test --test websocket_integration
# JavaScript tests
cd bindings/javascript
node test-complete.js
Current Test Status: 68/68 passing ✅
magneto-serge/
├── src/
│ ├── lib.rs # Core library
│ ├── proxy.rs # MagnetoProxy implementation
│ ├── cassette.rs # Cassette format
│ ├── player.rs # Replay engine
│ ├── recorder.rs # Record engine
│ ├── websocket/ # WebSocket support
│ ├── tls/ # TLS certificate management
│ └── error.rs # Error types
├── bindings/
│ └── javascript/ # NAPI-RS bindings for Node.js
│ ├── src/lib.rs
│ ├── package.json
│ └── index.js
├── tests/ # Integration tests
│ ├── integration_test.rs
│ └── websocket_integration.rs
├── examples/ # Usage examples
└── docs/ # Documentation
├── ROADMAP.md
└── ARCHITECTURE.md
| Documentation | Description |
|---|---|
| QUICK_START.md | 🚀 Quick start guide with use cases |
| Makefile | ⚡ 50+ automation commands |
| ROADMAP.md | 🗺️ Development roadmap & progress |
| ARCHITECTURE.md | 🏗️ Technical architecture details |
| API.md | 🌐 Complete REST API reference |
| TECH-STACK.md | 📚 Complete dependency list |
| SECRETS_SETUP.md | 🔐 GitHub secrets setup for CD |
| CLAUDE.md | 🤖 AI assistant instructions |
Phase 2 (Hydra API + Frontend):
| Documentation | Description |
|---|---|
| PHASE-2-COMPLETE.md | ✅ Phase 2 completion summary |
| frontend/DEVELOPMENT.md | 🅰️ Frontend development guide |
| PHASE-2.4-TESTING.md | 🔧 Angular 17 + Vite troubleshooting |
Web Ecosystem:
| Documentation | Description |
|---|---|
| nodejs-backend/README.md | 🟢 Node.js backend guide |
| nodejs-backend/ARCHITECTURE.md | 🏗️ Production architecture (3-tier) |
| angular-simple-client/README.md | 🅰️ Alternative Angular client |
| angular-client/README.md | 🅰️ Hydra demo client |
| examples/README.md | 📚 All examples catalog |
Bindings:
| Documentation | Description |
|---|---|
| JavaScript README | 🟨 JS/TS bindings guide |
| Phase | Status | Progress | Details |
|---|---|---|---|
| Phase 1 - HTTP/HTTPS Proxy | ✅ Complete | 100% | MITM proxy, record/replay |
| Phase 2 - Hydra API + Frontend | ✅ Complete | 100% | REST API, Angular UI, Hypermedia |
| Phase 3 - Testing & Polish | ✅ Complete | 100% | 186 tests (98.9% pass), 74.73% coverage, NgRx tested |
| Phase 4 - CLI & Production | ✅ Complete | 100% | CLI tool, templates, benchmarks |
✅ Completed:
magneto record, replay, auto, etc.)🚧 In Progress:
📅 Planned (Phase 5):
See SESSION-2025-10-27-PHASE3-COMPLETE.md for Phase 3 details, PHASE-2-COMPLETE.md for Phase 2, and ROADMAP.md for detailed milestones.
We welcome contributions! Issues are now enabled on this repository.
Here's how to contribute:
git checkout -b feature/amazing)cargo fmt and cargo clippyDevelopment requirements:
Areas where we need help:
Current benchmarks (Rust):
Test environment:
Note: Formal benchmarks coming in Phase 4. Use
cargo benchfor testing.
See Issues for complete list and workarounds.
Licensed under either of:
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Inspired by:
Built with:
⚡ Made with Rust for maximum performance and safety
Current Version: 0.6.0