Skip to content

Installation

Requirements

Requirement Why
Linux x86_64 rsyscall-ng's supported platform; the process control depends on it
Unprivileged user namespaces The parser, the analysis worker and the pool all run in kernel-enforced namespaces. There is no degradation path: without them the harness exits 1 with NamespaceUnavailableError naming the fix (on Ubuntu: kernel.apparmor_restrict_unprivileged_userns=0)
Python 3.12+ StrEnum, and the type syntax the codebase uses throughout
uv Lockfile-only installs; CI asserts uv sync --locked
bash The syntax validator runs bash -n; the path is resolved once and recorded
A TrustSight build The thing being measured. See below.

Install

git clone https://github.com/emiliano-go/trustsight-harness
cd trustsight-harness
uv sync --locked --all-extras

--locked is not optional discipline. A harness that resolves fresh dependencies at test time is not the harness whose results were published, and CI fails if the lockfile and pyproject.toml disagree.

Check the install:

uv run python -m pytest -q

All tests must pass before any campaign is worth running; several of them are self-security gates rather than unit tests.

Container

The image ghcr.io/emiliano-go/trustsight-harness bundles both the harness and a pinned TrustSight checkout, so it needs no local sibling and measures exactly the commit the release pinned:

docker run --rm --security-opt seccomp=unconfined \
  ghcr.io/emiliano-go/trustsight-harness:latest regression
docker run --rm --security-opt seccomp=unconfined \
  ghcr.io/emiliano-go/trustsight-harness:latest coverage

--security-opt seccomp=unconfined (or a seccomp profile permitting clone(CLONE_NEWUSER)) is a requirement, not a tuning knob: the harness creates user namespaces in every process it spawns and refuses to run without them. The host kernel must allow unprivileged userns as well.

The entrypoint is python -m harness, so every command in the CLI reference is an argument. Mount a directory over the tree a command writes to keep its report, for example -v "$PWD/out:/app/trustsight-harness/regression".

The image is built from the commit in .github/trustsight-commit; to measure a different build, change that file and rebuild.

Pointing at the build under test

The harness measures a pinned TrustSight. By default pyproject.toml resolves it from a sibling checkout:

[tool.uv.sources]
trustsight = { path = "../trustsight", editable = true }

This is deliberate. PyPI lags the build under test; during this harness's own development, PyPI lagged the build being measured (0.17.2); and a campaign that silently measured a different version from the one it declared is exactly the failure the environment descriptor exists to prevent.

To measure a released version instead, drop the [tool.uv.sources] block and pin the release:

dependencies = ["trustsight==0.17.2", ...]

Then re-lock:

uv lock && uv sync --locked

The declared version must match the installed one

Every campaign declares environment.trustsight_version. At startup the harness imports TrustSight, reads __version__, and refuses to run if the two differ. "latest" is rejected outright: a record saying "latest" records nothing, because the same file replayed next month measures a different tool and cannot say so.

Optional extras

Extra Contents Needed for
llm httpx LLM campaigns only
mcp mcp MCP server integration
docs zensical, seoslug Building the documentation site
dev pytest, ruff The test suite and the lint gate

uv sync --locked --all-extras installs all of them, which is what CI does.

Launching the MCP server

The server needs the mcp extra, and a bare uv sync prunes any extra it is not asked for, so a one-time sync can leave harness-mcp exiting with "the MCP server needs the mcp extra". Launch it through uv run instead, which re-asserts the extra on every start:

uv run --project <harness-checkout> --extra mcp harness-mcp

analyze_diff runs in an isolated .mcp-scratch config. The server regenerates that scratch rules.toml from the running build on startup, so upgrading TrustSight cannot leave a stale ruleset that makes every analysis report the ruleset_drifted coverage gap. No manual cleanup is needed.

Pre-commit hook

The secret scan runs in CI, but CI catches a key after it is pushed; which is after it is public. Install the local hook too:

pre-commit install

.pre-commit-config.yaml wires up the secret scan, ruff, and the self-security gates.

Next

Running a Campaign →