Contributing
Contributing to cubby.network
Thanks for your interest in contributing. This project is an open-source, deterministic NetOps control plane with an LLM-driven agent layer on top. Because the harness eventually controls real production networks, code quality and safety bars are high. This document explains how to work in the repo and what we expect from a pull request.
TL;DR
- Fork the repo and create a feature branch off
main. - Run
pytestlocally — the suite must be green before you push. - Add tests for everything you change. Adapters and validators must ship with golden fixture coverage.
- Open a PR that explains the why, not just the what. Link any related issue or architecture decision record (ADR).
- Sign off every commit with a DCO line (
git commit -s).
Branching, versioning, and releases
We use GitHub Flow with semantic-version tags:
mainis always deployable. CI gates every merge; nothing lands onmainthat isn't green.- Feature branches are short-lived and topic-named:
feat/<what>,fix/<what>,docs/<what>,chore/<what>. Rebase againstmainbefore opening the PR so the history stays linear. - One logical change per PR. Tests ride with the change. Infrastructure churn (formatting, imports) lands in its own PR so reviewers can see the real diff.
- Versioning is SemVer. Pre-1.0, breaking changes can land in
0.MAJOR.MINORbumps. At 1.0 the HTTP + CLI surface freezes and subsequent breaks require a major. - Pre-release chain for beta:
v0.x.y-alpha.N— internal / friendly testersv0.x.y-beta.N— external betav0.x.y-rc.N— release candidatev0.x.y— stable milestone
- Releases are cut from tags on
main. Seedocs/RELEASE.md. - Hotfixes branch from the tag being patched → PR to
main→ tag a patch release → cherry-pick forward as needed.
Developer Certificate of Origin (DCO)
By contributing you certify that your contribution is made under the terms of the Developer Certificate of Origin 1.1. Sign off every commit:
git commit -s -m "feat: …"
This appends Signed-off-by: Your Name <[email protected]> to the commit message. The DCO bot on GitHub checks every PR.
Project layout
| Path | What lives there |
|---|---|
apps/api, apps/cli, apps/worker, apps/scheduler | Process entry points. |
packages/domain | Typed entities, enums, dataclasses. The single source of truth for shapes. |
packages/sdk | Plugin contracts (ABCs), the registry, and SDK errors. Every plugin must inherit from the matching ABC and set simulated. |
packages/orchestrator | Workflow state machine, evidence service, signing. |
packages/policy, packages/validation, packages/execution, packages/verification | The deterministic core. |
packages/state | Authoritative intended/observed state, topology graph store. |
packages/discovery | Boot-crawl: BFS LLDP/CDP/SNMP probing. |
packages/transport | One interface for SSH/NETCONF/RESTCONF/gNMI/SNMP/REST. Adapters compose transports. |
packages/knowledge | Document store, embeddings, retrieval. |
packages/agents | LLM-backed reasoning workers + safety gate. Agents are read-only. |
packages/autonomy | Digital twin, forecasting, multi-agent coordinator, self-improvement. |
plugins/device, plugins/auth, plugins/inventory, plugins/telemetry, plugins/ticketing, plugins/validators, plugins/knowledge_ingestors, plugins/workflow_packs | Concrete adapters and packs. Add new vendors here. |
tests/unit, tests/contract, tests/integration | Test tiers. Unit tests must run offline. |
Hard rules
These are non-negotiable. PRs violating them will be sent back without deeper review.
- Adapters declare
simulated. Every concrete plugin setssimulatedtoTrue(in-memory or canned) orFalse(talks to real systems). The registry refuses to register a plugin without it. - Real adapters use the transport layer. Do not hand-roll SSH inside an adapter. Compose a
Transportfrompackages.transport. - Agents are read-only. Tools must inherit from
packages.agents.tools.Tooland haveread_only = True. The runtime's safety gate enforces bothallowed_toolsmembership andread_onlyat every call. - Evidence is signed and chained. Do not bypass
EvidenceService. Bundles get a SHA-256, a signature (HMAC by default, Ed25519 in prod), and aprev_sha256link. - API routes require a validated principal. Do not trust
requested_byoractor_rolesfrom the request body —stamp_principaloverrides them. - No new dependencies without an extras group. Heavy libraries (
scrapli,ncclient,pgvector,sentence-transformers,hvac,anthropic) live behind optional extras so the base install stays small. - Tests must be deterministic. No real network, no real LLM calls, unless gated by an environment variable (
NETOPS_DEVICELAB=1,NETOPS_LLM=1,NETOPS_VAULT=1,NETOPS_PGVECTOR=1).
Adding a new device adapter
- Create
plugins/device/<vendor>_<os>/. Inherit frompackages.sdk.contracts.DeviceAdapterand set:name,vendor,os_family,transports,simulated = False(orTrueif you can't reach a real device yet),capabilities(frozenset ofsnapshot,render,precheck,execute,rollback,verify).
- Compose a
Transportfrompackages.transport. Don't importscraplidirectly — request the right transport from the registry. - Write parsers in
plugins/device/<vendor>_<os>/parsers.py. Pure functions, no I/O. Cover them with golden tests undertests/unit/plugins/device/<vendor>_<os>/test_parsers.pyusing captured real device output stored intests/fixtures/<vendor>/<os>/. - Wire the adapter into
packages/orchestrator/bootstrap.pyso the registry knows about it. - Add an integration test under
tests/integration/plugins/device/...marked@pytest.mark.devicelab. It must skip cleanly whenNETOPS_DEVICELABis not set.
Adding a new validator
- Create
plugins/validators/<name>/plugin.py. Inherit fromValidatorPluginand implementsupports()andvalidate(). Return a dict withfindings,risk_score,blast_radius. - If your validator is a wrapper around an external system (Batfish, lab emulator), set
simulatedaccurately and put the heavy code path behind an extras dependency. - Add unit tests under
tests/unit/plugins/validators/<name>/.
Adding a workflow pack
Workflow packs are pure metadata. Create plugins/workflow_packs/<name>/pack.py with the required attributes from WorkflowPack. The orchestrator picks up new packs automatically once they are registered in bootstrap.build_demo_harness.
Tests and CI
- Unit tests (
tests/unit/) run offline, fast, deterministic. - Contract tests (
tests/contract/) verify ABC compliance and cross-implementation behavior (e.g., topology stores). - Integration tests (
tests/integration/) may touch the network or external systems. They are gated bypytest -mmarkers and environment variables.
Run pytest -q before pushing. CI runs the same suite plus linters.
Style
- Format with
blackand lint withruff. The pre-commit hook runs both. - Type hints are encouraged but not strictly enforced. New code in
packages/sdkandpackages/transportmust be fully typed. - Docstrings follow the same style you see in
packages/sdk/contracts.py: module-level prose explaining the why, then concise per-symbol notes.
Commit messages
Use imperative present tense ("Add NX-OS adapter", not "Added"). Keep the subject under 72 characters. The body should explain why the change matters.
Reporting security issues
Do not open a public GitHub issue. See SECURITY.md.
Code of conduct
Be kind, be professional, assume good faith. The full text is in CODE_OF_CONDUCT.md.