Skip to content

Project configuration (incan.toml)

This is the reference for the incan.toml project manifest format. For a practical guide to managing dependencies, see: Managing dependencies.

Overview

incan.toml is an optional project manifest that lives at your project root. It declares project metadata, build configuration, Incan library dependencies, Rust crate dependencies, and optional vocab companion crate settings. Project-aware commands discover it by walking upward from the current working directory, and file-oriented commands may also resolve it from the provided source path.

my_project/
├── src/
│   └── main.incn
├── tests/
│   └── test_main.incn
├── incan.toml            # Project manifest
└── incan.lock            # Generated lock file (commit to VCS)

You can scaffold a full new project (manifest, entry point, starter test, README, and .gitignore) with incan new. Use incan init when you already have a directory and want to add Incan project files there.

[project]

Project metadata. All fields are optional.

[project]
name = "my_app"
version = "0.1.0"
description = "A short description of what this project does"
authors = ["Alice <alice@example.com>"]
license = "MIT"
readme = "README.md"
requires-incan = ">=0.2"

[project.scripts]

Named entry points for CLI commands:

[project.scripts]
main = "src/main.incn"        # This is the default entry point if no other is set
migrate = "src/migrate.incn"  # This is an example of a named entry point called "migrate"

incan new and incan init set main = "src/main.incn" by default. When main is set, incan lock can run without a FILE argument.

[project.features]

Optional feature flags that can enable optional dependencies:

[project.features]
full = ["fancy_logging", "metrics"]

[build]

Build configuration. All fields are optional.

[build]
rust-edition = "2021"       # Rust edition for the generated Cargo.toml (default: compiler-chosen)
profile = "release"         # Cargo build profile
target = "x86_64-unknown-linux-gnu"  # Cross-compilation target
source-root = "src"         # Source root for module resolution (default: convention-based)

source-root

The directory where the compiler and test runner look for user modules. Resolution order:

  1. Explicit: if source-root is set, that directory is used (relative to project root)
  2. Convention: if a src/ directory exists at the project root, it is used automatically
  3. Fallback: the project root itself (flat layout)

Most projects use the conventional src/ layout and don't need to set this field. It exists for projects that keep their source in a different directory (e.g. lib/).

[tool.incan.envs]

Named project environments define reusable command contexts for incan env. They are useful when a project has different test, build, or release workflows that need different environment variables, working directories, or script arguments. The ambient default environment is always available; defining [tool.incan.envs.default] customizes it.

[tool.incan.envs.default]
env-vars = { INCAN_NO_BANNER = "1" }

[tool.incan.envs.default.scripts]
test = ["incan", "test", "tests/"]

[tool.incan.envs.release]
extends = ["default"]
env-vars = { INCAN_FANCY_ERRORS = "1" }

[tool.incan.envs.release.scripts]
build = ["incan", "build", "src/main.incn", "--locked"]

Fields:

Field Type Description
extends list of strings Other environments to merge before this one
detached bool Do not include default automatically
cwd string Working directory for scripts, relative to the project root unless absolute
env-vars table Environment variables to inject into the process
scripts table of string lists Script names mapped to argv lists

Use the environment with:

incan env list
incan env show release
incan env run release build

See Project lifecycle reference for merge and resolution rules.

[tool.incan.metadata]

Checked contract metadata settings for project-declared model bundles.

[tool.incan.metadata]
model-bundles = ["contracts/order_summary.json"]

Fields:

Field Type Description
model-bundles list of strings Canonical model bundle JSON files to validate, materialize during build/run, embed into .incnlib artifacts when publishable, and expose through incan tools metadata model.

Bundle paths are resolved relative to the project root unless absolute. See Checked contract metadata for the bundle schema and tooling commands.

[vocab]

Optional companion crate configuration for library-defined DSL metadata.

[vocab]
crate = "vocab_companion"

Use this only for library projects that export vocab entries. Projects without custom library DSLs can omit the section entirely.

Fields

Field Type Description
crate string Path to the vocab companion crate directory, relative to project root unless absolute

During incan build --lib, the compiler:

  1. resolves [vocab].crate
  2. validates that the directory contains Cargo.toml and src/lib.rs
  3. runs cargo build for that companion crate
  4. derives the vocab payload from the companion crate's library_vocab() registration
  5. packages the resulting metadata into the built .incnlib artifact

If the companion crate registers a desugarer via incan_vocab::DesugarerRegistration, incan build --lib also packages the matching Wasm artifact from the companion crate's build output. Any intermediate serialized metadata is a tooling concern (rather than part of the author-facing contract).

[dependencies]

Incan library dependencies available in all contexts. (Note: for Rust crates, see [rust-dependencies]).

[dependencies]
mylib = { path = "../mylib" }

[rust-dependencies]

Rust crate dependencies available in all contexts (build, run, test).

String shorthand

For simple registry dependencies with just a version:

[rust-dependencies]
serde = "1.0"
rand = "0.8"

Table form

For dependencies that need features, sources, or other options:

[rust-dependencies]
tokio = { version = "1.35", features = ["full"] }
serde = { version = "1.0", features = ["derive"], default-features = true }

All fields

Field Type Description
version string Cargo SemVer version requirement (required for registry)
features list Cargo features to enable
default-features bool Whether to include default features (default: true)
optional bool Mark as optional (see below)
package string The actual crate name if renaming (e.g. serde-json)
git string Git repository URL (mutually exclusive with path)
branch string Git branch (requires git)
tag string Git tag (requires git)
rev string Git commit hash (requires git)
path string Local path, relative to incan.toml location

[rust-dev-dependencies]

Dependencies available only in test contexts (tests/ directory). Same syntax as [rust-dependencies].

[rust-dev-dependencies]
criterion = "0.5"
test_helpers = { path = "../test-helpers" }

Importing a dev-only crate from production code is a compile-time error:

error: Rust crate `criterion` is dev-only and cannot be imported from production code
hint: Move the dependency to [rust-dependencies], or import it only from tests.

Overlap rules: If the same crate appears in both [rust-dependencies] and [rust-dev-dependencies], the version, source, and default-features must match. Features are unioned, and the crate is treated as a normal dependency.

[rust-dependencies.optional]

Syntactic sugar for declaring optional (rust) dependencies. Entries here are equivalent to setting optional = true:

[rust-dependencies.optional]
fancy_logging = "0.3"
metrics = { version = "1.0", features = ["prometheus"] }

is equivalent to:

[rust-dependencies]
fancy_logging = { version = "0.3", optional = true }
metrics = { version = "1.0", features = ["prometheus"], optional = true }

Optional dependencies generate a Cargo feature gate. Enable them at build time:

incan build src/main.incn --cargo-features fancy_logging

Legacy alias tables

[rust.dependencies] and [rust.dev-dependencies] remain supported as backward-compatible aliases for [rust-dependencies] and [rust-dev-dependencies]. Prefer the non-nested table names in new manifests.

Dependency sources

Registry (default)

The default source is crates.io. Version is required:

[rust-dependencies]
serde = "1.0"
tokio = { version = "1.35", features = ["full"] }

Git

Specify a git repository URL with exactly one of branch, tag, or rev:

[rust-dependencies]
my_internal_lib = { git = "https://github.com/company/lib.git", tag = "v1.0.0" }
bleeding_edge = { git = "https://github.com/company/lib.git", branch = "main" }
pinned = { git = "https://github.com/company/lib.git", rev = "abc1234" }

Strict mode and git branches

When building with --locked or --frozen, git dependencies using branch = "..." are rejected because branches are not reproducible. Use tag or rev instead.

Path

Local path dependencies, relative to the incan.toml location:

[rust-dependencies]
shared_utils = { path = "../shared-utils" }

Package renames

Use package to use a different crate name than the dependency key. For example:

[rust-dependencies]
json = { package = "serde_json", version = "1.0" }

This lets you import rust::json instead of import rust::serde_json.

Complete example

[project]
name = "my_web_app"
version = "0.1.0"
description = "A web application built with Incan"
authors = ["Alice <alice@example.com>"]

[project.scripts]
main = "src/main.incn"

[build]
rust-edition = "2021"

[vocab]
crate = "vocab_companion"

[dependencies]
mylib = { path = "../mylib/target/lib" }

[rust-dependencies]
tokio = { version = "1.35", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sqlx = { version = "0.7", features = ["runtime-tokio", "postgres"] }

[rust-dependencies.optional]
fancy_logging = "0.3"

[rust-dev-dependencies]
criterion = "0.5"

[tool.incan.envs.default]
env-vars = { INCAN_NO_BANNER = "1" }

[tool.incan.envs.default.scripts]
test = ["incan", "test", "tests/"]

See also