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