wishlist

repository·main·Indexed 23 days ago

https://github.com/charmbracelet/wishlist

An SSH directory tool that provides a single entry point for multiple SSH endpoints. It can function as a local explorer for `~/.ssh/config` or as a server to host multiple SSH applications under one interface. Wishlist supports endpoint discovery via Tailscale, Zeroconf/mDNS, and DNS SRV records, and provides a TUI for managing and connecting to SSH configurations.

Tokens
5K
Snippets
8
Records
32
Agent score
82%

What's inside wishlist

  1. Install Wishlist via package managers

    main

    You can install Wishlist using various package managers depending on your operating system:

    macOS or Linux

    brew install charmbracelet/tap/wishlist

    Arch Linux

    # Install the binary version
    yay -S wishlist-bin
    # Or the source version
    yay -S wishlist

    Windows

    Using winget:

    winget install wishlist

    Using Scoop:

    scoop bucket add charm https://github.com/charmbracelet/scoop-bucket.git
    scoop install wishlist

    Nix

    nix-env -iA nixpkgs.wishlist

    Debian/Ubuntu

    sudo mkdir -p /etc/apt/keyrings
    curl -fsSL https://repo.charm.sh/apt/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/charm.gpg
    echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" | sudo tee /etc/apt/sources.list.d/charm.list
    sudo apt update && sudo apt install wishlist

    Fedora/RHEL

    echo '[charm]
    name=Charm
    baseurl=https://repo.charm.sh/yum/
    enabled=1
    gpgcheck=1
    gpgkey=https://repo.charm.sh/yum/gpg.key' | sudo tee /etc/yum.repos.d/charm.repo
    sudo yum install wishlist
    # macOS or Linux
    brew install charmbracelet/tap/wishlist
    
    # Arch Linux (btw)
    yay -S wishlist-bin
    # or
    yay -S wishlist
    
    # Windows (with winget)
    winget install wishlist
    
    # Windows (with Scoop)
    scoop bucket add charm https://github.com/charmbracelet/scoop-bucket.git
    scoop install wishlist
    
    # Nix
    nix-env -iA nixpkgs.wishlist
    
    # Debian/Ubuntu
    sudo mkdir -p /etc/apt/keyrings
    curl -fsSL https://repo.charm.sh/apt/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/charm.gpg
    echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" | sudo tee /etc/apt/sources.list.d/charm.list
    sudo apt update && sudo apt install wishlist
    
    # Fedora/RHEL
    echo '[charm]
    name=Charm
    baseurl=https://repo.charm.sh/yum/
    enabled=1
    gpgcheck=1
    gpgkey=https://repo.charm.sh/yum/gpg.key' | sudo tee /etc/yum.repos.d/charm.repo
    sudo yum install wishlist
  2. Discover endpoints via SRV records

    main

    Wishlist can find nodes using DNS SRV records.

    wishlist --srv.domain example.com

    By default, the endpoint name is set to the SRV target. You can customize the name using a TXT record in this format: wishlist.name full.address:22=thename

    wishlist --srv.domain example.com
  3. Discover endpoints via Tailscale

    main

    Wishlist can discover nodes in your Tailscale tailnet. You can provide an API key or use OAuth credentials.

    Using an API Key

    wishlist --tailscale.net=your_tailnet_name --tailscale.key=tskey-api-abc123...

    To avoid the 90-day API key expiration, use OAuth clients with the following flags:

    • --tailscale.client.id (or $TAILSCALE_CLIENT_ID)
    • --tailscale.client.secret (or $TAILSCALE_CLIENT_SECRET)

    Note: The OAuth client requires the devices:read scope.

    wishlist --tailscale.net=your_tailnet_name --tailscale.key=tskey-api-abc123...
  4. Discover endpoints via Zeroconf/mDNS

    main

    Enable Zeroconf discovery to find _ssh._tcp services on your network using the --zeroconf.enabled flag.

    wishlist --zeroconf.enabled

    Additional options:

    • --zeroconf.timeout: Specify a timeout.
    • --zeroconf.domain: Specify the domain to look for services in.
    wishlist --zeroconf.enabled
  5. Run Wishlist in Local vs Server mode

    main

    Wishlist can be used in two primary modes:

    Local Mode

    Use this to explore your existing ~/.ssh/config. Running the command without arguments starts local mode.

    wishlist

    Server Mode

    Use this to act as a single entry point for multiple SSH endpoints (like Wish apps). This mode requires the serve subcommand.

    wishlist serve
    # Local mode
    wishlist
    
    # Server mode
    wishlist serve
  6. Configure Wishlist configuration files

    main

    Wishlist reads configuration from several locations. The first one loaded without errors is used. Supported formats are YAML and SSH Config.

    Configuration Order

    1. The -config flag
    2. .wishlist/config.yaml or .wishlist/config.yml or .wishlist/config
    3. [[user config dir]]/wishlist/config.yaml (or .yml or .config)
      • Unix: $XDG_CONFIG_HOME or $HOME/.config
      • Darwin: $HOME/Library/Application Support
      • Windows: %AppData%
      • Plan 9: $home/lib
    4. $HOME/.ssh/config
    5. /etc/ssh/ssh_config

    Using a custom config file

    wishlist -config /path/to/your/config.yaml
  7. Implement custom item descriptions with descriptors

    main

    A descriptor is a function type used to generate specific lines of text for an ItemWrapper's description. It has the signature func(e *Endpoint, styles styles) string.

    Commonly used descriptor patterns include:

    • withSSHURL: Returns an SSH link using the endpoint's address (e.g., ssh://<address>).
    • withLink: Returns the endpoint's link if available, otherwise renders "no link" using the provided styles.
    • withDescription: Returns the first line of the endpoint's description, or renders "no description" if empty.
    type descriptor func(e *Endpoint, styles styles) string
    
    func withSSHURL(i *Endpoint, _ styles) string {
    	return Link{URL: "ssh://" + i.Address}.String()
    }
    
    func withLink(i *Endpoint, styles styles) string {
    	if l := i.Link.String(); l != "" {
    		return l
    	}
    	return styles.NoContent.Render("no link")
    }
    
    func withDescription(i *Endpoint, styles styles) string {
    	if desc := strings.Split(i.Desc, "\n")[0]; desc != "" {
    		return desc
    	}
    	return styles.NoContent.Render("no description")
    }
  8. ListModel keybindings and interactions

    main

    The ListModel provides several interactive behaviors via keybindings:

    • Connect: Pressing enter or o triggers the SSHClient.For(endpoint) command for the currently selected item. This command is executed via tea.Exec.
    • Copy IP Address: Pressing y extracts the host portion of the selected endpoint's address (using net.SplitHostPort) and copies it to the system clipboard using termenv.Copy. A status message is returned to the list.
    • Quit: Pressing the default quit key (usually ctrl+c) sets the quitting flag to true, which causes View() to return an empty string.
    • Filtering: The model respects the list's filtering state; certain actions like connecting or copying are disabled while the user is actively setting a filter.
  9. Handle errors in the ListModel view

    main
    When an error occurs during an operation (like a connection failure), the ListModel captures it. In the View() method, if an error is present, the model renders an error screen showing the root cause of the error, a header, and instructions to press any key to return to the list. The error is cleared when the user presses any key in the Update loop.
  10. Configure SSH authentication using Users

    main

    Wishlist uses the Users field in the Config struct to manage SSH public key authentication.

    • If config.Users is empty, the server allows anyone to log in (no authentication required).
    • If config.Users is populated, the server implements a ssh.PublicKeyHandler that validates the incoming SSH key against the PublicKeys list for the specific username provided in the SSH session.
  11. Use the wishlist CLI

    main

    wishlist is a TUI for managing your SSH configurations. It can read from standard ~/.ssh/config files or custom YAML configuration files. You can use it to browse your endpoints in a terminal UI or connect directly to a specific endpoint by name.

    Common Tasks

    Browse endpoints in a TUI

    Run the command without arguments or with the list argument to open the interactive listing.

    wishlist
    # or
    wishlist list

    Connect to a specific endpoint

    Pass the name of the endpoint as an argument to connect directly via SSH.

    wishlist my-server-name

    Serve the TUI over SSH

    Use the serve command to make the wishlist TUI accessible to others over an SSH connection.

    wishlist serve
  12. Supported SSH options in Wishlist

    main

    When using Wishlist to connect to hosts (via SSH config or YAML), the following SSH options are currently supported:

    • User
    • Hostname
    • Port
    • IdentityFiles
    • ForwardAgent
    • RequestTTY
    • RemoteCommand
    • SendEnv
    • SetEnv
    • ConnectTimeout
    • Include
    • PreferredAuthentications
    • ProxyJump