Skip to main content
Sifr delegates all dependency resolution, lockfile management, and registry communication to Cargo. When you add a dependency to your Cargo.toml, Sifr reads the resolved package graph via cargo metadata and constructs its own Sifr package graph on top of it. This means the full power of Cargo’s ecosystem — version ranges, Git sources, path dependencies, private registries — is available to every Sifr project without Sifr having to reimplement a resolver.

Declaring dependencies

Add dependencies to Cargo.toml exactly as you would in any Rust project. Runtime dependencies go in [dependencies]; dependencies used only for tests or development tooling go in [dev-dependencies].
Normal sifr run, sifr check, sifr package, and sifr publish commands only expose runtime [dependencies] as import roots. Dev dependencies are available only in test contexts. Use sifr tree --edges dev if you want to inspect the dev dependency graph explicitly.

Fetching dependencies with sifr fetch

After adding or changing a dependency, run sifr fetch to resolve and download it:
In CI or any environment where you need a reproducible build, use --locked to prevent the lockfile from being updated:
sifr fetch delegates to Cargo under the hood and forwards your lock and network flags automatically through the package session.

Inspecting the dependency tree with sifr tree

Visualize your full dependency graph with sifr tree:
To include dev dependencies in the output:
Workspace-level tree inspection works the same way — run from the workspace root and pass --workspace if needed.

Lock modes

Sifr passes lock and network semantics directly to Cargo. Use these flags consistently across all package commands for reproducible results.
Use --locked in CI pipelines and --frozen in air-gapped or vendor-only environments. Use neither flag during local development when you intentionally want to update dependencies.
These flags apply uniformly across all Sifr commands that touch the package graph:

Pure Sifr packages

A pure Sifr package contains no non-trivial Rust code — all behavior is expressed in .sifr files. Because Cargo requires at least one build target, Sifr generates a minimal marker file at src/lib.rs:
Sifr validates this marker and rejects any package that contains non-trivial Rust in src/lib.rs but does not declare Rust-backed behavior. Do not add Rust logic to lib.rs in a pure Sifr package.

Trust policy for Rust interop

Some Sifr packages expose Rust crates or local bridge modules through @rust(...) declarations. Sifr enforces trust policy for build scripts, proc macros, native links, unsafe local bridge files, no-panic assertions, and panic-abort opt-ins. This policy is configured in sifr.toml and validated by sifr bridge check, sifr check, sifr package, and sifr publish. Packages that exceed their declared trust boundary are rejected with SIFR-RUST-TRUST-* diagnostics before the final Rust-backed package build is accepted. See Rust Interop for direct bindings, local bridges, shared bridge crates, opaque handles, async, zero-copy, callbacks, and trust examples.
Sifr wraps Cargo process failures in diagnostic code SIFR-PACKAGE-0101. If a dependency fetch, tree command, or other Cargo-delegated operation fails, run sifr --explain SIFR-PACKAGE-0101 for structured diagnostic help on SIFR-PACKAGE-0101. The explainer does not perform any package operations — it is safe to run at any time.

Python package requirements

Sifr does not resolve Python dependencies. Python packages are installed by the root application’s uv environment, and Sifr verifies the final interpreter/probe before compile or run. Dependency packages may declare:
For normal uv layout, Sifr discovers the root project’s pyproject.toml, uv.lock, .venv, and interpreter. The root application must authorize every required import root, and separately authorize native-extension roots it is willing to execute in process. Dependencies cannot select or authorize the environment. See Embedded Python Interop.

Cross-package imports and public APIs

Only names that a dependency exposes through its public namespace are importable from outside that package. A package’s public API is defined by src/__init__.sifr and any child namespaces that have their own __init__.sifr. Implementation files such as src/client.sifr are private to the package unless explicitly re-exported.
In your consuming package, you import through that public namespace:
Local package code may import its own implementation files directly. Attempting to import a dependency’s private implementation file is a compile-time error.

Keeping Cargo.toml in sync with sifr repair

After manual edits to Cargo.toml or after a merge conflict, the Sifr-managed entries in Cargo.toml may drift from the expected state. Check for drift without making any changes:
To regenerate Sifr-managed Cargo.toml entries and restore a missing pure marker: