Repository navigation
Dynamic Inventory
A dynamic folder auto-populates its contents from an external provider on a refresh interval. The entries inside aren't stored as connection rows in the DB - they're a live cache. Connecting to an entry constructs an ephemeral connection in memory, inherits the dynamic folder's credentials / jump host / SSH options, and runs through the standard SSH layer.
- Right-click a parent folder (or empty tree area) → New dynamic subfolder… Pick a provider, fill in the config, save.
- Required: an API token credential stored in the vault. The folder editor has an inline "+ New token credential" form so you don't have to switch tabs.
Proxmox VE. Config: base URL (the load-balancer URL works
fine - /cluster/resources returns the whole cluster regardless
of which node answers), API token id (user@realm!tokenid), and
the token secret credential. Filter: include hosts (Proxmox
nodes), include guests (VM + LXC), tag whitelist/blacklist,
hide-stopped. The hostname for SSH defaults to the VM name on the
assumption that DNS resolves it.
Hetzner Cloud. Config: API token credential, hostname source (name / public IPv4 / private IPv4 - pick what reaches the server from where you sit), label whitelist/blacklist, hide-stopped. Hetzner has no DNS auto-record, so the hostname source picker is load-bearing.
DigitalOcean. API token credential + hostname source
(name / public IPv4 / private VPC IPv4). Read-scope token from
Cloud → API → Tokens & Keys is enough. Droplet tags map straight
to the filter tag vocabulary.
Linode (Akamai Cloud Compute). API token credential + hostname
source (label / public IPv4 / private IPv4). Read-only Linodes
scope. Tags include a synthetic region=<region> plus the user's
own tags. Private-IP detection uses RFC1918 ranges + Linode's
192.168.128/17 private block.
Vultr. API token credential + hostname source (label / main
public IPv4 / internal IPv4). Read-access token. Tags include a
synthetic region=<region>.
Scaleway. API token credential + zone (required; e.g.
fr-par-1, nl-ams-1, pl-waw-1) + hostname source. One folder
per zone - the API scopes server listings by zone.
AWS EC2. Reuses the api_token credential shape: the
credential's token id field holds the access key, the secret
holds the secret access key. Folder config also needs the
region (eu-central-1 etc.); one folder per region. Hostname
sources include EC2-specific options (Name tag, Public DNS, public
IPv4, private IPv4). The Name tag is the default. Tags include a
synthetic region=<region>. Requires IAM permission
ec2:DescribeInstances. SigV4 is signed inline so there's no AWS
SDK dependency.
Ansible static inventory. Reads a local .ini or .yml
inventory file (format auto-detected by extension). Config:
- Inventory file path - absolute path; Browse… button opens a native file picker.
-
Host pattern / Group pattern - optional fnmatch globs
(
web*,prod_*). -
Display name -
inventory_hostname(default) oransible_hostvalue if you'd rather see IPs in the tree. -
Jump host credential - SSH-capable credential
(password / key / agent / opkssh) applied to every hop parsed
out of
ansible_ssh_common_args. Target host credentials almost never work on the bastion; without this picker connects through a jump host will fail authentication.
What gets lifted from the inventory at connect time:
| Ansible var | Becomes |
|---|---|
ansible_host |
actual SSH dial target (overrides hostname) |
ansible_user |
per-host username override |
ansible_port |
per-host port override |
ansible_ssh_common_args / _extra_args
|
jump chain (-J, ProxyJump=, ProxyCommand=ssh -W all recognised) |
Each Ansible group the host belongs to becomes a tag - group filter in the sidebar narrows to hosts carrying it, and Ctrl+K matches tags too. Folder structure stays flat (every host as a single entry); we don't mirror Ansible's DAG.
Per-connect overrides via the entry's detail pane
(Use different credential…): credential, username, password,
jump host ([user@]host[:port] - replaces the entire
parsed chain) and jump credential (one-off override of the
folder-level picker). All fields reset after Connect.
The five non-Hetzner cloud providers were added together and have not yet been tested end-to-end against real accounts. Please file a bug if your provider misbehaves - the per-provider Fetch path is small and easy to inspect.
- Globe icon instead of folder.
- Folder name + icon tinted teal so dynamic folders stand out.
-
Provider pill (
proxmox/hetzner/ansible/ …) inline next to the name. -
Red
!dot when the last refresh errored - hover for the message. - Tooltip on the name shows provider + "refreshed Nm ago" + last error if any.
- Count badge shows cached entry count (or hidden if empty).
- Per-entry icon based on kind: VM → Monitor, LXC → Box, host / server → Server tower. Consistent across the tree, Ctrl+K palette, and the detail-pane header.
Click a VM / LXC / server / node to open its detail pane. The pane is read-only - the source of truth is the provider, not the local DB.
- Header: name + status pill + provider chip + folder breadcrumb.
- Actions: Connect (with stopped-VM confirm), Copy host, Refresh inventory.
- Facts grid:
- Proxmox: resource type, hosting node, VMID, vCPUs, memory, disk, uptime.
- Hetzner: server id, server type, datacenter, image, public IPv4 / IPv6, private IPv4, created.
- DigitalOcean: droplet id, public IPv4, private IPv4.
- Linode: linode id, region, public IPv4, private IPv4.
- Vultr: instance id, region, main IPv4, internal IPv4.
- Scaleway: server id, public IPv4, private IPv4.
- AWS EC2: instance id, public / private IPv4, public DNS.
- Tags / labels row.
-
Live load bars (CPU / Memory / Disk) for Proxmox, colour-
coded green<60 / yellow<85 / red≥85. Hetzner doesn't expose live
load on the
/serversendpoint, so the section is hidden. - Collapsible Raw provider payload with the JSON for everything not covered above.
- Double-click an entry in the tree (or click Connect in the detail pane) to start an SSH session.
- The session inherits the dynamic folder's credentials, jump host, and SSH options - same cascade as a regular tree branch.
- A stopped entry triggers a confirm dialog ("connect anyway?") in case the VM is reachable on another address or the status is stale.
- The session's connection id is
dyn:<entryID>. It doesn't appear as a row in any connection list - it lives in the session pool until disconnected. - A failed connect surfaces inline in the entry's detail pane
(red row above the facts grid). The previous
alert()modal with thewails.localhost saystitle is gone.
-
Ctrl-click entries to toggle them in / out of a multi-
selection set; Shift-click selects a range. The detail pane
switches to a bulk view showing the count and two actions:
- Connect all opens one terminal tab per selected entry in parallel.
- Batch exec… runs a single command across the whole selection and shows per-host stdout / stderr / exit in a single modal (same component the regular multi-connection batch panel uses; results expanded by default).
- The Batch exec modal also has a Load snippet… picker so you can pull a saved global snippet straight into the command field before pressing Run.
Same gesture as on regular connections: Use different credential… next to Connect opens an inline block with three independent override fields. Resets after the next press; nothing persists. Useful for hitting a fresh dynamic host with a different identity without editing the folder's inherited credential.
- The folder refreshes on its configured interval (default 60s).
- Manual refresh via right-click → Refresh now or the button in an entry's detail pane.
- A refresh error leaves the previous entries cached (last good
state) and surfaces the error via the red
!dot.
Right-click a dynamic folder → Edit dynamic config… Same form as creation. Changing the refresh interval restarts the timer.
Some hosts in a dynamic inventory don't really need to be
dynamic - they don't move, you've got custom notes for them, and
you want them to survive even if the API token gets revoked or
the provider rotates the IP. Click the host in the tree to open
the detail pane, then Pin as connection…. The host becomes
a real connection inside the same folder (any per-attempt
credential override you picked becomes the new connection's
default credential; Ansible vars like ansible_user and the
parsed jump chain are carried over). Future inventory refreshes
skip that host's external ID so it doesn't appear twice - once
as the real connection, once as a dynamic ghost.
To unpin: just delete the pinned connection like any other. The next inventory refresh re-includes the host as a dynamic ghost.
When the source of truth stops changing - you got handed a one-off Ansible inventory, or you're retiring an API token but want to keep the snapshot - open the dynamic folder editor and click Convert to static…. Every cached host becomes a regular connection in the folder, the provider link is dropped, the refresh timer stops, and the folder behaves like any other folder you created by hand. Irreversible from the UI; existing pinned connections in the same folder are untouched.
Generated from docs/USER_GUIDE.md. Edits here may be overwritten - fix the guide in the repo.