contributing
Contributing — Overview
This page is the contributor's tour. It folds the repo's CONTRIBUTING.md into a fuller guide for someone arriving from outside the project. If you have not opened a PR against deputyOS before, read this end-to-end once…
This page is the contributor’s tour. It folds the repo’s
CONTRIBUTING.md
into a fuller guide for someone arriving from outside the project. If
you have not opened a PR against deputyOS before, read this end-to-end
once before you start writing code.
Before you start
- Read the architecture. Concepts → Architecture is the shortest path to a working mental model. The rest of this site is much easier to navigate once that page has been read.
- Read the profile-class rule. Whether your change is a profile or not, Concepts → Profile class explains the kind of system this is — and isn’t. A surprising number of proposals get a polite redirection because they are asking deputyOS to be a category of thing it is not.
- Look at recent issues and PRs. If your topic is already in motion, save yourself the duplicate effort. If a similar PR was recently closed without merge, the closing comment usually has the reason and what would unblock the same idea.
- Open an issue first for non-trivial changes. Architectural changes, new dependencies, new hardware targets, new milestones, and new ADRs all benefit from agreement on the approach before code lands. A typo fix or a clearly scoped bug fix can go straight to PR.
The load-bearing invariant The single non-negotiable design rule is zero first-boot network installs. Any change that introduces an
apt,npm,pip,cargo, orgit clonestep on the booted device — even an optional one, even gated behind a flag — will be rejected. See Concepts → Architecture for the rationale anddocs/adr/0002-zero-first-boot-network-installs.mdin the repo for the design history.
The development loop
You don’t need a Pi. You need a laptop:
git clone https://github.com/deputyos/deputyos.git
cd deputyos
make doctor # checks tools; tells you what to install
cargo test --all # 221 tests; the baseline
make try TARGET=qemu-aarch64 # ~12 min on a fresh checkout
# wizard at http://localhost:8088
The same Ansible role CI uses runs on your laptop — macOS (Apple Silicon and Intel), Windows (WSL2), and Linux (x86_64 and arm64) are all first-class build hosts. There is no privileged build environment. Per-host specifics are in Build → Image bake internals, and Build → Make targets is the inventory of every Make target.
The iteration loop:
- Edit (Rust, Ansible, Markdown — whatever your change touches).
cargo test --allif Rust changed.make build TARGET=qemu-aarch64if the image needs to change.make try TARGET=qemu-aarch64to boot the new image and exercise it.make smoketo run the QEMU smoke harness.make ci SCAFFOLD_PHASE=1to re-run the same checks CI runs.- Commit, push, open a PR.
Workspace structure
deputyOS is a five-crate Cargo workspace plus a shared Ansible role plus per-target Packer / cloud-init artefacts. The crates are:
| Crate | Purpose | Reference |
|---|---|---|
deputyctl | The operator CLI. Reads profile manifests, drives systemd, validates keys, performs updates, runs backups, prints capability limits. The single binary that owns the management surface. | Reference → CLI → deputyctl |
deputywizard | First-boot HTTP server on :8088. Writes /etc/deputyos/secrets.env. Profile-aware, profile-agnostic. | Reference → CLI → deputywizard, Reference → APIs → wizard HTTP |
deputypwa | Always-on web dashboard and built-in private chat client. Replaces the wizard on :8088 after first-boot. | Reference → CLI → deputypwa, Reference → APIs → PWA HTTP |
deputyos-track | Release-tracker bot. Polls upstreams every 30 minutes, opens PRs that bump pinned profile versions. | Reference → CLI → deputyos-track |
deputyos-desktop | Desktop launcher (~5 MB). Mandates the platform-native hypervisor — WSL2 on Windows, UTM on macOS, qemu+KVM on Linux. Downloads the latest signed image and boots it. | Reference → CLI → deputyos-desktop, Distribution → Desktop launcher internals |
Outside the Rust workspace:
roles/deputyos/— the shared Ansible role. The single source of truth for what goes into an image. Variant gates per hardware target. See Build → Image bake internals.packer/— Packer templates, one per buildable hardware target.cloud-init/—cloud-initrecipes for cloud targets that don’t get a baked image (Hetzner, Vultr, Linode).wsl/,macos/— host-specific tarball / qcow2 packagers.templates/— community templates (Proxmox, Unraid, TrueNAS).fly/— Fly.io OCI artefact.test/— QEMU smoke harness fixtures.profiles/— the three profile manifests.dist/— release-pipeline outputs, gitignored.
Adding things
The recipes for the most common contributions are in How-to:
- How-to → Add a profile — six files, zero Rust changes. The reference is the Khoj PR.
- How-to → Add a hardware target
— five files: variant task, Packer template, smoke fixture,
limits.jsonrow,main.ymlimport. - How-to → Add a hook — drop a script in
/etc/deputyos/hooks.d/<kind>/; payload schemas are in Reference → Schemas → hook payloads. - How-to → Add a model provider
— extend
deputyctl/etc/providers.json. - How-to → Add a channel — channel registry, ufw rule, AppArmor mediation.
What we accept (the short list): new profiles in the profile class, new hardware variants, bug fixes to the workspace crates or the build pipeline, doc fixes, new ADRs documenting architectural decisions we missed.
What we don’t accept: profiles outside the class, changes that re-introduce first-boot network installs, runtime dependencies without arm64 + x86_64 prebuilt binaries, features that expose the agent to the public internet by default (opt-in is fine), and code that bypasses signature verification on the update path.
Code style
| Language | Tool | Settings |
|---|---|---|
| Rust | rustfmt | Defaults. Run with cargo fmt --all. |
| Rust | clippy | cargo clippy --all-targets --all-features -- -D warnings. Warnings are errors in CI. |
| Ansible | ansible-lint | At the production profile. Run with make lint. |
| YAML | yamllint | 2-space indent. Configured via .yamllint. |
| Shell | shellcheck | All scripts in roles/deputyos/files/ and scripts/. |
| Markdown | (no auto-formatter) | No trailing whitespace; hard-wrap at 100 cols inside paragraphs of prose; use mkdocs-material admonitions where useful. |
| Commit messages | (no enforced format) | Imperative mood. Short, specific PR titles. Conventional-commit prefixes are welcome but not required. |
The single rule: no commits land without make ci green. CI is the
contract; if CI is wrong, fix CI in the same PR.
Testing
| Surface | What to run | Where to add tests |
|---|---|---|
| Rust units / integration | cargo test --all (221-test baseline) | Adjacent mod tests blocks; tests/ for cross-crate integration; deputyctl/tests/ for CLI shape tests. |
| Rust public API | cargo doc --all builds clean | Doc-comment examples are run; keep them minimal and meaningful. |
| Ansible role | ansible-lint and make lint | If you add a task, add an assert or a command check that proves the post-condition. |
| QEMU smoke | make smoke | test/qemu/<hw>.cloudinit.yaml per target; test/smoke/<scenario>.sh for behaviour. |
| Provider library | cargo test -p deputyctl model::test_provider_key | Stub-server integration test for every new provider. |
| Schema validation | The schema files live in deputyctl/etc/. | Add a fixture under deputyctl/tests/fixtures/ that exercises the new field. |
A profile PR must include a smoke fixture that boots an image with the
profile selected, runs the wizard, validates that the gateway comes up
healthy, and tears down. A hardware-target PR must include both a
smoke fixture and a limits.json entry; the latter feeds the picker
page and the deputyctl limits output.
The 221-test baseline is the floor — adding a feature without adding tests is a signal something is wrong, not a sign the feature is small.
ADRs
Architecture Decision Records live at docs/adr/ in the repo. There
are eight today (numbered 0001 through 0008). The numbering is
sequential; the next ADR is 0009.
Write an ADR when:
- You are making an architectural change — something that ripples across more than one crate, or that future contributors will want to understand the why of, not just the what.
- You are making a security decision — adding a default-on control, changing a trust boundary, picking a crypto primitive, opting out of a hardening feature for a reason.
- You are changing direction — replacing a previously-decided approach with a new one. The new ADR supersedes the old one and should reference it explicitly.
You do not need an ADR for: a routine bug fix, a refactor that preserves behaviour, a new test, a doc improvement, a profile addition inside the existing class, or a hardware-target addition that uses the existing variant-gate pattern.
The format is short — a problem statement, the alternatives considered, the decision, and the consequences. Pattern-match on the existing eight ADRs.
The PR flow
- Open an issue first for anything non-trivial. State the goal, the alternatives you considered, and the rough shape of the change. Get a thumbs-up from a maintainer before you spend a weekend on it.
- Branch. No enforced naming;
feature/<short-slug>orfix/<issue-number>-<slug>is conventional. - Implement. Keep PRs focused. A 2,000-line PR that touches
deputyctl, the wizard, and the Ansible role is harder to review than three 700-line PRs each focused on one surface. - Test locally.
make ci SCAFFOLD_PHASE=1is the same set of checks CI runs. If it’s red on your laptop, it’s red in CI. - Open the PR. Reference the issue. State what you did and what you didn’t do. If the change touches a documented surface, update this site (the page you’re touching most likely lives under Reference or Concepts).
- Review. Reviewers will land on the PR within a few days. Expect feedback; expect to iterate.
- Merge. Squash-merge is the default. The PR title becomes the commit message; keep it specific.
Don’t forget the docs site If your PR adds a CLI flag, a TOML field, an HTTP route, or a systemd unit, the matching page in this site needs to be updated in the same PR.
mkdocs build --strictis part ofmake ci, so a broken cross-link will fail your build.
Cross-references
- Concepts → Architecture — the system view.
- Concepts → Profile class — what qualifies as a profile.
- Concepts → Plugin model — the contract that makes a profile a drop-in.
- Build → Make targets — every Make target, what it does, what it produces.
- Build → Image bake internals — pi-gen, Packer, the Ansible role, variant gates.
- Reference → CLI → deputyctl — the authoritative command surface.
- Security → Reporting vulnerabilities — the private-disclosure path. Vulnerability reports do not go through public issues.
Code of conduct
Be kind, especially to people newer than you. deputyOS is built for semi-technical users — and that includes contributors, sometimes. Patient review and clear writing are first-class contributions on their own.