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:
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:
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:
Then re-lock:
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:
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-config.yaml wires up the secret scan, ruff, and the
self-security gates.