Skip to main content

Installation

Install a binary

curl -fsSL https://www.modality.org/install.sh | sh

That installs the modal CLI. Then create your first contract. Build from source when you need a development binary or the lean onboarding wrapper.

Prerequisites (from source)

  • Git
  • Rust toolchain (for building from source)

The repository pins the Rust compiler in rust/rust-toolchain.toml. Before measuring source-build onboarding, confirm Cargo is using that pinned toolchain and that the locked dependency graph resolves with it:

cd modality/rust
rustup show active-toolchain
cargo metadata --locked --no-deps --format-version 1

Install from Source

Build the lean onboarding wrapper first if your goal is the first-contract local flow:

# Clone the repo
git clone https://github.com/modality-org/modality.git
cd modality/rust

# Build the first-contract onboarding CLI
cargo build --release -p modal --no-default-features --features contract-onboarding

# Add to path
export PATH="$PATH:$(pwd)/target/release"

# Verify installation
modal --help

The lean wrapper includes modal contract and modal id commands without the network, hub, node, predicate, or program surfaces.

Build the full wrapper when you need those broader command groups:

# Clone the repo
git clone https://github.com/modality-org/modality.git
cd modality/rust

# Build the full CLI
cargo build --release -p modal

# Add to path
export PATH="$PATH:$(pwd)/target/release"

# Verify installation
modal --version

Development Builds

The Modal CLI is split into domain crates for faster incremental builds. When working on a specific area, build only that crate:

cd modality/rust

# Hub server work
cargo build -p modality-cli-hub

# Contract commands (lean default: no libp2p, wasmtime, or modality-lang)
cargo build -p modality-cli-contract

# Contract commands with all optional deps (P2P push/pull, model status, WASM upload)
cargo build -p modality-cli-contract --features full

# Program commands with WASM validation
cargo build -p modality-cli-program --features full

# Network info only (very lean: modality-networks + clap)
cargo build -p modality-cli-net

# Node management
cargo build -p modality-cli-node

# Predicate, program, chain, or network commands
cargo build -p modality-cli-predicate
cargo build -p modality-cli-program
cargo build -p modality-cli-chain
cargo build -p modality-cli-net

# Lean first-contract wrapper
cargo build -p modal --no-default-features --features contract-onboarding

# Full CLI (for integration testing)
cargo build -p modal

Verify Installation

modal --help
modality model --help

Use modal for contract logs, identities, commits, status, and the first-contract local flow. Use modality for model and rule authoring tasks such as modality model synthesize, modality model validate, modality model lint, modality model mermaid, and modality model view. A successful onboarding install should make both command surfaces visible before you start the first-contract guide.

Local source builds and temporary Cargo-root installs are verified by the onboarding smokes. Git URL installs are measured by tests/cli/check-modal-git-install-readiness.sh, which installs modal into a temporary Cargo root, checks the installed help surface, and runs the first-contract CLI smoke when a built modality binary is supplied. Set MODAL_ONBOARDING_GIT_REV=<commit> to pin the exact Git revision under test for release checklists or CI evidence. Release-archive-shaped binary bundles are measured by tests/cli/check-modal-release-archive-readiness.sh, which creates a modal-<version>-<os>-<arch>-<profile>.tar.gz containing bin/, bin/modal, and README.txt plus PROVENANCE.txt, EVIDENCE-BUNDLE.txt, and SHA256SUMS, unpacks it, verifies the checksum manifest, checks that the archive contains exactly those six entries in the emitted order, and checks that the manifest covers exactly bin/modal, README.txt, and PROVENANCE.txt plus EVIDENCE-BUNDLE.txt. The provenance file records the source revision, version, profile, features, platform, and expected help surface. The producer self-check compares the archive member list in emitted order before release evidence can pass. The archive producer requires the packaged MODAL_BIN to be a regular non-symlink executable. The archive producer now fails before emitting release evidence when the source revision is not a lowercase hex commit token, so unknown, too-short, uppercase, non-hex, overlong, or other hand-written revision notes cannot become the advertised archive provenance. Packaged modal --version revision markers follow the same boundary: too-short, uppercase, non-hex, and overlong tokens fail before wrapper version metadata can become release evidence. The evidence manifest names the replayable evidence bundle, artifact, version, source revision, profile, feature set, exact help surface, binary, provenance file, checksum file, and post-unpack checks, including the same-revision language CLI check when artifact smoke replay is enabled. With MODAL_ONBOARDING_ARTIFACT_SMOKE=1, the download verifier now requires a same-revision MODALITY_BIN, checks the unpacked help surface, and runs the first-contract CLI smoke against the unpacked modal binary. The language CLI revision may be the exact provenance revision or a longer matching hex prefix for the same commit; expected revisions must be full commit hashes or Git-style short hashes of at least seven lowercase hexadecimal characters. The producer also rejects too-short, uppercase, non-hex, or overlong explicit source revision overrides before archive evidence can pass. The producer and downloaded-artifact verifier both reject unknown, too-short, uppercase, non-hex, or overlong expected revision tokens before replay evidence can pass. Set MODAL_ONBOARDING_ARCHIVE_EXPECT_REV=<commit> when release evidence must fail if the built modal binary is stale or came from a different source revision. The .github/workflows/onboarding-release-archive.yml workflow wires this into GitHub Actions without publishing anything: it builds modality, builds the lean release modal wrapper with contract-onboarding, runs the archive readiness check with the expected source revision, then uploads the verified replayable archive bundle, detached tarball checksum, and VERIFY-DOWNLOAD.txt verification recipe as a workflow artifact. The recipe names, in order, the artifact, exact downloaded directory entries, expected source revision, expected profile, expected feature set, expected help surface, and verifier command. The workflow summary prints the exact gh run download command for that run and the matching MODAL_ONBOARDING_ARTIFACT_EXPECT_REV=<commit> replay verifier command, plus the optional MODAL_ONBOARDING_ARTIFACT_SMOKE=1 replay command that checks the downloaded binary's version, recorded help surface, same-revision language CLI, and first-contract path when MODALITY_BIN=/path/to/modality was built from the same source revision. The uploaded Actions artifact and the replay summary both use the modal-linux-x86_64-release-archive-<source-revision> artifact name, so the download command stays tied to the exact source revision under test. The summary also includes a local replay block that creates an artifact_dir, downloads the named artifact there, runs the pinned verifier against that directory, and then shows the optional smoke replay against the same directory. The same summary records the exact Actions artifact name, the GitHub artifact digest emitted by the upload-artifact artifact-digest output, and fails if that digest is missing. It also records the exact detached tarball checksum line that the local replay verifies, so a manual release-candidate run keeps the platform archive identity visible next to the Modality archive identity and replay commands. Run workflow_dispatch manually with this guarded handoff so an empty or wrong-revision run lookup stops before any artifact is trusted:

git fetch origin main
test "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)"
source_rev="$(git rev-parse HEAD)"
gh workflow run "Onboarding Release Archive" --ref main
for attempt in 1 2 3 4 5; do
run_id="$(gh run list --workflow "Onboarding Release Archive" --branch main --event workflow_dispatch --commit "$source_rev" --limit 1 --json databaseId,headSha --jq '.[0] | select(.headSha == "'"$source_rev"'") | .databaseId')"
test -n "$run_id" && break
sleep 3
done
test -n "$run_id"
gh run watch "$run_id" --exit-status

Tag a commit as modal-v* when you want tag-scoped release evidence. The workflow summary is the release-candidate handoff surface: it names the source revision, exact Actions artifact, GitHub artifact digest, detached tarball checksum, exact download command, pinned verifier command, and optional smoke replay command for that run. For checkpoint review, treat the minimum evidence bundle as complete only when the handoff includes the exact source revision, matching workflow run id, exact Actions artifact name, GitHub artifact digest, detached tarball checksum line, downloaded-artifact verifier result, and, when first-contract replay is claimed, the same-revision MODALITY_BIN smoke replay result. After downloading that artifact, verify it before unpacking or trusting the binary:

tests/cli/check-modal-release-artifact-download.sh /path/to/downloaded-artifact-dir

The download check expects exactly one modal-*.tar.gz plus its matching .sha256 sidecar plus exactly one VERIFY-DOWNLOAD.txt recipe, rejects any other top-level entries in the downloaded artifact directory, requires those entries to be regular non-symlink files with canonical 0644 modes, verifies the detached checksum first, requires the detached checksum sidecar to be one canonical SHA-256 line naming exactly that one archive, then rechecks the exact archive members in the emitted order, internal checksum manifest entries in the emitted order, executable bin/modal, regular non-symlink unpacked files with expected payload modes (bin/ and bin/modal as 0755; text and checksum files as 0644) plus a regular non-symlink bin/ directory with mode 0755, provenance metadata, source revision, single provenance marker, replayable evidence bundle marker, and the recipe's archive, checksum, revision, and verifier command. The producer smoke also mutates the downloaded archive, checksum sidecar, and recipe into directories, symlinks, or non-canonical modes, and renames the checksum sidecar without renaming the archive, so top-level payload checks and sidecar pairing checks stay covered by executable negative evidence. It also proves that a symlinked unpacked bin/ directory is rejected before installer evidence can pass. The archive filename must match the version, OS, architecture, and profile recorded in provenance, so a consistently renamed tarball, sidecar, and recipe still fails before the binary is trusted. The provenance file must keep exactly one value for version, profile, features, help surface, OS, and architecture. OS and architecture values must stay archive-safe lowercase platform tokens. The profile must be one of the supported build profiles (debug or release), and the feature set must be one of the supported wrapper feature sets (contract-onboarding or full), so stale, partial, or unsupported provenance fails before the binary is trusted. The source revision must also be a lowercase hex commit token, so unknown or hand-edited revision notes fail even when the checksums and replay recipe are rebuilt consistently around them. The archive producer and downloaded-artifact verifier both now prove that OS and architecture provenance fail when either value is not an archive-safe lowercase platform token, with producer and downloaded-artifact negative cases for unsafe OS and architecture provenance, so unsafe platform metadata cannot be advertised and then left for the other side of the handoff to catch later. The archive producer also fails before emitting release evidence when the build profile is not one of the supported values (debug or release), so ad-hoc profile labels cannot become installer provenance even when MODAL_BIN points at an explicit binary. The archive producer also fails before emitting release evidence when the advertised help surface or wrapper feature set is not one of the supported values, so experimental labels cannot be published as replayable installer provenance. It also requires the advertised help surface to match the wrapper feature set: contract-onboarding archives must advertise the lean help surface, and full archives must advertise the full help surface. The producer-side archive smoke proves both mismatch directions before release evidence is emitted. The producer-side archive smoke also proves that a regular but non-executable MODAL_BIN fails before any version metadata can be copied into release evidence, matching the symlinked-binary guard for the packaged wrapper. It also requires the packaged modal --version output to identify the modal wrapper and emit exactly one line, including no trailing blank version lines, with at most one embedded revision marker, and that marker must use the supported parenthesized (...@<commit>) form, with no other parenthesized version notes, and with the marker as the final version metadata, before that value is copied into the archive name, README, provenance, evidence manifest, and verification recipe. When present, the embedded marker must be a full commit hash or Git-style short hash of at least seven lowercase hexadecimal characters that matches the selected source revision; the producer-side smoke also proves uppercase revision markers fail before release evidence is emitted, and those uppercase revision markers are rejected as non-canonical tokens. It now proves non-hex revision markers fail there too, so an otherwise valid bundle cannot carry extra hand-written version notes, trailing version text, or stale version provenance as installer metadata. The producer derives that selected source revision from the source checkout before falling back to embedded version metadata, so a stale modal --version marker cannot become self-consistent archive provenance just because no expected revision was supplied. The provenance marker must also appear exactly once, so hand-merged provenance preambles fail before any field values are trusted. If the provenance version string carries an embedded revision marker, that marker must also be the only parenthesized version metadata and must be a full commit hash or Git-style short hash of at least seven lowercase hexadecimal characters that matches the single source revision recorded by provenance, and it must be the final version metadata. The downloaded-artifact verifier has negative evidence for uppercase revision markers in this provenance version field plus too-short, non-hex, and overlong revision markers, so malformed, annotated, trailing, or stale version metadata still fails before the binary is trusted. In other words, malformed, annotated, trailing, or stale version metadata still cannot anchor downloaded-artifact evidence. The help surface recorded in provenance must also be one of the supported surfaces (lean or full), and optional smoke replay checks that the unpacked binary reports exactly one version line matching provenance before checking that exact help surface instead of assuming a default. A consistently edited README, provenance file, and recipe that invent a new surface still fails before the binary is trusted. The README artifact marker must appear exactly once, and the README must repeat the same version, source revision, profile, feature set, and help surface as provenance, so stale human-facing bundle notes fail before the binary is trusted, including stale version notes. Those README metadata fields must also be single-valued, so hand-edited notes with both current and stale values fail before the binary is trusted. The evidence bundle marker must appear exactly once. The evidence bundle must also keep naming the same archive artifact, version, profile, feature set, and exact help surface as provenance plus the checked binary, provenance file, checksum manifest, and post-unpack smoke checks, so stale or hand-edited bundles cannot omit or drift from the replay ingredients while preserving checksums. Those evidence manifest fields must also be single-valued, so hand-merged manifests with both current and stale replay ingredients fail before the binary is trusted. The recipe's artifact section must name exactly the one downloaded archive, so missing artifact metadata fails before unpacking; stale or hand-edited extra artifact names fail before unpacking. The recipe title must also appear exactly once as the first line, so stale notes inserted before it or hand-merged recipe preambles fail before the binary is trusted. The provenance file must name exactly one source revision, so ambiguous or hand-merged provenance fails before the binary is trusted. It also checks that the recipe names exactly the expected downloaded directory entries in the emitted order: the archive, its .sha256 sidecar, and VERIFY-DOWNLOAD.txt. The recipe's expected source revision section must name exactly the same single revision as the unpacked provenance, so missing revision metadata or stale duplicate revisions fail before the binary is trusted. The recipe's expected profile and feature-set sections must also name exactly the same single values as the unpacked provenance. The recipe's expected help surface section must name exactly the same single help surface as the unpacked provenance, so a stale lean-versus-full replay recipe fails before the binary is trusted. Those recipe sections must stay in the emitted order, so a hand-shuffled recipe fails even when each section still carries the expected value. Set MODAL_ONBOARDING_ARTIFACT_EXPECT_REV=<commit> when a downloaded artifact must fail unless its internal provenance matches one exact source revision. Set MODAL_ONBOARDING_ARTIFACT_SMOKE=1 only when a same-revision regular non-symlink MODALITY_BIN is available; leave it unset for archive-only verification. The verifier now rejects missing MODALITY_BIN rather than silently downgrading the requested first-contract smoke to archive-only verification. The producer smoke now treats an explicitly set but non-executable MODALITY_BIN as an error instead of archive-only evidence, and proves that a non-executable or symlinked MODALITY_BIN is rejected before any replay can pass. Smoke replay also requires the downloaded bin/modal --version output to be one line that matches provenance before help-surface or first-contract replay is trusted, and requires that MODALITY_BIN --version return successfully with one line that identifies the language CLI with a modality prefix and exactly one embedded source revision marker in the supported parenthesized (...@<commit>) form with no other parenthesized version notes and with the marker as the final version metadata, so a helper that omits the revision, only prints a matching source revision marker, exits after printing a plausible version, advertises a too-short, uppercase, non-hex, or overlong revision marker, or appends extra revision notes cannot anchor first-contract replay evidence. When a regular non-symlink executable MODALITY_BIN is supplied to the producer smoke, it also passes the generated artifact directory back through the downloaded-artifact verifier with MODAL_ONBOARDING_ARTIFACT_SMOKE=1, so the consumer replay path is positively checked before producer-side first-contract replay is reported. The producer-side archive smoke and the downloaded-artifact verifier both accept exact or matching-prefix hex revision markers for the same commit before claiming same-revision first-contract replay, and both reject expected or same-revision language CLI revision tokens shorter than seven hexadecimal characters or longer than forty. They also reject too-short, uppercase, non-hex, or overlong explicit producer source revisions and unknown, too-short, uppercase, non-hex, or overlong expected revision tokens before producer or downloaded-artifact replay evidence can pass. The producer-side archive smoke now also independently enforces the same single-line modality prefix and supported-marker shape with no other parenthesized version notes, duplicate revision markers, bare @... markers, or trailing marker text before its local first-contract replay, so producer evidence does not rely only on the consumer verifier call for language-CLI identity. The producer-side archive smoke uses the same exact-or-prefix revision match as the downloaded-artifact verifier, including rejection of too-short, uppercase, non-hex, and overlong same-revision language CLI markers before local replay. It now proves the overlong language-CLI marker directly on the producer path, before local first-contract replay evidence can be reported. Unsupported smoke flag values now fail, too, including an explicit 0, instead of silently downgrading to archive-only verification, so a mistyped replay request cannot look like a successful archive-only check. The uploaded VERIFY-DOWNLOAD.txt repeats the expected source revision and the exact two-command verification section, plus the optional MODAL_ONBOARDING_ARTIFACT_SMOKE=1 and MODALITY_BIN=/path/to/modality same-revision replay environment, so the artifact directory remains self-describing after download. The detached checksum command, exact verifier command, smoke replay environment, and smoke replay description must each appear exactly once, and the verification section must stay present with no extra command added, so hand-merged recipes with missing, stale duplicate, or extra replay commands fail before the binary is trusted. The smoke replay note must remain the exact final three-line trailer, so a stale inserted line cannot split the optional smoke instructions while preserving both required strings. After those targeted checks pass, the full recipe must still match the canonical emitted text exactly, so hand-inserted prose between otherwise valid sections fails before the binary is trusted. External crates.io-style packaging is tracked separately: tests/cli/check-modal-package-readiness.sh reports the current blocker until the workspace CLI crates that modal depends on are available from the registry. Its blocker output names the selected direct workspace dependencies (modality-cli-contract, modality-common, and modality) and the selected package closure (modality-cli-common, modality-cli-contract, modality-common, modality, and modality-lang) that must be covered by an external package or installer plan.

For the language CLI, modality --help should only expose the model command group, and modality model --help should expose the parser and review tools:

model      Model related commands
check Check a formula against a model
synthesize Synthesize a model from a template
validate Validate a contract model
lint Lint governance formulas

For the lean onboarding wrapper, modal --help should show the first-contract command surface, including:

contract   Contract related commands
id ID and key related commands
passfile Passfile related commands
ai AI provider configuration
status Show status
commit Commit changes
set Set a state file value

You should not see the full runtime command groups such as hub, node, predicate, program, or chain unless you built the full wrapper.

For the full wrapper, modal --help should also include broader runtime commands such as:

modal - Modality CLI

USAGE:
modal <COMMAND>

COMMANDS:
contract Contract management
id Identity management
predicate Predicate operations
node Network node operations
hub Contract hub operations
help Print help