# Install the connector

The installer sets up the Canopy connector beside your inference engine. It does not install or start the engine itself.

```sh
curl -fsSL https://get.canopyx.ai/provider | sh -s -- --enroll K7QF-29XM-...
```

Copy the command from the **Install** page in the web app. Its enrollment code works once and expires after 30 minutes; generate a new one at any time.

The installer exchanges the code for a revocable **connector credential**, stored in a file only its owner can read. The credential never appears in the command, a URL or your shell history.

## What the installer does

1. Detects your platform and service manager, prints a plan, and asks you to confirm unless you pass `--yes`.
2. Installs a pinned Bun runtime and the SDK into the install directory, verifying every download against signed checksums.
3. Enrolls: exchanges the code, fetches your offering and starter settings, and generates editable TypeScript in `app/provider.ts`.
4. Checks your engine at `--engine-url` or the URL from your setup. Without either, it asks for the engine and suggests its usual local port. With `--yes`, it tries the usual local ports instead: vLLM on `8000`, llama.cpp on `8080`, SGLang on `30000`, Ollama on `11434`. If the engine serves the model under a different ID, it asks which one to use and updates your offering.
5. Offers to install a service, then prints the start command.

## Options

| Option                            | Effect                                                                                                 |
| --------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `--enroll CODE`                   | Enroll with this code. Omit when upgrading an existing install                                         |
| `--dir DIR`                       | Install directory. Defaults to `~/.canopy-provider`, or `/opt/canopy-provider` with `--service system` |
| `--version X.Y.Z`                 | Install a specific release instead of the latest                                                       |
| `--service system\|user\|launchd` | Install and start a service; see [Run as a service](#run-as-a-service)                                 |
| `--engine KIND`                   | `vllm`, `llamaCpp`, `sglang`, `ollama`, `mlx` or `tabbyApi`, instead of detecting it                   |
| `--engine-url URL`                | The engine's URL from this machine, instead of the setup profile or the usual local ports              |
| `--offering ID`                   | The offering to serve, when your account has several and the code is not scoped to one                 |
| `--name NAME`                     | The connector name shown in Canopy. Defaults to the host name                                          |
| `--app-url URL`                   | The Canopy web app. Defaults to `https://app.canopyx.ai`                                               |
| `--yes`                           | Do not prompt. Fails with guidance where a choice is needed                                            |
| `--dry-run`                       | Print the plan and exit without changing anything                                                      |
| `--uninstall`                     | Remove the service and files, and revoke the credential                                                |

## Inspect before running

Every release lives under `https://get.canopyx.ai/provider/vX.Y.Z/`, with a `SHA256SUMS` file signed as `SHA256SUMS.sig`. The installer checks every download against `SHA256SUMS` and, when `ssh-keygen` is available, refuses a release whose signature does not verify. It fetches nothing from npm.

To read the script first, download it with its checksums, verify both, dry-run it, then run it:

```sh
v=$(curl -fsSL --proto '=https' https://get.canopyx.ai/provider/latest)
for f in install.sh SHA256SUMS SHA256SUMS.sig; do
  curl -fsSLO --proto '=https' "https://get.canopyx.ai/provider/v$v/$f"
done
curl -fsSLO --proto '=https' https://get.canopyx.ai/allowed_signers
ssh-keygen -Y verify -f allowed_signers -I releases@canopyx.ai -n canopy-installer \
  -s SHA256SUMS.sig < SHA256SUMS
sha256sum --ignore-missing -c SHA256SUMS   # on macOS: shasum -a 256 --ignore-missing -c SHA256SUMS
less install.sh
sh install.sh --dry-run --enroll K7QF-29XM-...
sh install.sh --enroll K7QF-29XM-...
```

Compare the key in `allowed_signers` with the `SIGNING_KEY` line in `install.sh`.

## What it writes

| Path                        | Contents                                                         |
| --------------------------- | ---------------------------------------------------------------- |
| `<dir>/config.json`         | Canopy URL, connector ID, offering ID and engine URL. No secrets |
| `<dir>/credential`          | The connector credential, mode `0600`                            |
| `<dir>/app/provider.ts`     | Editable engine configuration and starter pricing callbacks      |
| `<dir>/app/`                | Its `package.json`, lockfile and installed SDK                   |
| `<dir>/bun/`                | The pinned Bun runtime                                           |
| `<dir>/bin/canopy-provider` | The CLI, using that runtime and this directory                   |

The installer does not add `canopy-provider` to your `PATH`. Run it as `~/.canopy-provider/bin/canopy-provider`, or `/opt/canopy-provider/bin/canopy-provider` for a system service.

You can edit `app/provider.ts`. Re-running the installer keeps your edited file.

## Run as a service

The installer asks whether to install a service, or installs the one named by `--service`. You can also install one later with `canopy-provider service install`.

| Service   | Command                                                                  | Runs as                                                           |
| --------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------- |
| `system`  | `sudo /opt/canopy-provider/bin/canopy-provider service install --system` | A sandboxed systemd unit under a dedicated `canopy-provider` user |
| `user`    | `~/.canopy-provider/bin/canopy-provider service install --user`          | A systemd user unit, kept running after you log out               |
| `launchd` | `~/.canopy-provider/bin/canopy-provider service install --launchd`       | A macOS launch agent. Logs go to `<dir>/connector.log`            |

Follow logs with `journalctl -u canopy-provider -f` (system) or `journalctl --user -u canopy-provider -f` (user). Check or remove the service with `canopy-provider service status` and `canopy-provider service uninstall`.

## Upgrade and uninstall

Run the install command again without `--enroll` to upgrade in place. The installer keeps your credential, configuration and edited `provider.ts`, and replaces Bun and the SDK with the new release.

`--uninstall` stops and removes the service, revokes the connector credential in Canopy, and removes the install directory. You can also revoke any connector from the **Install** page; it disconnects within a minute.

## Diagnose problems

```sh
~/.canopy-provider/bin/canopy-provider doctor
```

`doctor` checks the credential and account status, clock skew, the engine, an outbound WebSocket to Canopy, and Canopy's latest [public endpoint checks](/providers/reachability). It exits non-zero when any check fails. Keep NTP enabled: quote deadlines are one second.

See the [SDK reference](/providers/sdk-reference#cli) for every CLI command.

## Credentials

The connector reads its credential from, in order: the `token` set in `provider.ts`, the systemd credential `canopy-connector`, the file named by `CANOPY_PROVIDER_TOKEN_FILE`, and the `CANOPY_PROVIDER_TOKEN` environment variable. An installed connector uses its `credential` file.

For a protected engine, supply its key as the systemd credential `canopy-engine-key`, through `CANOPY_ENGINE_API_KEY_FILE`, or as `CANOPY_ENGINE_API_KEY`. The generated `provider.ts` reads it with `engineApiKey()`.
