Packages
Turbo has a small, built-in package manager. Dependencies are declared in turbo.toml, pinned in turbo.lock, and installed into a local turbo_modules/ directory. There is no central package server — packages are Git repositories, and the registry is a curated static index.
Browse published packages at turbolang.dev/packages or from the terminal with turbolang search.
Declaring dependencies
Add dependencies under [dependencies] (or [dev-dependencies]) in turbo.toml. Three source forms are supported:
[dependencies]
# 1. Registry version — resolved to a GitHub repo + a matching git tag
turbo-http = "0.1"
# 2. GitHub repo — pin by tag range (version), exact commit (rev), or lockfile
agent-kit = { github = "owner/agent-kit", version = "1.2" }
fixed = { github = "owner/fixed", rev = "a1b2c3d" }
# 3. Local path — symlinked into turbo_modules (great for workspaces)
mylib = { path = "../mylib" }
[dev-dependencies]
turbo-test-utils = "0.1"A version like "0.1" selects the latest 0.1.x tag; a full "0.1.0" pins that exact tag. Tags may be written with or without a leading v.
Installing
turbolang installinstall reads turbo.toml, resolves every dependency, and places it under turbo_modules/<name>:
- Path dependencies are symlinked (copied on non-Unix).
- GitHub and registry dependencies are cloned and checked out at the resolved commit, then recorded in
turbo.lock. - If a dependency is already present, it is re-pinned to the resolved commit rather than re-cloned.
In your source, import a package by its name — the resolver looks for turbo_modules/<name>/src/lib.tb (then src/<name>.tb):
import turbo_httpThe lockfile
turbo.lock pins every Git-backed dependency to an exact commit for reproducible installs. It is generated and overwritten by install and update, and looks like:
[github]
turbo-http = "ZVN-DEV/turbo-http#a1b2c3d4e5f6..."When a dependency lists neither a rev nor a version, install reuses the commit recorded here — so committing turbo.lock gives your collaborators the exact same dependency tree. Commit it for applications; libraries typically leave it out.
Updating
turbolang updateupdate moves Git-backed dependencies to the newest commit allowed by their declaration (the latest tag for a version, a fast-forward pull for a bare GitHub repo) and rewrites turbo.lock. Path dependencies are left untouched. Run installfirst if a dependency isn't present yet.
Registry resolution
A bare registry dependency (form 1) is a name, not a repo. Turbo maps the name to a GitHub repo by checking, in order:
- An explicit
[registries]entry in yourturbo.toml(a per-project override). - The published registry index at
turbolang.dev/registry/index.json. - The built-in default: any
turbo-*name maps toZVN-DEV/<name>.
[registries]
# Point a name at any repo you control
cool-lib = "myorg/cool-lib"If the index can't be reached, resolution simply falls back to the built-in default — a registry outage never blocks an install.
Finding packages
turbolang search json # match name, description, or category
turbolang search # list every published packagesearch fetches the published index and filters it case-insensitively, printing each match with a ready-to-run turbolang install command.
Publishing a package
The registry is a single JSON file in the Turbo repository. Publishing is an ordinary pull request — no account, no upload:
- Ship a Git repo with a
turbo.tomland asrc/lib.tbentry point, tagged with a semver release (e.g.v0.1.0). - Open a PR against
registry/index.jsonappending one entry to thepackagesarray. - On merge it's served at
turbolang.dev/registry/index.jsonand appears on the packages page and inturbolang search.
{
"name": "turbo-json", // unique; matches the turbo_modules dir name
"repo": "owner/turbo-json", // GitHub "owner/name"
"description": "A fast JSON parser and serializer",
"categories": ["serialization"], // lowercase tags
"min_turbo_version": "0.12.0", // optional: semver the package needs
"homepage": "https://example.com" // optional
}