:usage
Using Nutsh.
Everything the binary does today, in the order you are likely to need it. Where something is not built yet, it says so at the bottom rather than being quietly left out.
Where Nutsh comes from, and what kind means
Nutsh takes its shape from k9s, the terminal UI for Kubernetes: a colon prompt for jumping to a resource type, short aliases, contexts you switch between, tables you move through with j and k, and a read-only mode for when you would rather not fat-finger production. If you have used k9s, most of what follows will already make sense.
The word kind comes from the same place. In Kubernetes a kind is the type of a thing: Pod, Deployment, Service. Nutanix's v4 APIs expose resources the same way, so Nutsh uses the same word. Typing :vm here does what :pod does there.
Where Nutsh has to part ways with that model is the API underneath. Kubernetes hands you one API surface with one place to discover what lives on it. Nutanix v4 is split across 20 namespaces, each versioned on its own schedule:
aiops clustermgmt datapolicies dataprotection files iam licensing lifecycle microseg monitoring multidomain networking objects opsmgmt prism security storage tenancy vmm volumes
Virtual machines live in vmm, clusters and hosts in clustermgmt, and so on. Nutsh generates its catalog from the published OpenAPI specs, which is where the 232 kinds and 478 actions come from, and works out at connect time which version of each namespace your Prism Central actually answers on. Two of the twenty, storage and tenancy, are known only from pre-release specs, so they are allowed to be missing without that counting as a failure.
One thing worth being explicit about: this is not a Kubernetes client. It talks to Prism Central and nothing else. If you run Kubernetes on Nutanix, Nutsh will show you the virtual machines and storage underneath it, but kubectl is still kubectl.
First run
Nutsh needs to know which Prism Central to talk to. That is a context, and you can have as many as you have environments.
$ nutsh ctx add lab --host pc.lab.example --username admin --insecure
$ nutsh ctx login lab
$ nutsh --check
ctx login prompts once and verifies the password against the Prism Central before storing it, so a typo fails immediately instead of at the next thing you try. Repeating a failed login is a good way to lock the account out, so it asks once and stops.
About --insecure
Prism Central's default certificate is marked as a CA certificate, and rustls will not accept a CA certificate as a server certificate even if you trust it. That means --ca-bundle cannot rescue the default certificate, and a context on a stock Prism Central needs --insecure. Use --ca-bundle <pem> when the Prism Central carries a certificate from your own CA, which is the case worth aiming for.
With a context in place, running nutsh on its own opens the Dashboard. Give it a kind and it opens that instead: nutsh vm, nutsh cluster, nutsh host. Catalog id, alias and display-name prefix all work.
The screens
The sidebar, generated from the catalog. ctrl-b toggles it, ⇥ focuses it, / filters the labels while it has focus, and 0 to 9 jump straight to a group. Entries the Prism Central does not serve are greyed rather than hidden, and they say why.
One kind, one row per resource. 74 kinds have columns chosen by hand; the rest get a layout worked out from the schema. / filters the rows without making a request.
Several panes on one screen. The Dashboard and Disaster Recovery are both pages. ⇥ cycles panes and O opens the focused pane as a full table.
y on a row. Twelve kinds have a detail layout written for them; everything else is composed from the schema. Y and J show the raw YAML or JSON that came off the wire.
:ctx, or whatever you get on startup with no usable context. Add, log into, connect to and remove contexts without leaving the program. space on a row reads that Prism Central beside the session: every table then lists both, with a CONTEXT column.
a on a row. Lists what this kind can do, greys what you are not allowed to do, and says why.
:journal. Every action attempted this session with what came back. It is deliberately not written to disk.
:settings. Every setting, its current value and where that value came from: the catalog, the config file, or this session. The refresh section sets how often each namespace and each kind polls.
:search <term>. One list across every kind, by name, address or identifier. It answers from what is loaded first, then asks the Prism Central kind by kind, and says how far it looked.
?. The short version of the table below.
Keys
| Move | j k or ↑ ↓ a row, g G top and bottom, PgUp PgDn twenty at a time, ← → scroll columns |
|---|---|
| Navigate | ⏎ drill into a row, esc back, ⇥ focus the sidebar, O open a pane as a table, ctrl-b toggle the menu, 0 to 9 jump to a group, / filter the rows, or the sidebar when it has focus, h l collapse and expand |
| Read | y detail, Y raw YAML, J raw JSON, S sort, w wide |
| Refresh | ctrl-r now, ctrl-t how often this view polls |
| Act | a action menu, space mark a row for bulk |
| Palette | : open, ⇥ accept the ghost completion, ↑ ↓ move, ctrl-w delete a word, ⏎ run, esc cancel |
| Leave | q quit, twice while a task is being watched. ctrl-c always quits |
| Other | ? help, ctrl-o release the mouse for the rest of the session |
The palette
: opens it. Type a kind, a page, or a command. Kinds match on id, alias or the start of the display name, pages the same way, and arguments complete against real values, so :ctx offers your contexts and :skin offers the skins. The rest of the best match appears dimmed ahead of the cursor; ⇥ takes it. History survives a restart.
:ctx [name…] | The Contexts screen, or straight to a context. A second name and more are read beside the first, and a name left out is dropped |
|---|---|
:skin [name] | The skin picker, or straight to a skin. Persists to the config file |
:can-i <action> <kind> | Whether your roles permit that action on that kind |
:search <term> | Name, address or identifier, across every kind: what is loaded first, then the Prism Central |
:journal | This session's action attempts |
:settings | Every setting, its value, where it came from, and the refresh schedule per namespace and kind |
:log [level] | Turn the log on or off for this and later sessions |
:mouse | Turn the mouse on or off, and remember the answer |
:hide <id or group> | Drop something from the menu |
:show <id> | Put it back |
:all | Show everything again, including what the Prism Central does not serve |
:help | The help overlay |
:quit | Leave |
Actions
a opens the action menu for the row you are on. Eleven of the common ones also have a key you can press directly from the table. The confirm column is what you have to get past, and it is a floor: configuration can raise it, never lower it.
| Key | Action | Confirm | Danger |
|---|---|---|---|
| p | Power on | none | low |
| P | Power off | yes or no | medium |
| r | Reboot | yes or no | medium |
| C | Clone | none | low |
| s | Create recovery point | none | low |
| m | Migrate to host | yes or no | medium |
| M | Enter host maintenance | yes or no | medium |
| A | Acknowledge alert | none | low |
| R | Resolve alert | none | low |
| c | Cancel task | yes or no | medium |
| ctrl-d | Delete | type the name | high |
space marks rows, and an action then applies to everything marked, up to five rows. Where the API requires an If-Match, Nutsh reads the resource for its ETag and sends it, so an action against a resource somebody else just changed fails instead of overwriting them. Long-running actions produce a task, and the task is watched until it finishes, which is why the affected column settles without you pressing ctrl-r.
Guardrails
Five things are checked before a request goes out, in this order. The first one that objects wins.
- Read-only.
--readonly, or the flag on the context, or the one in the config file. Any of them is enough. Nothing is sent; the header says so and the journal records the attempt. - Your own deny rules.
[[guardrails]]entries in the config file that refuse an action outright. - Permission. The same check
:can-iruns. A lookup that fails leaves it unknown rather than blocking you. - Bulk ceiling. Five marked rows by default. Configurable, but not to zero.
- Confirmation. The catalog sets a floor per action. Your rules can push it higher, to yes-or-no or to typing the name.
Deleting always asks you to type the name. Power-off, power-cycle, reset, reboot, shutdown, guest shutdown, guest reboot, revert and entering host maintenance always ask yes or no. A guardrail rule that will not parse is a startup error naming the key, not a rule that silently does nothing.
Contexts and the config file
The config file lives under your XDG config directory. Contexts, the current one, and your preferences go in it. Passwords do not.
$ nutsh ctx add NAME --host H --username U [--cluster C] [--insecure] [--readonly]
$ nutsh ctx login NAME
$ nutsh ctx use NAME
$ nutsh ctx list
$ nutsh ctx show [NAME]
$ nutsh ctx remove NAME
What a context holds: host and port, username, an optional cluster to scope every view to, whether TLS is verified and which CA bundle to verify against, whether the session is read-only, and optionally a skin of its own.
Several Prism Centrals at once: :ctx lab dr, or -c lab,dr from the shell, keeps lab as the session and reads dr beside it. Every top-level table lists both under a CONTEXT column, each Prism Central in its own colour; / on a context name keeps that one's rows. A row acts against the Prism Central it came from and the confirm says which. Each context keeps its own session and pacing, and a credential one of them refuses stops that one alone. --check with two names prints one table per context.
The file also carries the current context, a global read-only switch, whether caching and the mouse are on, the skin, your guardrail rules, and which menu entries you have hidden.
Where the password goes
On macOS, the login Keychain. On Linux, a mode 0600 file in a mode 0700 directory under your XDG state directory, because the Secret Service backend needs D-Bus at build time and is off by default. Either way it is keyed by user, host and port, and it never appears in the config file, in a command line, or in the environment of a child process.
Cache and requests
Responses are cached on disk, one directory per context. Negotiated API versions are kept for a day and permission answers for an hour, which is what makes the second start of the day quicker than the first.
$ nutsh cache info
$ nutsh cache clear --only lab
$ nutsh --no-cache
Requests go out under a token bucket: a per-namespace limit taken from the API specs, and a ceiling of 30 requests a second for the whole host, whichever binds first. A 429 blocks the bucket rather than being retried into the ground. The status line shows the live rate and the share the cache absorbed, so you can watch what the tool is doing to a Prism Central rather than guess.
Upgrading the binary discards the cache once, on purpose, because a cache written by a different version is not trusted.
Outside the TUI
Asks all 20 namespaces which version they serve and prints the answer next to what the catalog expected. Exit code 2 when a namespace that should be there is not, so it works in a health check.
Catalog counts and the path to the config file.
Renders one frame to stdout and exits. --size COLSxROWS if you want something other than 120x40. No colour codes and no request meter, so the output is stable enough to diff or paste into a ticket.
Guarantees the session cannot write, whatever the config says.
Off unless asked. NUTSH_LOG=debug for one run, log = "debug" in the config file to keep it. One-shot commands log to stderr; the TUI writes to a capped file under your XDG state directory, because a line on stderr would be a hole in the frame.
$ nutsh vm --snapshot --size 120x40 --context lab
$ nutsh vm --snapshot -c lab,dr
Not built yet
Listed here so you do not go looking for them. These are planned, not present.
There is no key that diagnoses a failure and assembles the evidence for it. The project's own tagline promises this; the binary does not do it yet.
Not implemented.
/ matches text. There is no field syntax such as power=off.
No native build. The Linux build runs under WSL2.