Releases: lance0/nbox
Releases · lance0/nbox
v0.7.0
Added
- First-run orientation on the home screen. A fresh launch (or any time there
are no recent items yet) now lands on an oriented getting-started panel —/
search,j/k ↵browse a kind from the rail,Ddashboard,Tprefix tree,?
help — instead of a bare "Press / to search". Returning users with recents are
unaffected. - Connection status at launch, and a recoverable connection banner. A
successful start shows aconnected to NetBox vX.Yconfirmation in the footer. A
connection or auth failure (bad token, unreachable host) no longer hard-exits
before the TUI opens — it launches with an actionable banner ("Press S to edit
the profile or set NBOX_TOKEN"), so you fix the profile in-app and reconnect
without re-running the binary. A server below the supported NetBox floor stays a
hard, explicit error. - Edit the active connection from the Settings modal. The Config modal's
Settings section gains a Connection category for the active profile's
page_size,timeout_secs,exclude_config_context, and the per-surface
[api]vrf/route_targetbackends (rest/graphql). Saving a change
persists it to the profile (format-preserving) and reconnects so it takes effect
live. The profile editor still manages every profile; Settings is the quick-tweak
surface for the active one.
Performance
- Prefix detail loads its child prefixes and contained IPs concurrently — one
combined fetch after the prefix header instead of two sequential round-trips. - Search resolves the
--scopeand--vrfreferences concurrently before the
multi-endpoint fan-out, so a scoped + VRF-filtered search pays one resolution
tail instead of two.
Internal
- Deepened machine-facing contract tests: strict response-shape pins for the MCP
nbox_next_ip/nbox_next_prefix/nbox_cache_clear/nbox_journal/
nbox_list_tagstools, and CSV RFC-4180 quoting verified through the compiled
binary. - Refactors with no behavior change: bundled the profile-edit-form arguments
(ProfileFormData), shared one HTTP 429 retry policy across the REST and GraphQL
paths, and slimmedpersist_profile's signature. - Dependency maintenance:
sha20.11, several GitHub Actions bumps, and the Rust
toolchain pinned to the project MSRV (1.88) so CI keeps a low support floor.
What's Changed
- chore(deps): bump docker/build-push-action from 6 to 7 by @dependabot[bot] in #44
- chore(deps): bump actions/download-artifact from 4 to 8 by @dependabot[bot] in #42
- chore(deps): bump actions/checkout from 6 to 7 by @dependabot[bot] in #43
- chore(deps): bump docker/metadata-action from 5 to 6 by @dependabot[bot] in #40
- perf(detail): fetch prefix children and IPs concurrently by @lance0 in #48
- chore(deps): ignore dtolnay/rust-toolchain bumps (deliberate MSRV floor) by @lance0 in #49
- feat(tui): orient the empty home screen for first-run users by @lance0 in #50
- chore(deps): bump sha2 from 0.10.9 to 0.11.0 in the rust-dependencies group across 1 directory by @dependabot[bot] in #45
- refactor: bundle profile-edit-form args; share the 429-retry policy by @lance0 in #51
- feat(tui): recoverable connect banner + connected cue at startup by @lance0 in #52
- test: deepen CSV + MCP response-shape contracts by @lance0 in #53
- feat(tui): connection knobs in the Settings modal with hot-apply by @lance0 in #54
- refactor: persist_profile takes ProfileFormData; pin journal author key by @lance0 in #55
- feat(tui): api backends in the Settings Connection category by @lance0 in #56
- perf(search): resolve scope and VRF concurrently by @lance0 in #57
Full Changelog: v0.6.0...v0.7.0
v0.6.0
Added
nbox_get(MCP) acceptsip_addressas an alias forip. Anbox_search
result'skindisip_address(the one kind whose spelling differs from
nbox_get'sip; all others already match), so a search → get chain — and the
nbox://ip_address/<ref>resource URI — now works without translating the kind.- Edit more profile knobs from the in-app Config modal. The profile add/edit
form now sets the settings that used to need hand-editingconfig.toml:
timeout_secsandpage_size(numeric fields; empty = default),
exclude_config_context(Ctrl+E), and the per-surface API backends
[profiles.<name>.api] vrf(Ctrl+B) /route_target(Ctrl+R), each cycling
rest/graphql. REST backends and default/empty values leave the file clean
(no[api]table, no redundant keys), and writes stay format-preserving. The
API token is still never written toconfig.toml. - Drill into a prefix's children and contained IPs from the TUI. The prefix
detail's child-prefix and IP-address lists are now navigable tabs (cchildren,
aaddresses): select a row and press Enter to open that prefix or IP, with
b/Escwalking back through the drill path. Thenbox prefixCLI/JSON output
is unchanged. - Drill into a device's IPs and VLANs from the TUI. The device detail's
IP-address (p) and VLAN (v) tabs are now navigable — Enter opens that IP or
VLAN,b/Escwalks back. (Interfaces, cables, and services stay plain text;
they aren't standalone objects.) Thenbox deviceCLI/JSON output is unchanged. - Drill into a VLAN's prefixes from the TUI. The VLAN detail's prefix list is
now a navigableptab — Enter opens that prefix,b/Escwalks back. The
nbox vlanCLI/JSON output is unchanged. - Drill into a site's / rack's devices from the TUI. The site detail gains
navigabledevices(d) andracks(r) tabs, and the rack detail gains a
navigabledevices(d) tab alongside its elevation — Enter opens that device
or rack,b/Escwalks back. These lists are fetched best-effort (a fetch error
shows in the tab; the summary still loads). - The detail screen advertises its tabs and the drill action. The tab bar now
has a leadingtabs:label with the active tab bolded, and the footer shows
Tab tabs · Enter open— kind-agnostic (replacing the old device-only
i/p/c/v/shint), withEnter openshown only when the active tab actually has
navigable rows (so it never lies on a summary/text tab). Makes the navigable
detail lists discoverable instead of hidden behind an unmarked tab switch.
What's Changed
- ci(release): build the GitHub Release body from the CHANGELOG section by @lance0 in #27
- feat(tui): navigable prefix children + contained IPs by @lance0 in #28
- feat(tui): navigable device IPs + VLANs by @lance0 in #29
- feat(tui): navigable VLAN prefixes by @lance0 in #30
- feat(tui): navigable site/rack devices by @lance0 in #31
- chore(deps): bump ratatui 0.30.1 -> 0.30.2 by @lance0 in #32
- perf(tui): fetch site/rack detail sections concurrently by @lance0 in #34
- feat(tui): advertise detail tabs + guard the Enter-open hint by @lance0 in #35
- test(config): lock token precedence, onboarding, and format-preserving edits by @lance0 in #36
- feat(tui): editable per-surface backends + timeout/page_size/exclude in the profile editor by @lance0 in #37
- fix(tui): onboarding persists the timeout_secs/page_size it shows by @lance0 in #38
- ci(deps): ignore rand minor/major so it stops blocking the Dependabot group by @lance0 in #39
- feat(mcp): nbox_get accepts ip_address as an alias for ip by @lance0 in #46
- chore(release): add scripts/smoke.sh — the full pre-tag gate in one shot by @lance0 in #47
Full Changelog: v0.5.0...v0.6.0
v0.5.0
Added
- Route-target view can use GraphQL. Set
[profiles.<name>.api] route_target = "graphql"to fetch a route target's importing + exporting VRFs in one/graphql/
query instead of two RESTvrfslist calls. Identity resolution stays REST (so
not-found/ambiguous exit codes are unchanged) and the result is byte-identical to
the REST path; an instance whose schema can't support it transparently falls back
to REST, with the reason innbox status. The surface joins the per-surfaceapi
block and thecapabilitiesreport. (Also fixes the GraphQL filter probe to
introspectRouteTargetFilter, so theidfilter is shaped correctly.)
Changed
- Browsing one kind shows kind-aware list columns. Opening a kind from the Nav
rail now drops the redundant per-row KIND tag — the pane title already names the
kind — and labels the second column with the attribute that kind actually carries:
STATUSfor prefixes/IPs,VIDfor VLANs,RD/TENANTfor VRFs,TENANTfor
route targets,SITEfor devices/racks,SLUGfor sites. The column sizes to its
content. This replaces the fixed, often-emptySITEcolumn, so a site-less kind no
longer reads as a ragged, empty row. Mixed search results and Recent keep the
KIND / DISPLAY / SITElayout.
v0.4.0
Documentation
- Docs overhauled to the project standard. Restructured the README (a "vs the
NetBox web UI / raw API" comparison table, a complete keybindings table, a
troubleshooting section, a full documentation index) and added
docs/COMPARISON.md,docs/SCRIPTING.md, anddocs/TROUBLESHOOTING.md.
Documented the in-memory read cache ([cache]) and thenbox_cache_clearMCP
tool. Corrected the MSRV (1.88), the MCP tool count (nine), the searchable-kind
lists (racks, VRFs, route targets), and the GraphQL/REST split (search is always
REST; GraphQL backs the VRF view only) across every doc. ExpandedSECURITY.md
(thenbox servenetwork surface, supported-versions) andCONTRIBUTING.md
(module map, an "adding a feature" recipe), and added GitHub issue/PR templates.
Changed
- BREAKING: per-surface API backends replace the coarse
backendkey. The
profile-levelbackend = "rest"|"graphql"setting is removed; a config that
still sets it is rejected with a pointer to the new shape. Configure the backend
per read surface under[profiles.<name>.api]instead:A missing table/key means REST; unknown surfaces (e.g.[profiles.work.api] vrf = "graphql" # rest | graphql
detail) and invalid
values are config errors. REST stays canonical; agraphqlsurface is honored
only when the live schema probe supports it, otherwise it falls back to REST.
nbox status(CLI + MCP) drops the singlebackendfield for a per-surface
apiblock (configured/effective/reason), andcapabilities.graphqlis
now surface-aware (surfaces.{search,vrf}.{supported,recommended,missing}). - Search is always REST. NetBox's GraphQL API has no equivalent to REST's
full-textqquick-search (filtering moved to per-field Strawberry lookups in
4.3), so GraphQL can't reproduce canonical NetBox search.nbox searchnow
always runs over REST; asearch = "graphql"preference is accepted but
transparently falls back to REST, with the reason innbox status. The VRF view
is currently the only GraphQL-capable surface. (The per-kind GraphQL search
fan-out — which silently returned unfiltered results on 4.3+ — was removed.)
Fixed
- Search no longer randomly times out one endpoint. NetBox is commonly served
by gunicorn sync workers, which close the connection after each response;
reqwest could reuse such a half-closed keep-alive connection from its pool and
hang that request to the full timeout, sonbox search's ~17-way fan-out would
intermittently report one endpoint as failed (operation timed out) even though
the server was healthy. nbox now disables idle-connection reuse (a fresh
connection per request, like curl) and sets a 10s connect timeout.
Performance
- VRF detail (REST) fetches its prefixes and addresses concurrently via
tokio::try_join!instead of sequentially, roughly halving the REST VRF view's
latency on a high-RTT link.
Added
- Live-browse the Nav rail. Moving the Nav-rail cursor with
j/k(or
g/G) now auto-browses the highlighted kind into the results pane — noEnter
needed — so scrolling the rail previews each kind's list (and its first item)
beside it. It's debounced until the cursor settles, so a fast scroll doesn't
flash the list of every section it passes; focus stays on the rail, andEnter
still commits and jumps into the results. The footer reflects the rail's
controls when it's focused (j/k browse · Enter results). - TUI remembers the last-browsed kind. The Nav rail's browsed kind is
persisted to[ui].last_browsedon exit and restored on the next launch — the
cursor lands on it and its list preloads (focus stays on the Nav rail). First
run (nothing remembered) still opens on Recent. Also: a Route Targets Nav
section, right-aligned Nav counts, and a count on the Recent row. - Route targets are now a first-class object. A route target (BGP extended
community, e.g.65000:100) can be looked up (nbox route-target <name|id>),
found in search, opened (nbox open route-target/<ref>), journalled, and
fetched over MCP (nbox_get route_target/nbox://route-target/<ref>). Its
detail is the relation graph — the VRFs that import and export it — built by
resolving both directions over REST concurrently; each VRF row is navigable.
The VRF view's targets tab is now navigable: import/export route targets
are structured{id, name}(sovrf --jsongains the id) andEnteropens the
route target's detail, like the prefix/address sections. - VRF GraphQL bundle. With
[profiles.<name>.api] vrf = "graphql", the VRF
view fetches its prefixes + addresses in a single GraphQL query (the VRF
identity is still resolved over REST, preserving not-found/ambiguous exit codes).
REST and GraphQL produce a byte-identicalVrfDetail.nbox vrfnow prints the
full routing context (summary + prefix tree + addresses), and MCPnbox_get vrf
returns the same bundle. - VRFs are now a first-class object. A VRF can be looked up (
nbox vrf <name|rd|id>),
found in search (nbox search/ TUI// MCPnbox_search, REST and GraphQL —
subtitle = its RD, falling back to the tenant), browsed from the TUI Nav rail
(a VRFs section with a live count), opened from the palette (vrf <ref>),
resolved bynbox open vrf/<ref>, journalled (nbox journal vrf/<ref>), and
fetched over MCP (nbox_get/nbox://vrf/<ref>). The VRF view normalizes RD,
tenant, enforce-unique, import/export route targets, and the prefix/address
counts. In the TUI the detail opens as a routing context: a fixed header card
(RD · tenant · route-target counts · enforce-unique) over the VRF's prefix tree,
withaddressesandtargetstabs. The prefix and address rows are navigable —
j/kmove a cursor andEnteropens that prefix/IP (b/Escreturns), the
same drill the related-objects (R) jump performs. Previously VRF was only a
search filter (--vrf) and an exact-by-RD lookup, never a navigable object. - Navigable detail sections. Detail tabs can now be interactive lists (a row
cursor withEnterto open), not just scrollable text — the foundation the VRF
view's prefix tree and address list are built on. Sections without navigable rows
scroll exactly as before. - Three-pane home (Navigation rail). The home screen is now Nav │ Results │
Detail. The left Nav rail browses by kind — Devices / Prefixes / IPs / VLANs /
Sites / Racks — each with a domain-colored bullet and a live object count,
plus a Recent entry.Enterlists a kind into Results (search stays on/); the
Results title names the kind.Tab/Shift-Tabcycle the three panes. - Rack elevation. A rack's detail now has an
e(elevation) tab: a framed,
U-by-U front view (from NetBox's/dcim/racks/{id}/elevation/endpoint) where
each device fills its U span with the name on the top row, and any devices
assigned to the rack without a mounted position are listed below as "not racked".
Reachable viaeor by cycling detail tabs with Tab.
Install: cargo install nbox · brew install lance0/tap/nbox · pre-built binaries · GHCR image ghcr.io/lance0/nbox (tags 0.4.0, latest)
Full changelog: CHANGELOG.md · compare v0.3.0...v0.4.0
v0.3.0
What's Changed
- feat(netbox): add opt-in GraphQL search backend by @lance0 in #11
- test(netbox): add GraphQL backend confidence coverage by @lance0 in #13
- feat(tui): cycle profiles in config-file order by @lance0 in #14
- fix(tui): refine footer navigation states by @lance0 in #12
New Contributors
Full Changelog: v0.2.0...v0.3.0
v0.2.0
What's Changed
- chore(deps): bump actions/checkout from 4 to 6 by @dependabot[bot] in #2
- chore(deps): bump clap_mangen from 0.2.33 to 0.3.0 in the rust-dependencies group across 1 directory by @dependabot[bot] in #3
New Contributors
- @dependabot[bot] made their first contribution in #2
Full Changelog: v0.1.1...v0.2.0
v0.1.1
Full Changelog: https://github.com/lance0/nbox/commits/v0.1.1