skt recipes — adding a tool

A recipe is a single declarative TOML file describing where a tool's artifact lives and how to verify it. skt ships recipes embedded in the binary, and you can add or override one by dropping a file into your config directory — no recompile.

When you need a recipe

Runtimes (go, node, rust, python) have built-in resolvers and need no recipe. Every other CLI — trivy, kubectl, helm, and anything you add — is described by a TOML recipe. To support a new fixed-release CLI, you write one recipe file.

Resolution order

A tool name resolves to a recipe in this order, first hit wins:

  1. External override directory — ~/.config/skt/registry/<name>.toml (override the path with SKT_REGISTRY_DIR). Drop a file here to add or override a tool with no recompile.
  2. Embedded recipes — .toml files baked into the skt binary at build time. These always work offline, with zero external state.
  3. Otherwise — a hard, actionable error: tool "<name>" has no recipe (looked in <dir> and embedded registry); write a <name>.toml.

The intended workflow: prototype a new tool as an external .toml, validate it with skt install, then promote the proven file into the embedded set in a PR.

The recipe format

A recipe has three sections: [platform.*] (which asset for which OS), [install] (where to download and verify), and [resolve] (how to discover versions). Here is the real trivy recipe:

name = "trivy"
type = "cli"

[platform.linux]
download-file = "trivy_{version}_Linux-{arch}.tar.gz"
checksum-file = "trivy_{version}_checksums.txt"
exe-path      = "trivy"

[platform.macos]
download-file = "trivy_{version}_macOS-{arch}.tar.gz"
checksum-file = "trivy_{version}_checksums.txt"
exe-path      = "trivy"

[install]
download-url = "https://github.com/aquasecurity/trivy/releases/download/v{version}/{download_file}"
checksum-url = "https://github.com/aquasecurity/trivy/releases/download/v{version}/{checksum_file}"
[install.arch]                      # remap host arch → vendor token
x86_64  = "64bit"
aarch64 = "ARM64"

[resolve]
git-url         = "https://github.com/aquasecurity/trivy"
version-pattern = "^v((?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+))"

Tokens

URLs and file names are templated with a closed token set, expanded per platform and version:

tokenvalue
{version}the resolved version (e.g. 0.70.0)
{versionMajor} / {versionMinor} / {versionPatch}version components
{arch}host architecture after [install.arch] remap
{os}host OS
{libc}libc flavour (e.g. gnu, musl) where relevant
{download_file}the resolved download-file value
{checksum_file}the resolved checksum-file value

The [install.arch] table remaps the host's architecture (x86_64, aarch64) to whatever token the vendor uses in its file names (64bit, ARM64, …).

Version resolution

[resolve] tells skt how to discover and normalise versions:

Checksums

Every recipe must pin a checksum source — skt never installs an unverified artifact:

go-install tools

Go tools shipped only as modules use a type = "go-module" recipe routed to the go-install backend — a module path instead of a download URL:

name = "swag"
type = "go-module"

[install]
module = "github.com/swaggo/swag/cmd/swag"

skt resolves a go runtime first, then builds the module at the pinned version.