Every driver in the NAPALM org follows one standard for TDD, clean code and repository
usage, and publishes releases to PyPI. Decided by Christian on 2026-10-22; this settles NetOrk/netork#734 as option A.
Decisions
Releases by hand, by tag. Version and CHANGELOG are bumped by hand, vX.Y.Z is tagged, CI publishes. No schedule.
PyPI names: free napalm-<x> names stay; only collisions get a new name (napalm-netgear belongs to a third party; napalm-vyos, -s350, -fortios are the upstream projects). The entry-point driver names do not change.
netOrk pins released == versions with hashes in a lock. A git SHA pin stays allowed as a justified exception.
Quality ratchet. ruff, ruff format and bandit hold from day one. mypy-strict exemptions and the coverage floor start at each repo's current state and only improve.
Defaults:main everywhere, Python >= 3.10, Apache-2.0, project links on git.netork.io, one PyPI token per project (Trusted Publishing does not work from Gitea), and the stale PyPI uploads of device-types 0.1.0 and tplink 0.1.x get yanked.
Phases
1. Standard: STANDARDS.md, the shared workflow driver.yml, napalm-standard (check, ratchet, release), template. Pilot findings in #1.
2. Pilot: device-types (NAPALM/napalm-device-types#52) and linux (NAPALM/napalm-linux#38). Both are green except pip-audit, which #1 fixes. Then the first release to TestPyPI, then PyPI.
3. Rollout to the other drivers in dependency order, one issue and one PR each (table below).
4. netOrk:vendor-drivers.txt with versions, requirements-drivers.lock with hashes, Dockerfiles without the install-order hack, --check-pins asks PyPI, docs.
5. Forks: vyos, s350, fortios and lancom, after their port onto device-types and under new names.
Needs Christian
A PyPI account with 2FA (optionally a PyPI organisation "NAPALM"), and a TestPyPI account.
Tokens as repo secret PYPI_TOKEN: account-wide for the first upload, then scoped to the project.
Also in the org, not yet in scope: napalm-vyos, napalm-s350, napalm-fortios, napalm-lancom (phase 5).
Every driver in the NAPALM org follows one standard for TDD, clean code and repository
usage, and publishes releases to PyPI. Decided by Christian on 2026-10-22; this settles
NetOrk/netork#734 as option A.
## Decisions
- **Releases by hand, by tag.** Version and CHANGELOG are bumped by hand, `vX.Y.Z` is tagged, CI publishes. No schedule.
- **PyPI names:** free `napalm-<x>` names stay; only collisions get a new name (`napalm-netgear` belongs to a third party; `napalm-vyos`, `-s350`, `-fortios` are the upstream projects). The entry-point driver names do not change.
- **netOrk pins** released `==` versions with hashes in a lock. A git SHA pin stays allowed as a justified exception.
- **Quality ratchet.** ruff, ruff format and bandit hold from day one. mypy-strict exemptions and the coverage floor start at each repo's current state and only improve.
- **Defaults:** `main` everywhere, Python >= 3.10, Apache-2.0, project links on git.netork.io, one PyPI token per project (Trusted Publishing does not work from Gitea), and the stale PyPI uploads of device-types 0.1.0 and tplink 0.1.x get yanked.
## Phases
- [x] **1. Standard:** STANDARDS.md, the shared workflow `driver.yml`, `napalm-standard` (check, ratchet, release), template. Pilot findings in #1.
- [ ] **2. Pilot:** device-types (NAPALM/napalm-device-types#52) and linux (NAPALM/napalm-linux#38). Both are green except pip-audit, which #1 fixes. Then the first release to TestPyPI, then PyPI.
- [ ] **3. Rollout** to the other drivers in dependency order, one issue and one PR each (table below).
- [ ] **4. netOrk:** `vendor-drivers.txt` with versions, `requirements-drivers.lock` with hashes, Dockerfiles without the install-order hack, `--check-pins` asks PyPI, docs.
- [ ] **5. Forks:** vyos, s350, fortios and lancom, after their port onto device-types and under new names.
## Needs Christian
- [ ] A PyPI account with 2FA (optionally a PyPI organisation "NAPALM"), and a TestPyPI account.
- [ ] Tokens as repo secret `PYPI_TOKEN`: account-wide for the first upload, then scoped to the project.
- [x] Netgear package name: **`napalm-netgear-switches`** (decided 2026-10-22).
- [x] `master` → `main`: each repo is renamed right before its adoption PR merges (decided 2026-10-22).
Old `Co-Authored-By` lines in the history of 13 repos are rewritten only after phase 4, once netOrk no longer pins commit SHAs (decided 2026-10-22).
## Repositories
| Repo | Issue | Status | ruff (fixable) | mypy strict | Coverage |
|---|---|---|---|---|---|
| napalm-device-types | NAPALM/napalm-device-types#54 | pilot, PR #52 | clean | baseline | 95 % |
| napalm-linux | NAPALM/napalm-linux#40 | pilot, PR #38 | clean | baseline | 58 % |
| napalm-openmediavault | NAPALM/napalm-openmediavault#8 | open (after linux) | 14 (fixable 4) | 5 in 1 file | 16% |
| napalm-qnap-qts | NAPALM/napalm-qnap-qts#10 | open (after linux) | 0 (fixable 0) | 9 in 1 file | 76% |
| napalm-amt | NAPALM/napalm-amt#1 | open | 0 (fixable 0) | 5 in 1 file | n/a |
| napalm-bsd | NAPALM/napalm-bsd#19 | open | 22 (fixable 20) | 27 in 4 files | 95% |
| napalm-fritzbox | NAPALM/napalm-fritzbox#6 | open | 10 (fixable 3) | 6 in 1 file | 81% |
| napalm-hpe-aruba-procurve | NAPALM/napalm-hpe-aruba-procurve#18 | open | 200 (fixable 188) | 1 in 1 file | n/a |
| napalm-hpe-officeconnect | NAPALM/napalm-hpe-officeconnect#5 | open | 0 (fixable 0) | 4 in 1 file | 92% |
| napalm-netgear | NAPALM/napalm-netgear#14 | open | 168 (fixable 159) | 139 in 2 files | 47% |
| napalm-openwrt | NAPALM/napalm-openwrt#20 | open | 24 (fixable 15) | 130 in 9 files | 65% |
| napalm-opnsense | NAPALM/napalm-opnsense#30 | open | 91 (fixable 48) | 50 in 7 files | 79% |
| napalm-proxmox | NAPALM/napalm-proxmox#32 | open | 97 (fixable 68) | 200 in 11 files | 69% |
| napalm-redfish | NAPALM/napalm-redfish#2 | open | 12 (fixable 12) | 50 in 8 files | n/a |
| napalm-sonos | NAPALM/napalm-sonos#5 | open | 5 (fixable 2) | 5 in 1 file | 71% |
| napalm-tplink-jetstream | NAPALM/napalm-tplink-jetstream#8 | open | 23 (fixable 5) | 96 in 1 file | 37% |
| napalm-vmware | NAPALM/napalm-vmware#7 | open | 4 (fixable 1) | 35 in 11 files | 97% |
| napalm-windows | NAPALM/napalm-windows#4 | open | 0 (fixable 0) | 1 in 1 file | 96% |
| napalm-yealink | NAPALM/napalm-yealink#4 | open | 11 (fixable 5) | 20 in 1 file | 42% |
| napalm-zyxel | NAPALM/napalm-zyxel#8 | open | 13 (fixable 3) | 24 in 1 file | 69% |
Also in the org, not yet in scope: napalm-vyos, napalm-s350, napalm-fortios, napalm-lancom (phase 5).
Found during the rollout (2026-10-23), not fixed in the adoption PRs
These are outside the standard's scope. Each one needs its own issue, or a decision.
A driver names its consumer (STANDARDS.md §3)
opnsense: writes the [netork] tag as the description of entries it creates. The ping job is called "netork ping sweep".
proxmox: creates /var/lib/vz/template/netork-images/netork-<key>-… and /tmp/_netork_hosts.
device-types (ssh_server) and linux: stage files through /tmp/netork-ssh.*, .netork-tmp and /tmp/netork-stage.*.
vmware: user-facing error texts name netOrk.
Many repos: comments and READMEs mention netOrk, netork#1167 or "NetOrk Authors" headers.
A failed read returns empty instead of raising (§3)
zyxel: _dal_query swallows every exception.
yealink: get_facts.
fritzbox: get_wan_status and get_facts.
sonos: all getters.
tplink: get_snmp_config reports an invented "public".
Policy in a driver
get_device_warnings in procurve, tplink and redfish.
Tests open sockets (§4)
fritzbox and sonos: napalm's NetworkDriver.__del__ calls is_alive() on a MagicMock connection, so the test makes a real TCP connect to fritz.box or 192.168.1.100 and waits 5 s per test.
Security-relevant
zyxel: fixed AES IV; ping() reports invented success; session.timeout has no effect, so requests run without a timeout.
TLS verify off by default: zyxel, yealink, procurve and officeconnect.
amt: a DTD in a UTF-16 answer slipped past the old <!DOCTYPE check. This is fixed in napalm-amt#2 with defusedxml, test first.
Contract and typing
sonos:get_playback_state and get_zone_info break the MediaDriver contract.
device-types: fingerprint attributes are declared as instance variables, while drivers declare them as ClassVar. This hits every strict driver (officeconnect).
Unfloored dependencies resolved to unusable versions:
netaddr 0.3.1 (no IPAddress)
urllib3 1.21
napalm 0.10
redfish < 3.4.1
pyghmi 1.5.0
These are fixed in the PRs; openwrt got its netaddr floor afterwards.
## Found during the rollout (2026-10-23), not fixed in the adoption PRs
These are outside the standard's scope. Each one needs its own issue, or a decision.
**A driver names its consumer (STANDARDS.md §3)**
- **opnsense:** writes the `[netork]` tag as the description of entries it creates. The ping job is called "netork ping sweep".
- **proxmox:** creates `/var/lib/vz/template/netork-images/netork-<key>-…` and `/tmp/_netork_hosts`.
- **device-types (`ssh_server`) and linux:** stage files through `/tmp/netork-ssh.*`, `.netork-tmp` and `/tmp/netork-stage.*`.
- **vmware:** user-facing error texts name netOrk.
- **Many repos:** comments and READMEs mention netOrk, `netork#1167` or "NetOrk Authors" headers.
**A failed read returns empty instead of raising (§3)**
- zyxel: `_dal_query` swallows every exception.
- yealink: `get_facts`.
- fritzbox: `get_wan_status` and `get_facts`.
- sonos: all getters.
- tplink: `get_snmp_config` reports an invented `"public"`.
**Policy in a driver**
- `get_device_warnings` in procurve, tplink and redfish.
**Tests open sockets (§4)**
- fritzbox and sonos: napalm's `NetworkDriver.__del__` calls `is_alive()` on a MagicMock connection, so the test makes a real TCP connect to `fritz.box` or 192.168.1.100 and waits 5 s per test.
**Security-relevant**
- **zyxel:** fixed AES IV; `ping()` reports invented success; `session.timeout` has no effect, so requests run without a timeout.
- **TLS verify off by default:** zyxel, yealink, procurve and officeconnect.
- **amt:** a DTD in a UTF-16 answer slipped past the old `<!DOCTYPE` check. This is **fixed in napalm-amt#2** with defusedxml, test first.
**Contract and typing**
- **sonos:** `get_playback_state` and `get_zone_info` break the MediaDriver contract.
- **device-types:** fingerprint attributes are declared as instance variables, while drivers declare them as `ClassVar`. This hits every strict driver (officeconnect).
- **Undeclared imports:** proxmox `yaml`, redfish `requests`, zyxel `cryptography`.
**Lowest-version floors (CI `lowest` job)**
- Unfloored dependencies resolved to unusable versions:
- netaddr 0.3.1 (no `IPAddress`)
- urllib3 1.21
- napalm 0.10
- redfish < 3.4.1
- pyghmi 1.5.0
- These are fixed in the PRs; openwrt got its netaddr floor afterwards.
Release backlog on the single NAPALM runner (capacity 1, 30+ runs): duplicate main-push runs are cancelled where a tag run tests the same commit; raising the capacity is drafted as christianmanivong/infrastructure#52 (for an infrastructure session).
Release backlog on the single NAPALM runner (capacity 1, 30+ runs): duplicate main-push runs are cancelled where a tag run tests the same commit; raising the capacity is drafted as christianmanivong/infrastructure#52 (for an infrastructure session).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Every driver in the NAPALM org follows one standard for TDD, clean code and repository
usage, and publishes releases to PyPI. Decided by Christian on 2026-10-22; this settles
NetOrk/netork#734 as option A.
Decisions
vX.Y.Zis tagged, CI publishes. No schedule.napalm-<x>names stay; only collisions get a new name (napalm-netgearbelongs to a third party;napalm-vyos,-s350,-fortiosare the upstream projects). The entry-point driver names do not change.==versions with hashes in a lock. A git SHA pin stays allowed as a justified exception.maineverywhere, Python >= 3.10, Apache-2.0, project links on git.netork.io, one PyPI token per project (Trusted Publishing does not work from Gitea), and the stale PyPI uploads of device-types 0.1.0 and tplink 0.1.x get yanked.Phases
driver.yml,napalm-standard(check, ratchet, release), template. Pilot findings in #1.vendor-drivers.txtwith versions,requirements-drivers.lockwith hashes, Dockerfiles without the install-order hack,--check-pinsasks PyPI, docs.Needs Christian
PYPI_TOKEN: account-wide for the first upload, then scoped to the project.napalm-netgear-switches(decided 2026-10-22).master→main: each repo is renamed right before its adoption PR merges (decided 2026-10-22).Old
Co-Authored-Bylines in the history of 13 repos are rewritten only after phase 4, once netOrk no longer pins commit SHAs (decided 2026-10-22).Repositories
Also in the org, not yet in scope: napalm-vyos, napalm-s350, napalm-fortios, napalm-lancom (phase 5).
Found during the rollout (2026-10-23), not fixed in the adoption PRs
These are outside the standard's scope. Each one needs its own issue, or a decision.
A driver names its consumer (STANDARDS.md §3)
[netork]tag as the description of entries it creates. The ping job is called "netork ping sweep"./var/lib/vz/template/netork-images/netork-<key>-…and/tmp/_netork_hosts.ssh_server) and linux: stage files through/tmp/netork-ssh.*,.netork-tmpand/tmp/netork-stage.*.netork#1167or "NetOrk Authors" headers.A failed read returns empty instead of raising (§3)
_dal_queryswallows every exception.get_facts.get_wan_statusandget_facts.get_snmp_configreports an invented"public".Policy in a driver
get_device_warningsin procurve, tplink and redfish.Tests open sockets (§4)
NetworkDriver.__del__callsis_alive()on a MagicMock connection, so the test makes a real TCP connect tofritz.boxor 192.168.1.100 and waits 5 s per test.Security-relevant
ping()reports invented success;session.timeouthas no effect, so requests run without a timeout.<!DOCTYPEcheck. This is fixed in napalm-amt#2 with defusedxml, test first.Contract and typing
get_playback_stateandget_zone_infobreak the MediaDriver contract.ClassVar. This hits every strict driver (officeconnect).yaml, redfishrequests, zyxelcryptography.Lowest-version floors (CI
lowestjob)IPAddress)Release backlog on the single NAPALM runner (capacity 1, 30+ runs): duplicate main-push runs are cancelled where a tag run tests the same commit; raising the capacity is drafted as christianmanivong/infrastructure#52 (for an infrastructure session).