Skip to main content
Every Sifr package has two manifests: Cargo.toml and sifr.toml. While Cargo.toml handles distribution concerns such as registries, lockfiles, and publishing credentials, sifr.toml is where you configure everything the Sifr compiler cares about: your package name, edition, required compiler version, source root, scripts, and trust policy for backend Rust crates. This page covers every section of sifr.toml and explains what belongs there rather than in Cargo.toml.

A complete sifr.toml example

[package] — identity and compiler requirements

The [package] section identifies your package to the Sifr compiler and locks it to a compatible compiler range.
The name in sifr.toml is the Sifr package name used for imports and diagnostics. The Cargo package name in Cargo.toml follows the sifr-<kebab-case-name> convention and is distinct — do not assume they are the same string.
A minimal [package] section for a library package looks like this:

[source] — source roots

The [source] section tells Sifr where to find your .sifr source files.
roots defaults to ["src"] for production packages — you can omit the section entirely in a standard layout. You only need to specify roots explicitly when your source files live outside src/, which is an uncommon scenario reserved for legacy migration or non-standard project shapes.
New production packages should not use [exports].modules or Sifr manifest [[bin]] target tables. These are preserved only for legacy layout migration. Use src/__init__.sifr for library public APIs and src/bin/*.sifr for additional app targets instead.

[format] — formatter settings

The [format] section configures the Sifr source formatter. Settings here apply when you run sifr fmt.
Consult the Format & Lint guide for the full list of available keys.

[lint] — lint configuration

The [lint] section controls which diagnostics the Sifr linter reports. You can select lint rule sets and suppress specific codes.
Start with select = ["all"] and add ignore entries only for rules that conflict with your team’s style. This keeps your lint surface maximal by default.

[scripts] — structured command plans

Scripts let you define reusable Sifr command sequences and invoke them with sifr run --script <name>. Each entry is a structured Sifr command plan — not a shell string.
Invoke a script with:
Scripts do not execute shell strings or external programs. A script may only expand a single Sifr command. Nested script expansion — calling one script from another — is rejected with SIFR-PACKAGE-0714.
If an app target and a script share the same name, Sifr reports SIFR-PACKAGE-0605 and requires you to disambiguate with --bin or --script.

What goes in Cargo.toml vs sifr.toml

Knowing where to put configuration prevents confusion and keeps Sifr’s tooling working correctly. Put in sifr.toml:
  • Package name, edition, sifr-version
  • Source root ([source].roots)
  • Formatter and lint configuration
  • Named scripts
  • Rust interop bridge metadata and native trust policy
  • Root application Python environment selection and Python trust policy
Put in Cargo.toml:
  • Distribution metadata (description, authors, license, repository URL)
  • Dependency declarations ([dependencies], [dev-dependencies])
  • Registry and Git/path source configuration
  • include and exclude archive rules for publishing
  • Workspace configuration
  • Publishing credentials (never in sifr.toml or any Sifr-generated output)
The Cargo metadata hook is the bridge between the two files. Sifr reads cargo metadata --format-version 1, locates sifr.toml via the pointer below, and derives the Sifr package graph from Cargo’s resolved dependency graph:

[rust] and Rust interop trust policy

Rust-backed packages declare Sifr-owned bridge projection metadata in [rust] and execution trust evidence in [trust].
bridges lists package-local Rust bridge roots that Sifr may reference through the bridge decorator root. The bridge contract is selected by the compiler; there is no manifest version selector. Files such as src/bridges/tokenizer.rs are user-owned; Sifr-managed projection files are checked with sifr repair --check and regenerated with sifr repair. See Rust Interop for direct bindings, local bridges, shared bridge crates, opaque handles, async, zero-copy, callbacks, and the sifr bridge check workflow.

[python] and Python trust policy

Embedded Python interop is configured in sifr.toml. The root application selects exactly one uv-created virtual environment; libraries may only declare import roots that the final application must provide. Root application example:
Normal uv projects need no [python] path configuration: Sifr discovers the nearest pyproject.toml plus uv.lock, .venv, and its interpreter. The root application may set venv, pyproject, lock, or interpreter only for a non-standard layout. Library package example:
requires-imports is reserved for raw/dynamic imports that static declarations and bridge inventories cannot derive. Requirements from every package are deduplicated with provenance; only the root application may authorize them via [trust]. Sifr verifies uv lock --check --offline and the selected interpreter before generated Python-enabled binaries run. It never runs uv sync or mutates the environment. See Embedded Python Interop for runtime, blocking, callback, resource, and zero-copy semantics.

Keeping Cargo.toml in sync with sifr repair

If your Cargo.toml gets out of sync with what Sifr expects — for example after a manual edit or a merge conflict — you can check for drift without writing any files:
To regenerate Sifr-managed Cargo.toml entries and restore a missing pure marker: