Syver: Server Validation From a YAML Spec

SYstem VERification in a single self-contained binary: generate a spec from a running host, validate against it, or serve it as a health endpoint

Syver, a Krameff Solutions project

What Syver Validates

A build that succeeded is not the same as a build that is correct. The playbook exited zero, the image published, the container started, and none of that tells you whether the service is actually listening on the port you think, whether the package that got installed is the version you pinned, or whether a later change quietly undid the first one.

Syver is a YAML based alternative to serverspec for validating a server's configuration. You describe the state a host is supposed to be in, and Syver checks it. The spec is a plain YAML file that lives in the repository next to the code that builds the host, so the definition of correct is reviewed, versioned, and diffable like everything else.

It covers package, file, addr, port, service, user, group, command, dns, process, kernel-param, mount, interface and http, plus registry on Windows, with imports so a large estate can share common blocks rather than copying them. It ships as one self-contained binary under 20MB with no runtime to install, which is what makes it practical to drop into a container build or a golden image pipeline.

Three Ways to Run It

The same spec file serves all three, which is the point: what you assert at build time is what you keep asserting in production, rather than two separate sets of checks that drift apart.

Generate

syver autoadd sshd reads the current state of the host and writes the assertions for you. Starting from a machine that already works is far quicker than writing a spec from scratch, and it catches the details you would not have thought to assert.

Validate

syver validate runs the suite and exits non-zero on failure, which is all a CI job needs. Small and medium suites finish near instantaneously, so it is cheap enough to run on every build rather than nightly.

Serve

syver serve exposes the same spec as an HTTP health endpoint. A Kubernetes readiness probe or a load balancer check can then ask the question the spec already answers instead of settling for "the process is up".

There is also syver render for templated specs with variable injection, syver add for building a spec up by hand, and dsyver, dcsyver and ksyver wrappers for Docker, Docker Compose and Kubernetes.

Coming From goss

Syver is the renamed continuation of our krameff/goss fork, which is now archived. The original is goss-org/goss by @aelsabbahy; all of that work remains under Apache 2.0 and full credit goes to the original author for a genuinely solid foundation.

Syver has since diverged. The work is driven by what clients actually need rather than by tracking upstream, so the two projects will keep differing and release-for-release parity is not a goal. The file format is a separate matter, and it is held stable on purpose. The product renamed; your specs did not.

Your gossfiles keep working

gossfile: is still the canonical key and still what gets written out. goss.yaml and goss.yml are still probed, alongside the syver names. Syntax, resource types and matchers are unchanged, so there is no spec rewrite to schedule.

Your environment variables keep working

All sixteen GOSS_* variables are still honoured, permanently rather than as a deprecation. SYVER_* takes priority where both are set, and an exported but empty SYVER_* is treated as unset so it never shadows a real GOSS_*.

Your CI parsers keep working

The JUnit suite name is still goss, the Nagios prefixes are still GOSS OK and GOSS CRITICAL, and goss_tests_* Prometheus metrics are still emitted with the Syver names alongside them rather than instead of them. Whatever reads your output does not need touching.

Your wrappers keep working

dgoss, dcgoss and kgoss still ship and still work as shims, and the goss-<os>-<arch> release archive is still published. Both are kept for one major version rather than indefinitely, so they are a migration window, not a promise.

What does break: the name

The binary is syver, so anything that shells out to goss by name needs one symlink. The outbound User-Agent on http checks, the checksum filename, and the container image all carry the new name too. Only relevant if you assert on them.

What does break: the library API

If you import this as a Go library rather than running the CLI, the module path moved and the Resource interface and entry points now take a context.Context first. That is what makes interrupt handling work, and it is the one break that is not just a rename.

Full goss vs Syver comparison

What We Changed on Purpose

Three behaviours differ from upstream. None of them change what a spec file means, and a run that is allowed to finish produces identical results either way. Each came out of something going wrong in front of a client.

Ctrl-C stops the work

goss mints a fresh context inside each check, so interrupting a run leaves any command it had already started to finish on its own, orphaned. Syver threads the signal handler's context all the way down to exec, so the child dies with the parent.

syver serve also shuts down in an orderly way on SIGTERM rather than being killed mid-request, and a second signal always terminates the process outright, so nothing can become unkillable.

Unrecognised keys warn

A typo in a top-level key used to drop the entire block under it and validate the rest of the file as though it had never been written, still exiting zero. A spec that silently skips a section you believed was being checked is worse than one that fails.

Syver now logs a [WARN] naming the file, the line, and a suggested fix where the key is close to a real one. Worth running over a large spec collection: a file that validated clean under goss may turn out to have been ignoring a block the whole time.

The metrics endpoint works

/metrics served an empty registry under goss, which is a quiet way to have monitoring that reports nothing and looks healthy doing it. It now serves the real metrics.

CentOS 7 has also been dropped from the test matrix. It is end of life, and its systemd never activates services inside a container, so the results it produced were not telling us anything true.

Where It Fits Alongside Compliance Work

Hardening and validation answer different questions. A CIS or STIG role applies a control; it does not, by itself, tell you six months later that the control is still in place. Configuration drifts, an emergency change gets made at 2am, a base image is rebuilt from a different source, and the evidence that anything is still compliant is a playbook run from last quarter.

Validation closes that gap. A spec is an assertion about the state of a host that can be run on demand, on a schedule, or as a health check, and its output is a JUnit or JSON artefact an auditor can actually read. It is the difference between "we applied the benchmark" and "here is the host, checked this morning, and here is what it said".

That is how we use Syver in practice: the hardening work we do with Ansible-Lockdown applies the controls, and a validation spec proves they held. Neither is much use without the other.

View on GitHub Our Other Projects

Syver Questions

Syver is the renamed continuation of the krameff/goss fork of goss-org/goss, which is now archived. It began as a fork and has since diverged: the work is driven by client requirements rather than by tracking upstream, so release-for-release parity with goss-org/goss is not a goal. The spec file format is a separate matter and is deliberately held stable.

No. The product renamed, the file format did not. The gossfile: key is still canonical, goss.yaml and goss.yml are still probed, all sixteen GOSS_* environment variables are still honoured, the --gossfile and -g flags still work, and gossfile syntax, resource types and matchers are unchanged.

Almost everything that breaks is something that referenced the product by name: the binary is syver rather than goss, the outbound User-Agent on http checks is syver/<version>, the checksum file is syver_<version>_SHA256SUMS, and the container image is ghcr.io/krameff/syver. If you import the project as a Go library rather than using the CLI, the module path changed and the Resource interface and library entry points now take a context.Context first. Anything that shells out to goss by name can be fixed with a symlink.

Linux has full support and is tested against AlmaLinux 10, Alpine 3, Arch, Debian Bullseye, Ubuntu Jammy and Rocky Linux 9. Windows and macOS support is alpha, with some resources such as port only partly implemented outside Linux. The registry resource is Windows only.

rspecish is the default, alongside documentation, json, structured, tap, junit, nagios, prometheus and silent. The JUnit suite name and the Nagios GOSS OK and GOSS CRITICAL prefixes are deliberately unchanged so existing CI parsers keep working, and goss_tests_* Prometheus metrics are still emitted with syver_tests_* alongside them rather than instead of them.

Yes to both. Syver is free and open source under the Apache 2.0 licence, and always will be. We also offer paid support and maintenance arrangements for it, as we do for all of our open source projects, so there is someone to call when an upgrade needs planning or a spec does not hold up against your estate. Talk to us about support and we will scope it around what you run. Security issues should be reported privately to security@krameff.com rather than as a public issue.

Want Validation Wired Into Your Pipeline?

Writing a spec for one host is an afternoon. Deciding what is worth asserting across a mixed estate, wiring it into CI and health checks, and turning the output into evidence someone will accept at audit is the harder half. That is the part we help with, and we offer ongoing support once it is in place.

Talk to Us