-
Notifications
You must be signed in to change notification settings - Fork 0
live demo
The repository includes a self-contained demo with two virtual sites running from a single Conduit process: a public app with round-robin load balancing, proxy caching, compression, redirects, and an admin panel protected by Basic Auth.
- Node.js 18+ — runs the mock API backends
- Conduit binary — either built locally or installed via npm/cargo
# Verify
node --version # v18+ required
conduit --versionCache feature: the demo uses in-memory response caching. With the standard binary (
npx @lopatnov/conduit/cargo install) the cache is silently disabled — the demo still works but responses won't be cached. For caching, use the full binary or build with--features cache.
# Terminal 1 — start two mock API instances on ports 4000 and 4001
node demo/api/server.js
# Terminal 2 — start Conduit with the demo config
conduit -c demo/conduit.jsonVS Code users: run the "Demo: Start (Conduit + API)" task
(Terminal → Run Task… or Ctrl+Shift+B) to launch both processes at once.
| URL | Description |
|---|---|
| http://localhost:8080 | Public app — static files, proxied API, caching, compression, rate limiting |
| http://localhost:8081 | Admin panel — protected with Basic Auth (admin / demo1234) |
| Feature | Where to see it |
|---|---|
| Two virtual sites from one process | Two ports, one binary |
| Round-robin load balancing |
/api/* alternates between :4000 and :4001; servedBy field shows which |
| Proxy cache (10 s TTL) |
/api/users and /api/products — second request returns from cache |
| Basic Auth |
http://localhost:8081 — browser shows native login dialog |
| Rate limiting | Hit http://localhost:8080 rapidly → 429 Too Many Requests after 300 req/min |
| Compression |
Accept-Encoding: br → Brotli-compressed static assets |
| Redirects |
GET /old-page → 301 to /; GET /docs/x → 302 to /
|
| SPA fallback | HTML requests return index.html; JSON requests return 404 JSON |
| Security headers |
X-Content-Type-Options, X-Frame-Options, etc. on every response |
| Health endpoint |
GET /__health__ — includes upstream status |
| Prometheus metrics |
GET /__metrics__ — request counts, latencies, cache hits |
| X-Response-Time | Every response includes X-Response-Time: <ms>
|
The Admin API runs on loopback at 127.0.0.1:2019. While the demo is running:
# Server status (version, uptime, in-flight requests)
conduit status
# Upstream health and latency
conduit status --upstream
# Live upstream list
conduit upstreams
# Add a third backend at runtime (no restart needed)
conduit upstreams add --route /api --target http://127.0.0.1:4002
# Hot-reload the config
conduit reload
# Graceful shutdown
conduit shutdownThe full config is in demo/conduit.json.
Key sections:
{
"global": {
"workers": 2,
"admin": { "bind": "127.0.0.1:2019" }
},
"sites": [
{
"port": 8080,
"logging": "dev",
"cors": true,
"compression": true,
"securityHeaders": true,
"responseTime": true,
"rateLimit": { "windowSecs": 60, "limit": 300 },
"static": "./demo/dist",
"proxy": {
"/api": {
"targets": ["http://127.0.0.1:4000", "http://127.0.0.1:4001"],
"strategy": "round-robin",
"stripPrefix": true,
"retry": { "attempts": 2, "conditions": ["connection_error"] },
"cache": { "store": "memory", "ttlSecs": 10 }
}
},
"redirects": [
{ "from": "/old-page", "to": "/", "status": 301 }
],
"healthCheck": { "path": "/__health__", "includeUpstreams": true },
"metrics": { "path": "/__metrics__" },
"fallback": {
"byAccept": {
"html": { "status": 200, "file": "./demo/dist/index.html" },
"json": { "status": 404, "body": { "error": "Not Found" } }
}
}
},
{
"port": 8081,
"basicAuth": {
"users": { "admin": "demo1234" },
"realm": "Conduit Demo Admin"
},
"static": "./demo/admin",
"proxy": { "/api": { "targets": ["http://127.0.0.1:4000"], "stripPrefix": true } }
}
]
}Passwords in
basicAuth.usersshould use environment variables in production:{ "admin": "$ADMIN_PASSWORD" }.
See demo/README.md for a full walkthrough of every feature.
Requires
cargo build --features "rhai,wasm"
A separate demo in examples/middleware-demo/ shows a full four-stage
middleware pipeline — Rhai and WASM running together in request and response phases:
| # | File | Type | Phase | What it does |
|---|---|---|---|---|
| 1 | api-gate.rhai |
Rhai | request | API key check — 401/403 on bad/missing key |
| 2 | header-injector.wasm |
WASM | request | Injects X-Trace-Id + X-Wasm-Plugin onto upstream request |
| 3 | response-enricher.rhai |
Rhai | response | Adds X-Served-By, X-Error-Category; strips Server/X-Powered-By
|
| 4 | response-tagger.wasm |
WASM | response | Adds X-Processed-By: wasm to every response |
# Build with Rhai + WASM support
cargo build --features "rhai,wasm"
# Compile WAT → WASM (once)
wasm-tools parse examples/middleware-demo/header-injector.wat \
-o examples/middleware-demo/header-injector.wasm
wasm-tools parse examples/middleware-demo/response-tagger.wat \
-o examples/middleware-demo/response-tagger.wasm
# Start (proxies to httpbin.org — no local backends needed)
./target/debug/conduit -c examples/middleware-demo/conduit.yaml# Missing API key → 401
curl -i http://localhost:8080/
# Valid key → 200 with all four injected headers
curl -i -H "X-Api-Key: demo-secret" http://localhost:8080/
# X-Trace-Id: <request-id> ← WASM (step 2)
# X-Wasm-Plugin: header-injector ← WASM (step 2)
# X-Served-By: demo-api ← Rhai response (step 3)
# X-Processed-By: wasm ← WASM response (step 4)See examples/middleware-demo/README.md for details.