| name | netbox-super-cli |
| description | Use when the user asks to query, create, update, or delete NetBox infrastructure data (devices, sites, racks, IPs, prefixes, VLANs, tenants, etc.) via `nsc`. Drives the dynamic NetBox CLI generated from the live OpenAPI schema. |
| when_to_use | The user has `nsc` installed (`pip install netbox-super-cli` or `uv tool install netbox-super-cli`) and asks anything that maps to a NetBox resource — listing, creating, updating, deleting, bulk operations, or audit-log inspection. Also use when the user is debugging a NetBox automation that calls `nsc`. |
netbox-super-cli (nsc)
nsc is a dynamic NetBox CLI: every subcommand is generated from the live
OpenAPI schema of the NetBox instance you point it at. There is no hand-curated
endpoint list to go stale.
What's installed
nsc is on PATH after pip install netbox-super-cli.
- Config lives at
~/.nsc/config.yaml.
- Audit log lives at
~/.nsc/logs/audit.jsonl (one JSON-line per request/response;
passwords are redacted). defaults.audit_redaction controls how much is
kept: safe (default) masks sensitive headers/fields but keeps bodies;
full drops all bodies/headers/query and leaves only
{method, url, status_code, timestamp, profile} (url stripped of query and
user:pass@).
- Cache lives at
~/.nsc/cache/<profile>/<schema-hash>.json.
Command shape
nsc <tag> <resource> <verb> [args] [--apply] [--output json]
<tag> — the OpenAPI tag, e.g., dcim, ipam, tenancy, circuits, extras.
<resource> — plural form, e.g., devices, prefixes, tenants, vlans.
Curated singular forms are also accepted for common resources (device,
prefix, tenant, vlan, site, rack, interface, cable, tag).
<verb> — reads: list, get. Writes: create, update, replace,
delete, plus any custom actions the schema exposes. replace is a
PUT-with-id (full-object) replace; update is a PATCH. (Bulk variants exist
for write verbs via -f <file> — use a .ndjson / .jsonl extension to trigger
NDJSON mode.)
A few curated aliases skip the tag (ls, get, rm, search):
nsc ls <resource> — alias for nsc <tag> <resource> list.
nsc get <resource> <id_or_name> — fetch one (dereferences a non-numeric
name via name=).
nsc rm <resource> <id_or_name> — delete one (dry-run unless --apply).
nsc search <query> — /api/search/?q=<query>.
Plus interactive meta-commands (not aliases):
nsc tui (aka nsc interactive / nsc i) — launch the full-screen keyboard
TUI to browse, filter, edit, bulk-edit and search. Human-driven; not for
scripting. If a user wants to explore or edit NetBox visually, point them here.
nsc init — interactive config bootstrap.
nsc login — verify / create / rotate a profile's token.
nsc commands --schema <path-or-url> — dump the entire generated command
tree as JSON (useful for discovery; --schema is required here).
With shell completion installed, TAB now expands resource names, profiles
(nsc --profile <TAB>), and --status enum values. Completion reads the
on-disk cache only — no network call at TAB time — and silently offers
nothing if the cache is missing.
Dry-run / apply discipline
ALL writes are dry-runs by default. The --apply flag is the only path to
mutation:
nsc dcim devices create -f device.yaml # dry-run; prints what would happen
nsc dcim devices create -f device.yaml --apply # actually creates
Bulk writes use -f <file> with a .ndjson / .jsonl extension (one JSON
object per line) and the same --apply rule applies. Read commands (list,
get) ignore --apply — never paste it into a read by reflex. A --strict
delete of an object that does not exist exits 9. A malformed line in an
NDJSON/JSONL bulk input exits 4 (input_error); any other bad input — a
malformed single JSON/YAML document, an empty list, a missing -f file —
exits 6 (client).
Stable JSON output
The default output is table, but when stdout is not a TTY (piped, captured)
nsc auto-switches to json — overriding any configured default. Still,
pass --output json (or -o json) explicitly for everything an agent reads;
formats are exactly table, json, jsonl, yaml, csv. The NSC_OUTPUT env var
sets the default format (a --output/-o flag still wins over it).
nsc ls devices --output json | jq '.[] | select(.status.value == "active")'
Pick columns with --columns (comma-separated), including individual custom
fields via dotted paths: nsc ls devices --columns id,name,custom_fields.rack_role.
In the TUI the column chooser (f in a list view) lists each custom field as
its own toggleable row.
Errors come back as JSON envelopes (on stderr by default; on stdout when
--output json is set). The envelope shape is locked:
{
"error": "human-readable message",
"type": "auth | not_found | validation | conflict | rate_limited | server | transport | schema | config | client | internal | input_error | ambiguous_alias | unknown_alias",
"endpoint": "/api/dcim/devices/",
"method": "POST",
"status_code": 400,
"operation_id": "dcim_devices_create",
"details": { …field-keyed errors when applicable… }
}
Exit codes correspond to envelope type (the stable scripting contract):
0 success; 1 internal; 3 schema; 4 validation or input_error
(a malformed NDJSON/JSONL line); 5 server (5xx); 6 client (incl. a
malformed single JSON/YAML input, an empty list, a missing -f file);
7 transport; 8 auth (401/403); 9 not_found (404, also --strict
delete of an absent object); 10 conflict (409); 11 rate_limited (429);
12 config/profile; 13 ambiguous_alias; 14 unknown_alias. (Code 2 is
an argument/usage error from the CLI framework and is not part of the typed
contract.)
Common patterns
- Discover the surface:
nsc commands --schema <path-or-url> --output json | jq 'keys'
- List with filters:
nsc dcim devices list --site mysite --status active --output json
- Get one:
nsc dcim devices get 42 --output json
- Create from a YAML file:
nsc dcim devices create -f new-device.yaml --apply
- Bulk create from NDJSON:
nsc dcim devices create -f devices.ndjson --apply
- Update a field:
nsc dcim devices update 42 --status decommissioning --apply
- Delete:
nsc dcim devices delete 42 --apply (preview first WITHOUT --apply)
Performance: prefer one bulk call to many small ones
Each nsc invocation costs at least one HTTP round-trip to NetBox. When
operating on many objects, prefer one filtered list call to N individual
gets, and one NDJSON write to a shell loop of single writes. A loop of
1,000 patches is 1,000+ round-trips; a single NDJSON apply is one bulk
operation.
Read patterns
- Need every interface on a device? Use
list with a server-side
filter, not a loop:
nsc dcim interfaces list --device 42 --all --output json
nsc dcim devices get 42 --output json | jq '.interfaces[]' | \
while read id; do nsc dcim interfaces get "$id"; done
- Need a subset by some non-filterable property? Pull the wider set
once, then
jq locally:
nsc dcim interfaces list --device 42 --all --output json \
| jq '[.[] | select(.enabled == true and (.name | startswith("Gi")))]'
- Working across many devices? Scope once, filter locally. Don't
loop
nsc dcim interfaces list --device <id> over every device — pull
the broadest reasonable scope (a site, a role, a device type) in a
single call, then narrow with jq. Server-side filters compose, so
pick the tightest scope NetBox can apply for you:
nsc dcim interfaces list --site dc1 --all --output json \
| jq --argjson ids '[42, 43, 44]' \
'[.[] | select(.device.id as $d | $ids | index($d))]'
nsc dcim interfaces list --device_role leaf-switch --all --output json \
| jq 'group_by(.device.name)'
for id in 42 43 44; do
nsc dcim interfaces list --device "$id" --all --output json
done
The same pattern applies to IPs, cables, inventory items, and any
child resource: filter by the parent scope (site, tenant, role,
device-type), fetch once, partition locally.
- Pagination defaults:
list returns the first page (50 rows by
default). Pass --all to follow next links until exhausted, or
--limit N for a hard cap. nsc commands --schema <path-or-url> --output json reveals every resource's parameters (which fields can
be filtered server-side).
- Don't run
nsc commands per-resource in a loop. It serializes
the whole command tree; cache the output once per session.
Write patterns
- Bulk writes go through
-f <file>.ndjson --apply. One JSON object
per line; nsc streams them through a single bulk endpoint where the
schema supports it, otherwise issues one request per line with shared
auth and connection pooling — still much faster than a shell loop:
nsc dcim interfaces update -f changes.ndjson --apply
while read line; do
id=$(echo "$line" | jq .id)
nsc dcim interfaces update "$id" -f <(echo "$line") --apply
done < changes.ndjson
- Generate the NDJSON once with whatever scripting tool you prefer
(
jq, Python, awk). Don't re-run list between every patch.
- Speed up large bulk writes with
--workers N. Bulk write commands
accept --workers N (default 1, max 32) to keep N requests in
flight at once instead of one-at-a-time:
nsc dcim interfaces update -f changes.ndjson --workers 8 --apply
Per-record --on-error semantics are unchanged at any worker count, and
the audit log stays intact — appends are serialized so each record is
still exactly one well-formed JSON line. Start at the default and raise
it only when the record count is large; respect the 32 ceiling.
Schema fetches
nsc fetches /api/schema/ to build its command tree, then caches the
result under ~/.nsc/cache/<profile>/<schema-hash>.json. By default the
cache is trusted for 24h (schema_refresh: daily in
~/.nsc/config.yaml), so back-to-back commands don't repeat the schema
GET. Force a refresh with --refresh-schema (one-shot) or set
defaults.schema_refresh: on-hash-change if you need every invocation
to verify against the live schema. Other policies: manual (cache
indefinitely until manually refreshed), weekly (7-day TTL).
When something fails
- Check the exit code (
echo $?) — the type is in EXIT_CODES.
- Check the audit log:
tail -n 20 ~/.nsc/logs/audit.jsonl | jq.
The exact request and response are recorded.
- Re-run with
--debug for verbose logging on stderr.
What NOT to do
- DO NOT skip
--apply's dry-run by reflex. The dry-run is your one chance to
surface an objection BEFORE the wire request.
- DO NOT hand-curate endpoint lists in your reasoning. Use
nsc commands --schema <path-or-url> --output json instead — endpoints
change with NetBox versions.
- DO NOT authenticate per-command. Configure a profile (
nsc init then
nsc login) once at session start; --profile <name> switches between them.
- DO NOT assume every resource name has a singular alias. Resource names are
plural (
nsc ls devices). Curated singular forms also work for the common
resources — device, prefix, tenant, vlan, site, rack, interface, cable, tag
(so nsc ls device == nsc ls devices) — but outside that list, use the
plural. An unknown singular suggests its plural when one exists.
Profile management
nsc init — first-time setup; writes ~/.nsc/config.yaml.
nsc login — verify the active profile's token. nsc login --new --profile <name> --url <url> creates a new profile (then prompts
"Fetch and cache the live schema now?", default yes); --rotate --profile <name> replaces an existing token. --fetch-schema forces
a schema fetch without the prompt.
nsc profiles list — show configured profiles.
nsc --profile prod ls devices — one-shot profile override.
Cache management
The on-disk cache speeds up repeated invocations against the same NetBox
schema. By default the cache is trusted for 24h before re-fetching the
live schema (see Schema fetches above).
nsc cache prune — show what would be deleted (orphan profile dirs
and stale-hash files). prune is the only nsc cache subcommand.
nsc cache prune --apply — actually delete. Add --max-age N to
also prune cache files older than N days.
nsc --refresh-schema <subcmd> — force a one-shot live re-fetch
bypassing the TTL.
NetBox Device Type Library
The NetBox Device Type Library
is a community-maintained collection of device type YAML definitions for hundreds of
real-world hardware devices (routers, switches, servers, PDUs, firewalls, etc.). It is
the canonical source for importing device types you don't want to define by hand, and is
especially useful for demo setups.
Library YAML vs. NetBox API format
The library stores definitions in its own YAML dialect. The NetBox API (and therefore
nsc) uses a slightly different shape. The key differences:
| Library field | NetBox API field | Notes |
|---|
manufacturer: Cisco | manufacturer: {name: "Cisco", slug: "cisco"} | both name and slug are required |
interfaces: | separate POST /api/dcim/interface-templates/ | component templates are separate resources |
console-ports: | separate POST /api/dcim/console-port-templates/ | same pattern |
power-ports: | separate POST /api/dcim/power-port-templates/ | same pattern |
module-bays: | separate POST /api/dcim/module-bay-templates/ | same pattern |
Import workflow
git clone https://github.com/netbox-community/devicetype-library.git
ls devicetype-library/device-types/Cisco/
nsc dcim manufacturers list --slug cisco --output json
nsc dcim manufacturers create --field name=Cisco --field slug=cisco --apply
nsc dcim device-types create -f device-type-body.yaml --apply
DT_ID=$(nsc dcim device-types list --slug <slug> --output json | jq '.[0].id')
nsc dcim interface-templates create -f iface-templates.ndjson --apply
nsc dcim console-port-templates create -f console-templates.ndjson --apply
nsc dcim power-port-templates create -f power-templates.ndjson --apply
Device type body YAML (for nsc dcim device-types create -f)
Translate the library YAML into the API shape before passing to nsc:
manufacturer:
name: Cisco
slug: cisco
model: "Catalyst 2960-24TC-L"
slug: cisco-catalyst-2960-24tc-l
u_height: 1
is_full_depth: true
Component template NDJSON (one object per line)
Each component template must reference the parent device type's device_type ID:
{"device_type": 42, "name": "GigabitEthernet0/1", "type": "1000base-t"}
{"device_type": 42, "name": "GigabitEthernet0/2", "type": "1000base-t"}
Generate this from the library YAML with any scripting tool (jq, Python, etc.) before
passing to nsc via -f <file>.ndjson.
Demo / seed workflow
To quickly seed a fresh NetBox with common hardware for demos:
ls devicetype-library/device-types/
For each YAML file, apply steps 3–6 above. Because nsc dry-runs by default, you can
preview each step before committing.
What NOT to do
- DO NOT pass the raw library YAML directly to
nsc — it will fail schema validation
because manufacturer is a string in the library but a nested object in the API.
- DO NOT try to create component templates before the parent device type exists; they
all require a
device_type foreign key.
- DO NOT import duplicates blindly; check
nsc dcim device-types list --slug <slug>
first and skip if it already exists.
See also
nsc commands --schema <path-or-url> --output json — the full command tree.
- Project docs site (deployed on every release tag).
- NetBox Device Type Library — community YAML definitions.