# Install shdctl

> Install the shdctl command-line tool, configure registry credentials, and review external CLI requirements

Unity provides `install-shdctl.sh` as part of your Self-Hosted Deployment onboarding package, and every release from 2.0.0 on carries it under `common/scripts/`. To move a deployment of an earlier release from vpctl to shdctl, follow [Install shdctl 1.0.0 before you pull](../shd/maintenance/migration#install-shdctl-1.0.0-before-you-pull) instead. Once you have the script, run:

```sh
./install-shdctl.sh latest
```

The following sections explain the script's options, the external CLIs that shdctl uses for specific operations, and how to configure the registry credentials shdctl uses to pull releases.

## Requirements

shdctl is a single Go binary; it has no runtime dependencies of its own. External CLIs are required only for operations that shell out to them:

| You need...                                                       | Required for...                                                                                                                                                                                                                           |
| ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `oras` CLI                                                        | The install script (`install-shdctl.sh` shells out to `oras` to pull the binary)                                                                                                                                                          |
| `docker` CLI, plus `docker login` to source and target registries | `shdctl artifact sync images` (uses `docker pull`/`tag`/`push`/`manifest inspect`/`rmi`) and `shdctl artifact sync preflight`. `shdctl artifact sync verify` needs it too, but only `docker manifest inspect` against the target registry |
| `docker login` (to populate `~/.docker/config.json`)              | `shdctl artifact sync oras` and `shdctl artifact sync images` (reads the release manifest to find the images it names) — `oras-go` reads credentials from the Docker credential store                                                     |
| `helm` CLI on `PATH`                                              | `shdctl release deploy --format helm` and `shdctl release uninstall`                                                                                                                                                                      |
| `kubectl` CLI on `PATH`, plus a kubeconfig                        | `shdctl cluster check`, `shdctl secret deploy`, `shdctl secret export` (reads secrets from one namespace), `shdctl release deploy --format argocd` (applies one `Application`)                                                            |
| `cue` CLI (optional)                                              | Only if you want to validate `manifest.yaml` outside shdctl after `shdctl manifest schema --export`. The schema is embedded in the binary, so `shdctl manifest validate` works without it                                                 |

`shdctl release pull`, `shdctl release generate`, `shdctl secret generate`, `shdctl manifest init/validate/schema`, and `shdctl configure` do not shell out to any external CLI.

## Install the shdctl binary

The install script automatically detects your OS and architecture and downloads the correct binary from the Unity registry.

**Prerequisites:** You need registry credentials (username and password). Unity provides these credentials.

The script supports interactive mode for users and non-interactive mode for CI and automation.

### Interactive mode

1. Run the script without setting credentials. It prompts you for your username and password:

   ```sh
   ./install-shdctl.sh latest
   ```

   You can also pin a specific version:

   ```sh
   ./install-shdctl.sh 1.0.0
   ```

2. Enter your credentials at the prompt:

   ```text
   [install-shdctl] Logging in to uccmpprivatecloud.azurecr.io (interactive)...
   [install-shdctl] Please enter your registry credentials:
   Username: <your-username>
   Password: <your-password>
   ```

### Non-interactive mode for CI and automation

For automated environments, set both `ORAS_USERNAME` and `ORAS_PASSWORD` environment variables:

```sh
export ORAS_USERNAME="<your-username>"
export ORAS_PASSWORD="<your-password>"

./install-shdctl.sh latest
```

You can also pin a specific version:

```sh
export ORAS_USERNAME="<your-username>"
export ORAS_PASSWORD="<your-password>"

./install-shdctl.sh 1.0.0
```

### Custom installation directory

Specify a custom installation directory as the second argument:

```sh
./install-shdctl.sh latest /opt/bin
```

The script creates a missing directory, with `sudo` when you cannot create it yourself — `/opt/bin` as a non-root user, for instance.

### Script actions

The script performs the following actions:

* Auto-detects your platform (`linux`/`darwin`/`windows`) and architecture (`amd64`/`arm64`)
* Checks whether the `oras` CLI is installed, and stops with a link to its installation instructions if it is not
* Authenticates to the registry by using interactive or non-interactive mode based on environment variables. The login stays in your Docker credential store afterwards; run `oras logout <registry>` if you don't want it kept
* Downloads and extracts the correct shdctl binary
* Installs it to `/usr/local/bin` or the directory you specify, using `sudo` to create that directory or copy into it when you cannot
* Links the tool's previous name, `vpctl`, to it (except on Windows), so scripts that still call `vpctl` keep working until shdctl 2.0.0
* Verifies the installation by running `shdctl version`

### Environment variables

| Variable        | Required | Default                        | Description                                  |
| --------------- | -------- | ------------------------------ | -------------------------------------------- |
| `ORAS_USERNAME` | No\*     | -                              | Registry username (for non-interactive mode) |
| `ORAS_PASSWORD` | No\*     | -                              | Registry password (for non-interactive mode) |
| `ORAS_REGISTRY` | No       | `uccmpprivatecloud.azurecr.io` | Registry URL                                 |

\* Both `ORAS_USERNAME` and `ORAS_PASSWORD` must be set together for non-interactive mode, or both unset for interactive mode.

## Configure registry credentials

Before you can pull releases, configure your registry credentials.

1. Run the interactive configuration command:

   ```sh
   shdctl configure
   ```

   The command prompts you for:

   * Username
   * Password (input is hidden)

   The registry defaults to `uccmpprivatecloud.azurecr.io`. To configure credentials for a different registry, pass it as a positional argument: `shdctl configure set <registry-url>`.

2. Alternatively, configure credentials non-interactively. The registry argument is positional and defaults to `uccmpprivatecloud.azurecr.io`. You can provide the password by using one of the non-interactive methods in [Security: providing registry credentials](#security:-providing-registry-credentials).

Credentials are stored in `~/.shdctl/config.json`, readable only by your user (`0600`, in a `0700` directory) but not encrypted — protect it like any other credential file. Only `shdctl release pull` reads it; `artifact sync` uses your Docker credentials instead.

### Security: providing registry credentials

`shdctl configure set` accepts the password through four input modes, listed from most to least preferred for automation:

1. **`--password-stdin`** — read the password from stdin. Best for CI/CD pipelines:
   ```sh
   echo "$REGISTRY_PASSWORD" | shdctl configure set <registry-url> --username "$REGISTRY_USERNAME" --password-stdin
   ```
2. **`SHDCTL_PASSWORD` environment variable** — non-interactive without piping. Pair with `SHDCTL_USERNAME`. The pre-rename `VPCTL_USERNAME` and `VPCTL_PASSWORD` are still read, with a deprecation notice, until shdctl 2.0.0:
   ```sh
   export SHDCTL_USERNAME=<username>
   export SHDCTL_PASSWORD=<password>
   shdctl configure set <registry-url>
   ```
3. **Interactive prompt** — the default when you run `shdctl configure set <registry-url>` without flags. Password input is hidden.
4. **`--password <value>` flag** — *discouraged*. The password is exposed in your shell history and in the process list (`ps aux`). shdctl emits a warning to stderr:
   ```text
   WARNING: Using --password via the CLI is insecure (visible in shell history and the process list).
   Use --password-stdin, set SHDCTL_PASSWORD, or omit --password to be prompted interactively.
   ```
   Kept for backward compatibility; not recommended.

`--password` and `--password-stdin` are mutually exclusive. If neither is set and stdin is not a TTY (and no `SHDCTL_PASSWORD` is in the environment), `shdctl configure set` errors out rather than hanging.

To view or delete stored credentials, and for every `configure` subcommand, refer to [Manage registry credentials](./commands/configure.md).
