What it is
Servienta gives your test suite a controlled instance of the network services your application talks to — a file server on five transports and receivers for syslog, SNMP traps, RADIUS, TACACS+, DNS, NTP, Kafka, and IPFIX. Bring it up with one command, generate traffic, read back exactly what arrived, inject faults, and reset — reproducibly, offline.
The application under test is out of scope: the engine never reaches into it. It only accepts, serves, and reports what it observed.
Core concepts
Quickstart
Run the engine, serve a file, record a message, read it back, reset. Free image, no license.
docker pull ghcr.io/servienta/engine:latest mkdir -p fixtures && echo hello > fixtures/hello.txt docker run --rm -p 8080:8080 -p 8081:8081 -p 9000:9000 \ -v "$PWD/fixtures:/fixtures:ro" ghcr.io/servienta/engine:latest # discover endpoints, serve a fixture byte-for-byte curl http://localhost:8080/api/v1/endpoints curl http://localhost:8081/hello.txt # declare a run, send traffic, read it back curl -X PUT http://localhost:8080/api/v1/runs/run-1 -d '{"sources":["172.17.0.1"]}' echo 'hello from my app' | nc localhost 9000 curl 'http://localhost:8080/api/v1/received/reference?run=run-1' # reset to a known state curl -X POST http://localhost:8080/api/v1/reset
Your app can't put a run id inside a raw syslog packet, so a run claims the source addresses its traffic comes from. The engine attributes each message to a run by source; one source belongs to one active run.
Receivers
Send to a service, read it back parsed at /api/v1/received/<service>?run=<id>.
Response control
Steer what RADIUS, TACACS+, DNS, and NTP answer, for the run. Default is a successful reply; reset restores it.
curl -X PUT .../api/v1/responses/radius -d '{"outcome":"reject","reason":"locked"}'
curl -X PUT .../api/v1/responses/dns -d '{"outcome":"nxdomain"}'
curl -X PUT .../api/v1/responses/ntp -d '{"outcome":"stratum","stratum":5}'
curl -X PUT .../api/v1/responses/dns -d '{}' # restore default
Fault injection
Force failures on both sides to test your error paths. Reset lifts them.
# file faults: auth-reject | missing | truncate | corrupt curl -X PUT .../api/v1/faults/files/hello.txt -d '{"kind":"truncate"}' # receiver modes: refuse | drop | delay | cut | error curl -X PUT .../api/v1/faults/receivers/syslog -d '{"mode":"drop"}'
Licensing
A license enables a set of stands. It is an Ed25519-signed file, validated at startup, fully offline. The free image runs the file server and a demo receiver; the service receivers unlock with a license — Files, Standard, Enterprise, or a custom stand set.
# mount a license and start SERVIENTA_LICENSE=./license.json docker compose up -d # or paste it in the console: License card -> Apply a license
Offline licensing of self-hosted software is compliance clarity, not DRM: it makes what is enabled signed and verifiable, and prevents forgery. Every shipped credential is throwaway and documented as such.
API reference
Base path /api/v1 on the control port (default 8080). Full schema ships as openapi.yaml.