Servienta
Documentation

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

Instance
One bring-up of the engine. Mutations — reset, faults, response controls — are instance-wide.
Run
One test execution inside an instance, identified by a run id you choose. Messages are read back per run.
Source claim
A run declares the source addresses whose traffic belongs to it. The engine attributes each message to a run by source — so two runs share one instance without reading each other's traffic.
Stand
One licensable service. The free image runs the file server (HTTP) and a demo receiver; a license unlocks the rest.

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

ServiceProtocolRecorded content
reference TCP line (demo, free) line
syslog UDP · TCP · RELP facility, severity, message
snmp-traps v2c · v3 USM variable bindings
radius / tacacs authentication username (reply steerable)
dns / ntp resolution / time query (reply steerable)
kafka broker (in-process) topic, key, value
ipfix collector flow record fields

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.

GET /version Contract version
GET /endpoints Service addresses
GET /license License status
PUT /runs/{id} Declare a run (claim sources)
DEL /runs/{id} Release a run
GET /received/{service} Read messages
POST /reset Reset the instance
PUT /responses/{service} Steer a reply
PUT /faults/files/{fixture} File fault
PUT /faults/receivers/{service} Receiver fault