[package]
name = "nbox"
version = "0.7.2"
edition = "2024"
rust-version = "1.88" # floor: `let`-chains, stable since 1.88, used throughout
description = "Terminal UI and CLI for NetBox — fast search, IPAM lookups, and device context"
license = "MIT OR Apache-2.0"
repository = "https://github.com/lance0/nbox"
homepage = "https://github.com/lance0/nbox"
readme = "README.md"
keywords = ["netbox", "network", "ipam", "tui", "dcim"]
categories = ["command-line-utilities", "network-programming"]

[dependencies]
# Async runtime
tokio = { version = "1", features = ["full"] }
futures = "0.3"

# HTTP
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }

# TUI
ratatui = "0.30"
crossterm = { version = "0.29", features = ["event-stream"] }
nucleo = "0.5"
# Polished widgets (Input cursor editing for search/palette). Pinned: 0.7.0
# targets ratatui ^0.30, matching nbox — confined behind the cheese adapter
# layer in `src/tui/cheese.rs`.
ratatui-cheese = "=0.7.0"
# Display width of glyphs, to place the terminal cursor on the cheese input the
# same way cheese lays it out (already in the tree via ratatui/cheese).
unicode-width = "0.2"

# CLI
clap = { version = "4", features = ["derive", "env"] }
clap_complete = "4.6"
clap_mangen = "0.3"

# Serialization
serde = { version = "1", features = ["derive"] }
# Order-preserving map so `[profiles.*]` keep TOML document order (profile
# cycling follows the config file, not alphabetical). Already in the tree.
indexmap = { version = "2", features = ["serde"] }
serde_json = "1"
# JSON Schema derivation for MCP tool output schemas. Pinned to the exact
# version rmcp 1.7 depends on so `Json<T>` accepts our `JsonSchema` types
# (the trait bound is satisfied only by that one schemars instance).
schemars = "=1.2.1"
# `preserve_order` keeps map keys in TOML document order through deserialization
# (so `[profiles.*]` load in file order for the TUI switcher, not alphabetical).
toml = { version = "1", features = ["preserve_order"] }
toml_edit = "0.25"

# Config and paths
dirs = "6"

# Errors and logging
anyhow = "1"
thiserror = "2"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# File logging: the non-blocking writer behind `--log-file` / config `log_file`
# (a `rolling::never` appender + `WorkerGuard`). See `init_logging` in src/lib.rs.
tracing-appender = "0.2"

# Terminal helpers
is-terminal = "0.4"
arboard = { version = "3", optional = true }
open = "5"

# IP/network handling
ipnet = "2"

# OS keyring for the API token (`nbox config token set`). Pinned to v3 (=3.6.3):
# v4 is a restructured "sample" crate, not an upgrade path. The real D-Bus
# backend is its own off-by-default feature (`keyring-secret-service`); with no
# backend selected (the default, and on musl) keyring v3 compiles to its built-in
# mock keystore — runtime Err, no D-Bus/C deps — so static builds link cleanly and
# degrade gracefully. The apple/windows backends are cfg(target_os)-inert off-OS.
keyring = { version = "=3.6.3", optional = true }

# Update notifications, later
update-informer = { version = "1", default-features = false, features = ["github", "ureq", "rustls-tls"], optional = true }

# MCP server (`nbox serve`) — read-only tools over stdio
rmcp = { version = "1.7", default-features = false, features = ["server", "macros", "transport-io"] }

# Optional loopback HTTP transport for `nbox serve --http` (behind the `http`
# feature). Pulls in the rmcp Streamable HTTP server plus an axum router to mount
# it on; `tokio-util` supplies the CancellationToken for graceful shutdown. The
# OIDC resource-server mode (rung 3) adds JWT validation on `/mcp`: `jsonwebtoken`
# does the decode + signature/iss/aud/exp checks, JWKS fetch + cache-by-`kid` is
# hand-rolled over the existing `reqwest` so we keep full control of the 401/403
# bodies and `WWW-Authenticate` headers the spec mandates. The pure-Rust crypto
# backend (`rust_crypto`, no aws-lc-rs C build) keeps the musl/rustls story clean.
axum = { version = "0.8", optional = true }
tower = { version = "0.5", optional = true }
tokio-util = { version = "0.7", optional = true }
jsonwebtoken = { version = "10", default-features = false, features = ["rust_crypto", "use_pem"], optional = true }
# Hash the `Mcp-Session-Id` before it lands in the audit log: a short SHA-256
# prefix keeps a session correlatable across its requests without writing the
# raw session token to the log. Already in the tree via `jsonwebtoken`'s
# `rust_crypto`; declared here so the direct use is explicit and version-pinned.
sha2 = { version = "0.11", optional = true }

# Unix syscalls — used in `main` to reset the SIGPIPE disposition to SIG_DFL so a
# closed stdout pipe (e.g. `nbox … | head`) terminates the process normally
# instead of panicking. Already in the tree transitively; declared here so the
# direct use is explicit and version-controlled. (Must stay AFTER the plain
# [dependencies] above, or it swallows the entries below it — that broke the
# Windows release build once already.)
[target.'cfg(unix)'.dependencies]
libc = "0.2"

[features]
# The default set is the canonical shipped binary — every cross-platform user
# feature on (clipboard, http/MCP, native keyring, the update notifier). nbox ships
# a single binary; `--no-default-features` is a dev-only lean stdio build, and the
# D-Bus `keyring-secret-service` backend stays opt-in so the musl build links clean.
default = ["clipboard", "http", "keyring", "updates"]
clipboard = ["dep:arboard"]
updates = ["dep:update-informer"]
# OS keyring token storage (`nbox config token`). Default-on like `http`. Selects
# the apple/windows native backends, which are cfg(target_os)-inert off their OS,
# so on Linux with no further backend feature this resolves to keyring v3's mock
# keystore (no D-Bus/C link dep) — keeping the static musl build identical.
keyring = ["dep:keyring", "keyring/apple-native", "keyring/windows-native"]
# The real Linux D-Bus (Secret Service) backend — OFF by default; desktop
# packagers opt in. `vendored` builds libdbus from source so `--all-features` CI
# needs no `libdbus-1-dev`; `crypto-rust` keeps the encrypted transport pure-Rust.
keyring-secret-service = [
    "keyring",
    "keyring/sync-secret-service",
    "keyring/crypto-rust",
    "keyring/vendored",
]
# Loopback HTTP transport for `nbox serve --http` (rung 2 of the MCP transport
# ladder, DESIGN §24). In `default` — HTTP/MCP is the agent-first headline
# feature, so it ships out of the box; drop it with `--no-default-features` for
# a lean stdio-only build.
http = ["dep:axum", "dep:tower", "dep:tokio-util", "dep:jsonwebtoken", "dep:sha2", "rmcp/transport-streamable-http-server"]

[dev-dependencies]
tokio-test = "0.4"
tempfile = "3"
insta = "1"
wiremock = "0.6"
# Mint a real RSA keypair in the OIDC resource-server tests: derive the JWKS the
# mock IdP serves from the public key (n/e components) and sign tokens with the
# private key via `jsonwebtoken`. Gated by the http-only tests at use sites.
# `rand` 0.8 supplies the RNG for keygen (matches `rsa`'s `rand_core` 0.6).
rsa = { version = "0.9", features = ["pem"] }
rand = "0.8"
base64 = "0.22"

# Lints apply to every target of the package — lib, bin, AND the integration
# test crates in tests/ (each its own crate). A `[lints]` table is the only way
# to make `clippy::pedantic` a true whole-project gate; inner `#![warn(...)]`
# attributes on the crate root reach the lib/bin only. Pedantic is a project gate
# (every change is linted against it); the allows below are the lints judged to be
# pure noise or stylistic churn for this codebase, silenced package-wide rather
# than chased. Everything else pedantic flags is fixed. Keep this list tight —
# prefer fixing. The negative `priority` lets the specific allows override the
# group warn.
[lints.clippy]
pedantic = { level = "warn", priority = -1 }
missing_errors_doc = "allow"
missing_panics_doc = "allow"
module_name_repetitions = "allow"
must_use_candidate = "allow"
module_inception = "allow"
needless_pass_by_value = "allow"
redundant_else = "allow"
too_many_lines = "allow"
used_underscore_binding = "allow"
# Backtick nags on proper nouns (NetBox, JSON-RPC, IPAM, …) in doc comments.
doc_markdown = "allow"
# Benign u16/usize/f64 math in the TUI (scroll offsets, terminal coordinates,
# a percent bar): the casts are intentional and bounded.
cast_possible_truncation = "allow"
cast_sign_loss = "allow"
cast_precision_loss = "allow"
# Stylistic: `if let … else` vs a two-arm `match`, and merging identical arms.
# Mixing the two styles inside one `match` hurts readability more than it helps.
single_match_else = "allow"
match_same_arms = "allow"
items_after_statements = "allow"
# The CLI flags struct legitimately carries many bool options; collapsing them
# would be an API change, not a cleanup.
struct_excessive_bools = "allow"

[profile.release]
lto = true
codegen-units = 1
strip = true
