Skip to content

Installing spot-cli

spot-cli ships as a Docker image alongside the rest of the platform. A small POSIX wrapper invokes the image so you can call spot-cli ... from anywhere instead of typing docker compose --profile cli run --rm cli ....

Pick the path that matches how you installed SPOT.

This is the path for operators who installed SPOT from the deploy/ directory of the spot repository (the supported install method).

The CLI is an optional component — like the bundled Ollama side-car or the milter — and is enabled the same way: by adding cli to COMPOSE_PROFILES in your .env. spot-cli setup does it, and pulls the image: after setup, only the symlink of step 3 is left. The deploy stack ships:

  • a cli service in docker-compose.yml, gated on profiles: [cli], pinned to ${CORE_REGISTRY}/spot-cli:${CORE_VERSION};
  • a scripts/spot-cli wrapper that runs the image transparently, so you can type spot-cli auth login instead of the full docker compose run --rm cli auth login.

Install

cd /opt/spot/deploy                 # or wherever you cloned spot

# 1. Enable the cli profile: add `cli` to COMPOSE_PROFILES in .env,
#    after a comma if it lists others (COMPOSE_PROFILES=ollama,cli).

# 2. Pull the image. With the profile active, compose now sees the
#    cli service; without the profile this fails with
#    `no such service: cli`.
docker compose pull cli

# 3. Symlink the wrapper onto PATH: a symlink, not a copy. The wrapper
#    loads scripts/ops/common.sh, and setup, backup, restore and upgrade
#    run from the deploy directory. `plugin new` runs in the current
#    directory, with uv and git.
sudo ln -s "$PWD/scripts/spot-cli" /usr/local/bin/spot-cli

Without sudo:

mkdir -p "$HOME/.local/bin"
ln -s "$PWD/scripts/spot-cli" "$HOME/.local/bin/spot-cli"

Verify

which spot-cli
spot-cli --help
spot-cli auth login

The wrapper forwards SPOT_API_URL (default http://api-gateway:8000) and persists auth tokens on the host under ~/.config/spot-cli/ so they survive across --rm invocations. If the image is missing locally, the CLI's own commands exit with a clear pointer back at step 2 above: for them the wrapper never runs docker pull, on purpose, since keeping image pulls inside compose preserves the version pin and the COMPOSE_PROFILES gate. backup, restore and upgrade (through its backup), which run on the host, pull the image when it is missing: they read the configuration volume with it, with or without the cli profile.

Upgrade

scripts/spot-cli upgrade <version> pulls the new release's images, the CLI's with them when the cli profile is on (see the Upgrading guide). After a change of the CORE_VERSION pin in .env:

docker compose pull cli              # picks up the new tag

Old images can be reclaimed with docker image prune later.

Using raw docker compose run (no wrapper)

If you don't want the wrapper on PATH at all, the same profile unlocks the direct compose form:

docker compose run --rm cli auth login
docker compose run --rm cli health

Same image, same auth-token volume — the wrapper is only there to save typing.

On a development install (the spot repository)

For SPOT contributors working out of the SPOT_Project/spot repository directly.

There is one wrapper, deploy/scripts/spot-cli, the one operators use. It runs the image named by SPOT_CLI_IMAGE, so a developer builds the image from the working tree once and points the wrapper at it:

docker compose --profile cli build cli      # builds spot-cli:latest
export SPOT_CLI_IMAGE=spot-cli:latest
deploy/scripts/spot-cli --help

Drop the wrapper on your PATH once; a symlink keeps it in sync with repo updates (the wrapper finds its files through the link):

# Option A: system-wide
sudo ln -s "$PWD/deploy/scripts/spot-cli" /usr/local/bin/spot-cli

# Option B: without sudo, under a user-owned directory your shell has on PATH
mkdir -p "$HOME/.local/bin"
ln -s "$PWD/deploy/scripts/spot-cli" "$HOME/.local/bin/spot-cli"

If ~/.local/bin is not yet on PATH (echo $PATH to check), add it to your shell rc:

echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc   # or ~/.zshrc

Tab completion

Sourcing the included static completion stops the shell from spawning the Docker container on every <Tab>. The completion file uses bash-style syntax that works in both bash and zsh.

Bash:

# deploy install
echo 'source /opt/spot/deploy/scripts/spot-cli-completion.bash' >> ~/.bashrc

# development install
echo 'source /path/to/spot/deploy/scripts/spot-cli-completion.bash' >> ~/.bashrc

# system-wide
sudo cp <wrapper-dir>/spot-cli-completion.bash /etc/bash_completion.d/spot-cli

Zsh (load bashcompinit first so complete -F is recognised):

# Once, in ~/.zshrc:
autoload -U +X bashcompinit && bashcompinit
autoload -U +X compinit     && compinit
source /opt/spot/deploy/scripts/spot-cli-completion.bash

The completion is intentionally static (it knows the command groups, not their options) so <Tab> costs zero processes.

Running against a remote deployment

The wrapper attaches to the platform's docker network (auto-resolved from compose labels, e.g. spot_spot-network) and forwards SPOT_API_URL. Override either when needed:

SPOT_CLI_NETWORK=other_spot-network spot-cli health
SPOT_API_URL=https://spot.example.com spot-cli auth login

Other knobs the wrapper honours: SPOT_CLI_IMAGE (defaults to the deploy registry image or spot-cli:latest for the dev install) and SPOT_TOKEN_DIR_HOST (default ~/.config/spot-cli).

For a deployment exposed over the network rather than via local compose, override the api-gateway URL directly:

SPOT_API_URL=https://spot.example.com spot-cli auth login

Benchmark directories

spot-cli benchmark reads raw datasets from SPOT_DATASETS_DIR and writes normalized files plus run outputs to SPOT_BENCHMARK_DIR. The wrapper bind-mounts the host paths into the container when these are set:

export SPOT_DATASETS_DIR=/home/me/spot/datasets
export SPOT_BENCHMARK_DIR=/home/me/spot/benchmarks

spot-cli benchmark normalize --all
spot-cli benchmark run --workflow default-workflow --all

SPOT_DATASETS_DIR is mounted read-only (raw corpora are never mutated). SPOT_BENCHMARK_DIR is read-write and is created on the host if it does not already exist.

Next steps