Leywn 1.0.0 released — the last echo service you will need

SvenWal

July 31, 2026

Leywn (“Last Echo You Will Need”) reaches 1.0.0 today. It is a modern, all-in-one demo and test backend for APIs and HTTP services: a single container that mirrors requests, speaks every common authentication scheme, returns any HTTP status code, injects faults on demand, generates test data, and serves its own Swagger UI — so you can test clients, proxies, load balancers and API gateways without standing up a real backend.

Far more than an echo

Echoing a request is where Leywn starts, not where it stops. /echo returns method, scheme, host, port, path, query parameters, headers, caller IP and body. On top of that sit the endpoints that make it a full test backend:

See all endpoints documented at https://leywn.org/docs

Three examples that show what it is for

1. /echo — see exactly what your client sends

Point any HTTP client, SDK or gateway at /echo and it reports back the whole request: the headers your library adds behind your back, how it encoded the body, which IP the server actually saw.

curl -X POST "http://localhost:4000/echo/foo/bar?hello=world" \
  -H "Content-Type: application/json" \
  -d '{"message": "test"}'

Bodies over the size limit are acknowledged rather than silently dropped (truncated: true), and binary bodies are detected and excluded instead of mangled. Add Accept: application/xml and the same answer arrives as XML — which is also a quick way to check your XML parser against a real payload.

2. /auth/mtls — mutual TLS without building a PKI

Client-certificate authentication is the thing nobody wants to set up just to test it. Leywn generates a CA, a server certificate and a client certificate in memory at every startup, then hands you the client credentials over HTTP:

curl -k https://localhost:4443/auth/mtls/get-client-cert   # returns cert_pem + key_pem
curl -k --cert client.pem --key client.key https://localhost:4443/auth/mtls

The response reports the client_dn and client_ca it validated. When a load balancer terminates TLS instead, start Leywn with LEYWN_MTLS_IN_HEADER=X-Client-Cert and it reads the forwarded certificate from that header — so you can prove your Kong, Nginx, ALB or Envoy is passing through the certificate you think it is. Bring your own CA and client certificate via LEYWN_MTLS_CERT / LEYWN_MTLS_KEY and Leywn trusts it automatically.

3. /chaos-engineering — make it fail on purpose

/chaos-engineering answers like /echo, but injects real faults at rates you control: random 4xx/5xx errors, responses truncated mid-stream so the JSON is genuinely unparseable, and random latency up to a bound you set.

# 10 % errors, 10 % mangled responses, 20 % slow up to 2 s
curl http://localhost:4000/chaos-engineering/10/10/20/2000

The same parameters can be sent as X-Chaos-* headers, and every response carries a _chaos field reporting exactly which faults were applied and how much latency was added. That turns “does our retry logic actually work, and does our circuit breaker actually open?” into a question you can answer in one command — against a backend that is honest about what it did to you.

Genuinely lightweight

Leywn is written entirely in Elixir on the Erlang/OTP runtime. There is no JVM, no Node.js, no .NET, no Python, no interpreter to warm up and no framework stack to configure. The Docker image is a multi-stage Alpine build containing nothing but the compiled OTP release — no Mix, no Hex, no source code, roughly 17 MB (compressed). It runs as a non-root user (UID 1001, GID 0) and is compatible with OpenShift’s arbitrary-UID injection.

Even the plumbing avoids weight: certificate generation uses Erlang’s built-in :public_key and :crypto, YAML parsing is pure Erlang with no C NIFs, and the whole service carries a handful of dependencies.

Stable and fast, because of the BEAM

Running on the Erlang virtual machine is not a stylistic choice. Every request is handled in its own lightweight process, so one slow or failing request cannot block or corrupt another; the scheduler is preemptive, so a /delay/30000 call does not stall the rest of the server; and an OTP supervision tree restarts what fails instead of taking the node down. This is the same runtime that has carried telecom switching for decades, and it is why a service this small can hold thousands of concurrent connections on modest hardware while idling at a few tens of megabytes.

Ready for production use as a test tool

1.0.0 is a hardening release as much as a feature release. XML element names are sanitised so header and query-parameter names cannot inject markup; chaos latency headers are range-validated so they cannot be turned into a denial of service; credential comparison for Basic auth and API keys runs over SHA-256 digests via :crypto.hash_equals/2 so neither value nor length leaks through timing; the bundled Swagger UI is version-pinned with Subresource Integrity; the generated server certificate carries a proper subjectAltName; and a half-configured TLS setup now aborts at startup instead of silently falling back. The test suite has grown to 180 tests, run in the container on every change.

Get it

One command, no configuration:

docker run -p 4000:4000 -p 4443:4443 svenwal/leywn:latest

Port 4000 serves plain HTTP, port 4443 serves HTTPS and mTLS with a self-signed certificate generated at startup. Open http://localhost:4000 and every endpoint is there, documented and executable in the browser.