Package Manager Guide

Create, build, and manage Iron projects and their dependencies.

v3.0.0-alpha

This guide covers iron package manager commands, which are unchanged in v3. For the current language syntax, including methods inside object blocks and the pub visibility model, see the language reference.

Overview

Iron ships a two-binary toolchain inspired by Rust's Cargo/rustc split:

BinaryRoleAnalogy
ironPackage manager — projects, dependencies, buildsCargo
ironcRaw compiler — compiles single .iron files to native binariesrustc

For most workflows, you only interact with iron. It reads your project manifest (iron.toml), resolves dependencies, and invokes ironc behind the scenes.

The Toolchain

iron and ironc are installed side by side in ~/.iron/bin/. The iron binary discovers ironc automatically as a sibling binary — no configuration needed.

When to use which

Use caseCommand
Create a new projectiron init
Build a projectiron build
Compile a single fileironc build hello.iron
Quick scriptiron run script.iron

iron also transparently forwards single-file commands to ironc. If you run iron build hello.iron (with a .iron file argument), it silently delegates to ironc.

Creating a Project

Binary project

$ iron init
  Created binary project `my-project`

$ tree
.
├── iron.toml
├── .gitignore
└── src/
    └── main.iron

Library project

$ iron init --lib
  Created library project `my-lib`

$ tree
.
├── iron.toml
├── .gitignore
└── src/
    └── lib.iron

iron init works in non-empty directories. It creates files that don't exist and skips files that already do — safe to run in an existing repo.

Project Structure

my-game/
├── iron.toml          # Project manifest
├── iron.lock          # Lockfile (auto-generated, commit to VCS)
├── .gitignore         # Ignores target/
├── src/
│   └── main.iron      # Entry point (bin) or lib.iron (lib)
├── tests/
│   └── test_math.iron # Test files discovered by iron test
└── target/
    ├── my-game        # Built binary
    └── combined.iron  # Concatenated source (when deps exist)
Conventions

Entry point is determined by convention: src/main.iron for binaries, src/lib.iron for libraries. No entry field needed.

iron.toml — Manifest Format

The manifest file describes your package and its dependencies.

[package]

[package]
name        = "my-game"
version     = "0.1.0"
type        = "bin"           # "bin" (default) or "lib"
description = "A cool project" # optional
FieldRequiredDescription
nameYesPackage name (used for binary output filename)
versionYesSemantic version string
typeNo"bin" (default) or "lib"
descriptionNoShort description of the package

[dependencies]

[dependencies]
raylib   = true                                          # built-in
iron-ecs = { git = "user/iron-ecs", version = "0.1.0" }  # GitHub dep

Dependencies are specified as inline TOML tables with two fields:

FieldFormatDescription
git"owner/repo"GitHub repository in short form
version"X.Y.Z"Version tag to resolve (tries vX.Y.Z then X.Y.Z)
GitHub-only (alpha)

During alpha, dependencies are resolved exclusively from GitHub. The git field uses the short owner/repo format — Iron prepends https://github.com/ automatically.

iron init

Create a new Iron project in the current directory.

$ iron init          # binary project
$ iron init --lib    # library project

Creates iron.toml, src/main.iron (or src/lib.iron), and .gitignore. Runs git init if not already in a git repo. Skips existing files.

iron build

Build the current package.

$ iron build
   Downloading iron-ecs v0.1.0 (user/iron-ecs)
   Compiling my-game v0.1.0
    Finished dev [unoptimized] in 0.42s

Reads iron.toml, resolves and fetches dependencies, concatenates source files, and invokes ironc. Output binary is placed in target/.

FlagDescription
--verboseShow generated C code (passed through to ironc)

iron run

Build and immediately execute the package binary.

$ iron run
   Compiling my-game v0.1.0
    Finished dev [unoptimized] in 0.38s
     Running target/my-game
Hello, Iron!

Arguments after -- are passed to the built binary:

$ iron run -- --port 8080

iron check

Type-check the package without producing a binary. Fast feedback loop.

$ iron check
    Checking my-game v0.1.0
    Finished check completed

iron test

Discover and run all .iron files in the tests/ directory.

$ iron test
     Testing my-game v0.1.0
     Running test_math.iron
     Running test_strings.iron
    Finished 2 test(s) passed

Adding Dependencies

Edit iron.toml to add a dependency:

[dependencies]
iron-math = { git = "user/iron-math", version = "0.2.0" }

On the next iron build, Iron will:

  1. Resolve the version tag to a commit SHA via the GitHub API
  2. Download and extract the source tarball into ~/.iron/cache/
  3. Concatenate dependency source files with your project source
  4. Write the resolved SHA to iron.lock

How Resolution Works

Tag resolution

Iron resolves version tags to 40-character commit SHAs using the GitHub REST API:

  1. Try v{version} tag first (e.g., v0.2.0)
  2. Fall back to {version} (e.g., 0.2.0)
  3. Handle annotated tags with a two-step API dereference

The resolved SHA (never a mutable tag name) is stored in iron.lock for reproducibility.

Build composition

Iron concatenates all source files into target/combined.iron in this order:

  1. Transitive deps (leaf dependencies first)
  2. Direct deps (in iron.toml declaration order)
  3. Project source (src/*.iron alphabetically, main.iron last)

Within each dependency, lib.iron is placed first (if it exists), then remaining files alphabetically.

The Lockfile

iron.lock pins every dependency to an exact commit SHA. It is auto-generated on first build and should be committed to version control.

# This file is auto-generated by iron. Do not edit manually.

lock_version = 1

[[package]]
name    = "iron-ecs"
version = "0.1.0"
git     = "user/iron-ecs"
sha     = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"

Lockfile behavior

ScenarioWhat happens
No iron.lockAll deps resolved via GitHub API, lockfile created
Lockfile existsLocked SHAs used directly (no API calls)
New dep in iron.tomlOnly new dep resolved, existing entries preserved
Dep removed from iron.tomlEntry pruned from lockfile on next build
Cache deletedRe-fetched using exact SHAs from lockfile

Cache

Downloaded dependency sources are stored globally in ~/.iron/cache/:

~/.iron/cache/
└── user/
    └── iron-ecs@a1b2c3d4.../
        ├── iron.toml
        └── src/
            └── lib.iron

The cache is shared across all projects on the machine. A second project using the same dependency at the same version reuses the cached source without re-downloading.

To force a clean re-fetch, delete the cache: rm -rf ~/.iron/cache/

Transitive Dependencies

If your dependency has its own [dependencies] in its iron.toml, Iron resolves them recursively. All transitive deps are flattened into iron.lock.

Diamond dependencies

If A depends on B and C, and both B and C depend on D at the same version, D is included only once.

Version conflicts

If two dependencies require different versions of the same transitive dep, Iron refuses to build and shows which deps conflict:

error: version conflict for user/iron-math
  iron-ecs requires v0.2.0
  iron-physics requires v0.1.0

  hint: align dependency versions in the respective iron.toml files

Circular dependencies

If A depends on B which depends on A, Iron detects the cycle and prints the full chain:

error: circular dependency detected
  -> user/iron-ecs
    -> user/iron-math
      -> user/iron-ecs  (cycle)

  hint: remove the circular dependency from one of the packages

Environment Variables

VariableDescription
IRON_GITHUB_TOKENGitHub auth token for API requests (5000/hr vs 60/hr). Checked first.
GITHUB_TOKENFallback auth token (auto-set in GitHub Actions). Checked second.
NO_COLORDisable colored output when set to any value.
FORCE_COLORForce colored output even when not a TTY.
CI tip

In GitHub Actions, GITHUB_TOKEN is set automatically. No configuration needed for dependency resolution in CI.

Error Messages

ErrorCauseFix
tag 'X' not found for owner/repoVersion tag doesn't exist on GitHubCheck that the repo has the tag (with or without v prefix)
GitHub API rate limit exceededToo many unauthenticated requestsSet IRON_GITHUB_TOKEN for 5000 req/hr
circular dependency detectedDep graph has a cycleRemove the circular dep from one package
version conflict for ...Two deps need different versions of the same transitive depAlign versions across your dependency tree
has no iron.tomlA dependency is not a valid Iron packageEnsure the dep has an iron.toml at its root
no iron.toml foundNot in a project directoryRun iron init or cd into a project

FAQ

Do I commit iron.lock?

Yes. The lockfile ensures reproducible builds. Without it, teammates and CI may resolve different commits.

Can I use private repos?

Not yet. Alpha supports public GitHub repositories only. Private repo auth is planned for a future release.

What about semver ranges?

Iron pins to exact commit SHAs, not version ranges. The ecosystem is currently too small for version conflict resolution. Semver ranges may be added in a future release.

Where are deps downloaded?

To ~/.iron/cache/ (global, shared across all projects). Delete this directory to force a clean re-fetch.

Can I use local path dependencies?

Not yet. Local path dependencies (path = "../my-lib") are planned for a future release.

How do I update a dependency?

Delete the dep's entry from iron.lock and re-run iron build. It will re-resolve the tag to the latest commit SHA. An iron update command is planned.