Turnkey is toolchain management for Nix flakes. You declare the tools a
project needs in a small toolchain.toml; turnkey resolves them to pinned Nix
packages and puts them in a devenv shell. For projects
built with Buck2, it also ships a pinned buck2 release
and prelude, and turns each language's native dependency files (go.mod,
Cargo.lock, uv.lock, pnpm-lock.yaml, ...) into Buck2 dependency cells
built by Nix.
Turnkey is a library, not an application: projects import it as a flake input
and configure it through its flake-parts module. This
repository uses itself that way, so its own flake.nix and toolchain.toml
are a working example.
Supported languages: Go, Rust, Python, TypeScript, Solidity and Jsonnet.
-
Nix with flakes enabled. If they aren't, add this to
~/.config/nix/nix.conf:experimental-features = nix-command flakes -
direnv (recommended), hooked into your shell, so the environment loads when you enter the project.
Start from the template, which sets up a Go project built with Buck2:
mkdir my-project && cd my-project
nix flake init -t github:firefly-engineering/turnkey
direnv allow # or: nix developAdd turnkey, flake-parts and devenv to your flake inputs, import the modules
and point turnkey at your toolchain.toml:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
devenv.url = "github:cachix/devenv";
turnkey.url = "github:firefly-engineering/turnkey";
};
outputs = inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
imports = [
inputs.devenv.flakeModule
inputs.turnkey.flakeModules.turnkey
];
systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
perSystem = { ... }: {
turnkey.toolchains = {
enable = true;
declarationFiles.default = ./toolchain.toml;
# Optional: turnkey's pinned buck2, plus a Go dependency cell
buck2 = {
enable = true;
go = {
enable = true;
depsFile = ./go-deps.toml;
};
};
};
};
};
}# toolchain.toml
[toolchains]
go = {}Don't declare buck2 in toolchain.toml: buck2.enable adds turnkey's
pinned release. Each buck2.<language>.enable puts that language's
dependency generator on PATH and registers its cell with Buck2.
Then enter the shell with direnv allow or nix develop, and build:
tk build //...The user manual
covers installation,
project setup, the
full toolchain.toml reference
and each language.
A turnkey shell provides two CLI tools.
tk wraps buck2. Before any command that reads the build graph (build,
test, run, query, ...), it syncs the generated files the graph depends
on: dependency cells such as go-deps.toml, and rules.star files. Other
commands (clean, kill, ...) pass straight through.
tk build //some:target # sync, then build
tk test //... # sync, then test
tk sync # sync stale files explicitly
tk check # report stale files without changing them (for CI)
tk --no-sync build //... # skip the dependency sync (--no-rules-sync skips rules.star)What gets synced, and from which inputs, is configured in
.turnkey/sync.toml.
tw wraps the language's own tools (go, cargo, uv). It records the
dependency files before the command runs and, if the command changed them,
runs the matching sync, so adding a dependency the usual way keeps the Buck2
side current.
tw go get github.com/pkg/errors
tw cargo add serde
tw uv add requests
tw -v go get ... # show what tw detects and syncsIn a turnkey shell the go, cargo and uv on PATH are thin wrappers that
call tw for you, so a plain go get does the same. Set TURNKEY_NO_WRAP=1
to run the real tool directly.
The CLI reference documents both tools in full.
- User manual (source): using turnkey in a project
- Developer manual (source): working on turnkey itself
- Architecture decisions
Clone the repository and run direnv allow in it; the dev shell provides every
toolchain turnkey itself uses.
Work is tracked in GitHub Issues, prioritised in the turnkey project.
- Find work:
gh issue list --search "-is:blocked no:assignee" - Claim work:
gh issue edit <n> --add-assignee @me - Submit work: commit with
Fixes #<n>, then push
See AGENTS.md for detailed workflow instructions.