Skip to content

CONTRIBUTING

github-actions[bot] edited this page Jul 19, 2026 · 7 revisions

Contributing to kvm-pilot

Thanks for considering a contribution. This project aims to stay small, honest about what it has and hasn't been tested against, and dependency-light.

Dev setup

git clone https://github.com/DustinTrap/kvm-pilot
cd kvm-pilot
pip install -e ".[dev,totp,ws]"

Before opening a PR

ruff check .                                # lint (and `ruff format .` if you like)
mypy src/kvm_pilot                          # types
pytest                                      # tests
bandit -c pyproject.toml -r src/kvm_pilot   # SAST — CI gates on this
pip-audit                                   # dependency CVEs — CI gates on this

All of these are installed by the [dev] extra above; run pip-audit inside the project venv so it scans the same environment CI does.

CI runs the lint/type/test trio on Python 3.11, 3.12, 3.13, and 3.14, plus a security job (bandit + pip-audit), a redfish-integration job that drives the Redfish CLI path end-to-end against the DMTF-conformant sushy-tools emulator, and an emulator-stack job that brings up compose.yaml via make emulators and runs the same integration tests through it. PRs should keep them all green. If your change touches the Redfish driver or CLI dispatch, run what CI runs: pip install "sushy-tools==2.2.0" && pytest tests/integration -m integration (without sushy-emulator on PATH those tests silently skip).

Principles

  • Client/driver code stays stdlib-only at import time, but pip install kvm-pilot ships everything a user needs — CLI, skill, and MCP server. A user-facing surface lives under src/kvm_pilot/ and its runtime dep is a base dependency (mcp for the server); feature deps like totp / ws stay optional extras, imported lazily. Don't hide a user-facing surface behind an extra.
  • No hard-coded model versions. The vision backends resolve or accept a model at runtime; don't bake a version string into the code.
  • Destructive operations are gated. If you add a method that can change a target's running state (power, reset, media, GPIO, resets), add it to DESTRUCTIVE_OPS and route it through self.safety.guard(...).
  • Be honest about hardware. If you've tested on real hardware, say which device and firmware in the PR. The compatibility table in the README should reflect what's actually been verified versus assumed.
  • New docs pages must be registered. See Adding a doc below — CI enforces every step (build_wiki.py --check, #175/#221).

Adding a doc

Every doc has one obvious home (#221). Pick the row that matches what you're writing, name the file accordingly, and register it — CI fails the build until all three navigation surfaces agree.

You are writing... Section Naming
A task walkthrough (how to accomplish something) Guides <topic>.md, imperative topic (unattended-install.md); operator bring-up guides end in -onboarding.md
What exists and how it behaves (a subsystem, driver, or surface) Reference named after the subsystem (redfish.md, amt.md, cli.md)
A procedure executed against real hardware Runbooks & test plans *-test-plan.md (or *-runbook.md)
A design decision or RFC Design records add to decisions.md if it's one decision; a new file only for a full RFC (reflexes.md)
A dated session-level review narrative Analysis (internal reports) docs/analysis/YYYY-MM-DD-slug.md — the only subdirectory

Then, mechanically:

  1. Put the file flat in docs/ (kebab-case). No new subdirectories — structure lives in the navigation manifest, not the filesystem, because published wheels and llms.txt consumers hold links to today's paths.
  2. Register it in PAGES in build_wiki.py with its section (or OPT_OUT with a reason). The wiki sidebar is generated from this.
  3. Link it from the matching section of docs/README.md.
  4. Add it to the root llms.txt if it's agent-operating material, otherwise to LLMS_OPT_OUT in build_wiki.py with a reason.

python .github/scripts/build_wiki.py --check (run by CI) verifies all of it: registration, hub link, llms.txt membership, and dead relative links.

Hardware reports welcome

You don't need to write code to help. If you run kvm-pilot against a device not in the compatibility table (PiKVM v3/v4, BliKVM, GL-RM1, etc.), open an issue with what worked and what didn't — that's directly useful.

Testing without hardware

The test suite mocks the HTTP and vision layers, so you can run and extend it with no device. See tests/conftest.py for the fakes. For end-to-end transport coverage, tests/test_emulator.py drives the real KVMClient against a pure-stdlib fake kvmd (tests/emulator.py) on 127.0.0.1 — no Docker, runs on macOS and Linux.

Emulator stack (make emulators)

With Docker installed, make emulators (or docker compose up --wait) stands up the local emulator stack from the root compose.yaml (#21):

Service Emulator Endpoint Credentials
redfish sushy-tools 2.2.0 (--fake) http://127.0.0.1:8000/redfish/v1/ Basic auth accepted but not checked

Point the CLI at it:

kvm-pilot --driver redfish --host 127.0.0.1 --port 8000 --scheme http \
  --redfish-auth basic --user admin --passwd password info

make integration runs tests/integration against the running stack (it sets KVM_PILOT_REDFISH_URL=http://127.0.0.1:8000, the first source the integration conftest checks). make emulators-down stops the stack, make emulators-logs follows it. The stack publishes on loopback only — an emulator answering to fake credentials must never listen on the LAN — and the port is fixed at 8000; if that collides with something local, edit compose.yaml.

kvmd-testenv (Linux only): make kvmd-testenv clones pikvm/kvmd into .kvmd-testenv/ and delegates to upstream's own make run, which serves the real kvmd API on http://127.0.0.1:8080 (https on 4430), credentials admin/admin. Run healthcheck as first contact (the intake gate), then e.g.:

kvm-pilot --driver pikvm --host 127.0.0.1 --port 8080 --scheme http \
  --user admin --passwd admin info

Prerequisites are hard: upstream's recipe runs a privileged container, sudo modprobes the gpio_mockup kernel module, and passes through a V4L2 /dev/video0 (a webcam, or v4l2loopback-dkms) — it exits early without them, so this target cannot run on macOS or in a stock CI runner. It is not a compose service for the same reason. Automated tests against it are tracked in #16.

ipmi_sim is absent from the stack until #62 lands an IPMI driver.

Recommended Claude skills

If you use Claude Code, these skills help keep contributions consistent with the project's standards (optional aids — the CI checks above remain the gates):

  • /security-review — run before opening a PR. This project drives real hardware and handles credentials + secret redaction, so security review matters.
  • /code-review — review your own diff for bugs and CLAUDE.md compliance.
  • /claude-api — read before changing anything under src/kvm_pilot/vision/; it covers current Anthropic model ids and vision parameters.
  • /find-skills — discover other useful skills in the ecosystem.

Clone this wiki locally