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:
- External override directory —
~/.config/skt/registry/<name>.toml(override the path withSKT_REGISTRY_DIR). Drop a file here to add or override a tool with no recompile. - Embedded recipes —
.tomlfiles baked into thesktbinary at build time. These always work offline, with zero external state. - 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:
| token | value |
|---|---|
{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:
git-url+version-pattern— list the repo's tags and normalise them with the regex (named capture groupsmajor/minor/patch).manifest-url+manifest-version-key— read a single-key JSON manifest.
Checksums
Every recipe must pin a checksum source — skt never installs an unverified
artifact:
checksum-url— a combinedSHA256SUMS-style file or a per-asset checksum file (as in thetrivyexample above).checksum-public-key— a minisign public key for signed releases.
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.