Security-first CLI tool installer. If it can't be verified, it doesn't get installed.
Scuta installs, updates, and pins CLI tools from GitHub releases with fail-closed verification at every step: SHA256 checksums by default, plus opt-in signature verification, cosign/SLSA provenance, signed registries and org policies, tamper detection, and signed air-gapped bundles for offline environments.
Bring your own tools. The bundled registry is intentionally tiny — Scuta is built to point at your catalog, whether that's a per-machine local registry or a self-hosted one shared across your org. See Run Your Own Registry below, or the full docs/REGISTRY.md for the local vs. hosted setup and manifest schema.
brew install sid-technologies/scuta/scutago install github.com/sid-technologies/scuta@latestgit clone https://github.com/sid-technologies/Scuta.git
cd Scuta
make build
./dist/scuta init# Set up Scuta on your machine (interactive setup)
scuta init
# Install every tool in your registry
scuta install --all
# Check what's available
scuta list
# Update everything
scuta update| Command | Description |
|---|---|
scuta init |
Setup ~/.scuta/, detect auth, configure PATH |
scuta init --from <url> [--key <pub.pem>] |
Non-interactive bootstrap from an org config URL |
scuta install <tool> |
Install a tool from the registry |
scuta install <tool> --from <archive> |
Install from a local archive (offline) |
scuta install --all |
Install all tools |
scuta install --system |
Install to system-wide location (requires sudo) |
scuta uninstall <tool> |
Remove a tool |
scuta uninstall --system |
Uninstall from system-wide location (requires sudo) |
scuta update [tool] |
Update one or all tools |
scuta update --system |
Update system-wide installations (requires sudo) |
scuta list |
Show all tools + versions + install status |
scuta info <tool> |
Show detailed information about a tool |
scuta doctor |
Health check (PATH, binaries, state, CVEs) |
scuta doctor --skip-cve |
Skip CVE check (for offline environments) |
scuta doctor --audit |
Security audit: provenance, tamper detection, policy, posture |
scuta doctor --audit --json |
Audit report as JSON (for CI / fleet aggregation) |
scuta history |
Show install/update history |
scuta rollback <tool> |
Reinstall the previous version from history |
scuta sync |
Reconcile installed tools to a declarative manifest (scuta.lock.yaml) |
scuta sync --dry-run |
Show the reconciliation plan without applying it |
scuta sync --prune |
Also remove installed tools absent from the manifest |
scuta sync --check |
Exit 8 if the machine has drifted from the manifest (CI gate) |
scuta cache info |
Show download cache location, entry count, and size |
scuta cache clear |
Remove all cached downloads |
scuta self-update |
Update Scuta itself |
scuta version |
Print version |
Pin your whole toolset in a manifest and converge every machine to it. Commit
scuta.lock.yaml to a repo, and every engineer runs scuta sync:
# scuta.lock.yaml
tools:
# Registry tool, pinned version (shorthand form):
pilum: "0.7.5"
# Arbitrary public repo whose binary name differs from the repo name:
ripgrep:
version: "14.1.0"
repo: "BurntSushi/ripgrep"
bin: "rg"
# Unpinned — installed only when missing, kept current by `scuta update`:
bat: "latest"scuta sync # converge to the manifest
scuta sync --dry-run # preview the plan
scuta sync --prune # also uninstall tools not listed
scuta sync -f path/to.yaml # use a specific manifest
scuta sync --check # CI gate: exit 8 on drift, change nothingSync looks for scuta.lock.yaml, scuta.lock.yml, scuta.yaml, or
scuta.yml in the current directory when -f is omitted. Pinned versions are
fail-closed on checksum verification for registry-blessed tools; direct repos
fall back to best-effort when a release ships no checksums file.
| Command | Description |
|---|---|
scuta bundle create [tool...] |
Create an offline bundle with tool archives |
scuta bundle create --from-manifest <file> |
Bundle exactly what a scuta.lock.yaml pins |
scuta bundle create --platforms <os/arch,...> |
Include builds for multiple target platforms |
scuta bundle create --sign <key.pem> |
Embed a signed manifest in the bundle |
scuta bundle verify <bundle> [--key <pub.pem>] |
Verify bundle signature and asset checksums |
scuta bundle install <bundle> |
Install tools from an offline bundle |
| Command | Description |
|---|---|
scuta config list |
Show all config values (merged: system + remote + local) |
scuta config get <key> |
Get a config value (effective merged value) |
scuta config set <key> <value> |
Set a config value (local config only) |
scuta config reset <key> |
Reset a config value to its default |
Valid config keys: update_interval, github_token, registry_url, github_base_url, policy_url, config_url, telemetry, require_signature, require_signed_metadata, signature_public_key, audit_log_destination, disable_download_cache, provenance_verify, cosign_identity_regexp, cosign_oidc_issuer
| Command | Description |
|---|---|
scuta registry list |
List local registry entries |
scuta registry list --all |
Show merged registry with source info |
scuta registry add <name> --repo <owner/repo> |
Add a tool to the local registry |
scuta registry remove <name> |
Remove a tool from the local registry |
| Command | Description |
|---|---|
scuta admin keygen |
Generate an Ed25519 signing key pair |
scuta admin sign <file> --key <key> |
Create a detached signature (<file>.sig) |
scuta admin verify <file> |
Verify a file against its detached signature |
scuta completion bash > /etc/bash_completion.d/scuta
scuta completion zsh > "${fpath[1]}/_scuta"
scuta completion fish > ~/.config/fish/completions/scuta.fishUse --system to install tools to /usr/local/bin (or C:\Program Files\Scuta\bin on Windows) instead of ~/.scuta/bin/. This requires root/admin privileges:
sudo scuta install --all --system
sudo scuta update --system
sudo scuta uninstall <tool> --systemSystem-wide state is stored in /etc/scuta/ (or C:\ProgramData\Scuta on Windows).
For environments without internet access, use bundles to transport tools:
# On a connected machine: create a bundle
scuta bundle create pilum
# Bundle exactly what your manifest pins, for multiple platforms, signed:
scuta bundle create --from-manifest scuta.lock.yaml --platforms darwin/arm64,linux/amd64 --sign scuta-signing.key
# Transfer the .tar.gz bundle to the air-gapped machine, then:
scuta bundle verify ./scuta-bundle-20260319.tar.gz --key scuta-signing.pub
scuta bundle install ./scuta-bundle-20260319.tar.gz
# Or install a single tool from a local archive:
scuta install pilum --from ./pilum_2.1.5_darwin_arm64.tar.gzScuta verifies every download:
- Checksum verification (default): SHA256 checksums are verified against the release's
checksums.txt. Fails if checksums are missing (use--skip-verifyto override). - Signature verification (opt-in): Enable with
scuta config set require_signature trueand provide a PEM public key viascuta config set signature_public_key <pem>. Supports RSA, ECDSA, and Ed25519. When enabled, installs fail if no.sigfile is found. - Signed metadata (opt-in): remote registry, policy, and org config fetches are verified against a detached
.sigfile using the samesignature_public_keytrust root. Enable fail-closed mode withscuta config set require_signed_metadata true. Operators sign withscuta admin keygen/scuta admin sign— see docs/REGISTRY.md. - Signed bundles:
scuta bundle create --sign <key>signs the bundle manifest (which pins every asset by SHA-256, across all platforms in the bundle).bundle verifyandbundle installcheck the signature against thesignature_public_keytrust root; an invalid signature is always fatal, andrequire_signature truemakes unsigned bundles fail closed (and blocks--skip-verify— the policy is authoritative). Asset checksums are verified on every install either way. - Provenance verification (opt-in): cosign keyless signatures and SLSA build provenance are checked via the
cosign/slsa-verifierCLIs when present on PATH. Enable withscuta config set provenance_verify auto(verify when a release ships sigstore bundles,.sig+.pempairs, a cosign-signedchecksums.txt, or*.intoto.jsonlattestations; skip quietly otherwise) orrequire(fail unless at least one backend verifies). Present-but-invalid material is always fatal, in any mode. The expected signer identity defaults to the release repository's GitHub Actions workflows; pin it explicitly withcosign_identity_regexp/cosign_oidc_issuer(local/system config only — never honored from remote config, and remote config can strengthen but never weaken the mode). Verified backends are recorded in state and surfaced byscuta doctor --audit. - Policy enforcement: Organizations can enforce version constraints via a remote
policy_url— allowed/blocked versions, minimum Scuta version. - Download cache: verified assets are cached content-addressed by SHA-256 under
~/.scuta/cache, so repeat installs skip the network without weakening verification — only checksum-verified assets are ever cached, and entries are re-hashed on every hit. Inspect withscuta cache info; disable withscuta config set disable_download_cache true.
Telemetry is opt-in and disabled by default. When enabled, Scuta records events locally to ~/.scuta/telemetry.jsonl:
- Event type (install, update, uninstall, self-update)
- OS and architecture
- Timestamp
No tool names, versions, or personal information is collected.
scuta config set telemetry true # enable
scuta config set telemetry false # disableThe bundled registry is intentionally tiny — it is meant to be replaced with yours. Point Scuta at your own list of tools and it becomes an org-wide version manager for whatever CLIs your team ships or depends on (private repos, curated public tools, or both).
Scuta merges three layers, later wins on conflicts:
embedded (baked in) → remote (registry_url, cached 1h) →
local (~/.scuta/local.yaml, per-machine).
Local only — no hosting:
scuta config set registry_url local
scuta registry add ripgrep --repo BurntSushi/ripgrep --description "fast grep"
scuta install ripgrepHost your own — org-wide: write a registry.yaml, serve it over HTTPS
(GitHub raw, a private repo, S3, any static host), and point every machine at it:
scuta config set registry_url https://raw.githubusercontent.com/acme/tools/main/registry.yaml
scuta config set github_token <token> # only for private registries/repos
scuta install --allMinimal manifest:
# registry.yaml
tools:
pilum:
description: "Multi-cloud deployment CLI"
repo: sid-technologies/Pilum
ripgrep:
description: "Recursively search directories"
repo: BurntSushi/ripgrep
bin: rg # executable name if it differs from the tool nameInspect the merged result anytime:
scuta registry list --all # shows a SOURCE column: embedded / remote / localFull schema (asset templates, os/arch maps, version prefixes, dependencies) and hosting options are in docs/REGISTRY.md.
During scuta init you can pick a mode up front:
| Mode | Description |
|---|---|
| Public (default) | Uses the built-in starter registry — no auth needed |
| Private | Uses a private GitHub-hosted registry (requires token) |
| Local only | No remote registry — manage tools manually via scuta registry add |
Change anytime with scuta config set registry_url <url> or scuta config set registry_url local.
Point a new machine at your org in one command — no prompts, CI-friendly:
scuta init --from https://example.com/scuta/config.yaml --key org.pubThis installs org.pub as the local trust root before anything is fetched,
enables require_signed_metadata (all remote metadata fails closed from then
on), verifies and applies the org config, and saves the URL as config_url so
future runs keep pulling org settings (registry, policy, security flags).
Bootstrapping without --key works but is unverified — Scuta warns.
To publish an org config, start from the registry-starter template.
| Flag | Description |
|---|---|
--verbose, -v |
Show detailed output |
--quiet, -q |
Suppress non-error output |
--json |
Output in JSON format |
Direction, not commitment (as of v1.x):
- PATH shadowing detection: warn when an unmanaged binary earlier in PATH shadows a Scuta-verified one
- Audit adapters for existing package managers (brew, apt, mise), answering the same four questions Scuta answers for its own installs: where did it come from, does the binary still match, is there provenance, has it drifted
- SBOM export for managed tools
Non-goals: replacing your package manager, language-level dependencies (npm, pip, cargo), Windows package managers.
BSL 1.1