Skip to content

live demo

github-actions[bot] edited this page Jun 12, 2026 · 1 revision

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.

Prerequisites

  • Node.js 18+ — runs the mock API backends
  • Conduit binary — either built locally or installed via npm/cargo
# Verify
node --version   # v18+ required
conduit --version

Cache 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.


Running the demo

# 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.json

VS Code users: run the "Demo: Start (Conduit + API)" task (Terminal → Run Task… or Ctrl+Shift+B) to launch both processes at once.


What's running

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)

What the demo shows

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>

Admin API

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 shutdown

Demo config at a glance

The 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.users should use environment variables in production: { "admin": "$ADMIN_PASSWORD" }.

See demo/README.md for a full walkthrough of every feature.


Middleware pipeline demo

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.

Clone this wiki locally