Skip to content

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 install

install 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_http

The 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 update

update 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:

  1. An explicit [registries] entry in your turbo.toml (a per-project override).
  2. The published registry index at turbolang.dev/registry/index.json.
  3. The built-in default: any turbo-* name maps to ZVN-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 package

search 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:

  1. Ship a Git repo with a turbo.toml and a src/lib.tb entry point, tagged with a semver release (e.g. v0.1.0).
  2. Open a PR against registry/index.json appending one entry to the packages array.
  3. On merge it's served at turbolang.dev/registry/index.json and appears on the packages page and in turbolang 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
}