mkit-cli 0.5.0

The mkit command-line tool: a content-addressed VCS with native attestation support
Documentation
[package]
name = "mkit-cli"
description = "The mkit command-line tool: a content-addressed VCS with native attestation support"
keywords = ["mkit", "vcs", "cli", "attestation", "content-addressed"]
categories = ["command-line-utilities", "cryptography"]
readme = "README.md"
# Published to crates.io so `cargo install mkit-cli` works (it installs the
# `mkit` binary). It ALSO ships as a signed binary via the GitHub Release
# archives and `cargo install --git` (see docs/INSTALL.md). NOTE: this crate's
# library surface is the CLI's internals and is NOT a stable API — it is
# deliberately excluded from cargo-semver-checks; depend on the `mkit-*`
# library crates instead, not on `mkit_cli::…`.
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
# The binary is named `mkit` (src/main.rs); set explicitly because it differs
# from the crate name. Install via `cargo install mkit-cli`, the release
# archives, or `cargo install --git … mkit-cli`.
default-run = "mkit"

# `cargo binstall mkit-cli` metadata. Points cargo-binstall at the
# GitHub Release archive layout so users can install the prebuilt
# binary without building from source. The crate is published to
# crates.io, so plain `cargo binstall mkit-cli` resolves this manifest;
# the `--git` form below works too when targeting an unpublished revision:
#
#     cargo binstall --git https://github.com/officialunofficial/mkit mkit-cli
#
# Archive layout pinned by release.yml:
#   mkit-<version>-<target>.tar.gz
#     └── mkit-<version>-<target>/mkit
[package.metadata.binstall]
pkg-url = "{ repo }/releases/download/v{ version }/mkit-{ version }-{ target }.{ archive-format }"
pkg-fmt = "tgz"
bin-dir = "mkit-{ version }-{ target }/{ bin }{ binary-ext }"
# Cosign bundle / SHA256 sidecar files live next to the tarball.
# cargo-binstall will pull the .sha256 automatically via the same URL
# pattern + ".sha256" suffix. Cosign verification is out-of-band.
pub-key = ""

[lints]
workspace = true

[[bin]]
name = "mkit"
path = "src/main.rs"

[dependencies]
# Inter-workspace deps need explicit `version = ...` alongside the
# path so `cargo publish` accepts them; path-only deps are rejected by
# the registry. Track workspace.package.version.
# `batch-verify` (not a default feature — see mkit-core's Cargo.toml)
# is required by `verify_new_object_signatures`'s batch fast path.
mkit-core = { version = "0.5", path = "../mkit-core", features = ["batch-verify"] }
mkit-attest = { version = "0.5", path = "../mkit-attest", features = ["grants"] }
mkit-git-bridge = { version = "0.5", path = "../mkit-git-bridge", optional = true }
mkit-keystore = { version = "0.5", path = "../mkit-keystore" }
# `mkit-transport-memory` is a dev-only (test) dependency — see
# `[dev-dependencies]`. It is intentionally NOT a normal dependency so it
# does not ship in `cargo install mkit-cli`.
mkit-transport-file = { version = "0.5", path = "../mkit-transport-file" }
# `mkit-transport-http`'s bespoke JSON dialect is retired as the ACTIVE
# `mkit+https://`/`mkit+http://` implementation (see `mkit-transport-connect`
# below) as of mkit#701 (SPEC-TRANSPORT-CONNECT verb parity). The dependency
# stays: its `sparse-checkout`/`pack-shards` cargo features are not yet
# covered by `mkit.transport.v1` (SPEC-TRANSPORT-CONNECT §8), and
# `config.rs` still reads its `TOKEN_ENV` constant (identical value to
# `mkit-transport-connect::TOKEN_ENV`, so callers of either transport read
# the same `MKIT_API_TOKEN` env var).
mkit-transport-http = { version = "0.5", path = "../mkit-transport-http" }
# `mkit-transport-connect` is the native ConnectRPC client — the active
# implementation behind `mkit+https://` / loopback `mkit+http://` dispatch
# (mkit#701). Required unconditionally (client dispatch, not
# feature-gated). The CLI hosts no Connect server and has no HTTP
# server stack.
mkit-transport-connect = { version = "0.5", path = "../mkit-transport-connect" }
mkit-transport-s3 = { version = "0.5", path = "../mkit-transport-s3" }
mkit-transport-ssh = { version = "0.5", path = "../mkit-transport-ssh" }
# Optional: encrypted-stream transport. Off by default so the
# default `mkit-cli` build doesn't compile its tcp stack (tokio's `net`,
# governor, rand) or commonware. Turn on via `--features enc-transport`. See SPEC-TRANSPORT-ENC §6.
# Version-locked to the workspace (0.3); now that mkit-transport-enc is
# published, release-plz rewrites this requirement on each lockstep bump.
mkit-transport-enc = { version = "0.5", path = "../mkit-transport-enc", optional = true }
# Optional: the tokio runtime `mkit mcp --http` (the `mcp-v2` feature) runs
# on, with the hyper server. Off by default, so the default `mkit-cli`
# build stays server-free (scripts/check-cli-baseline.sh): tokio is in the
# default graph only as the Connect and reqwest clients' runtime.
tokio = { version = "1", default-features = false, features = [
    "rt-multi-thread",
    "net",
    "time",
    "sync",
    "macros",
], optional = true }
# Optional: `mkit mcp`'s modern (MCP 2026-07-28) local server, behind the
# `mcp-v2` feature. `default-features = false`: no reqwest/client-side bits —
# `transport-io` is the default stdio server, `transport-streamable-http-server`
# is `mkit mcp --http <addr>` (SEP-2567 dropped sessions for 2026-07-28
# traffic; `legacy_session_mode` still serves pre-2026-07-28 clients over
# reconnect-friendly SSE, same dual-era design as apps/mcp's TS server). Off
# by default so the default `mkit-cli` build compiles no MCP HTTP server,
# mirroring `enc-transport`. See `commands/mcp_v2.rs`.
rmcp = { version = "3", default-features = false, features = [
    "server",
    "transport-io",
    "transport-streamable-http-server",
], optional = true }
# `--http`'s server, matching rmcp's own documented hyper/hyper-util idiom
# (examples/servers/src/counter_hyper_streamable_http.rs) rather than pulling
# in axum: `StreamableHttpService` implements `tower::Service` directly, so
# raw hyper is all the glue this needs.
hyper = { version = "1", features = ["server", "http1"], optional = true }
hyper-util = { version = "0.1", features = ["tokio", "server-auto", "service"], optional = true }
# Constant-time bearer-token comparison for `mkit mcp --http-token` —
# already resolved transitively (ed25519-dalek pulls it), so this is not a
# new supply-chain root.
subtle = { version = "2", optional = true }
# HeaderMap/header names for `mkit mcp --http`'s bearer-auth wrapper — already resolved
# transitively (reqwest pulls it), not a new supply-chain root.
http = { version = "1", optional = true }
# `tower_service::Service` — the trait `mkit mcp --http`'s bearer-auth
# wrapper (`mcp_v2.rs`'s `BearerAuthHttp`) implements around
# `StreamableHttpService` before handing it to `TowerToHyperService`.
# Already resolved transitively (rmcp/hyper-util pull it), not a new
# supply-chain root.
tower-service = { version = "0.3", optional = true }
# `BoxBody`/`Bytes` — used to construct the 401 response `BearerAuthHttp`
# returns without calling into `StreamableHttpService`, matching its
# `Response = http::Response<BoxBody<Bytes, Infallible>>` type exactly.
# Already resolved transitively (rmcp pulls both), not a new supply-chain
# root.
http-body-util = { version = "0.1", optional = true }
bytes = { version = "1", optional = true }
mkit-rpc = { version = "0.5", path = "../mkit-rpc" }
# `mkit serve`'s engine: the ssh-frame session (`ssh`) over the pipeline
# with the `.mkit` layout stores (`fs`). `default-features = false` keeps
# out `connect` (the Connect binding: connectrpc, http) and every other
# feature: the CLI serves no Connect, SQL or test seam, and
# scripts/check-release-artifact-features.sh allows only these two.
# Nothing in it builds an async runtime (tokio's `sync` only, for a lock
# `mkit serve` never takes). First published at the 0.5.0 release.
mkit-server = { version = "0.5", path = "../mkit-server", default-features = false, features = ["ssh", "fs"] }
# `mkit serve` drives the session future with `futures::executor::block_on`
# on the main thread: no async runtime. Already in the graph (via
# mkit-transport-connect and reqwest), so not a new supply-chain root.
futures = { version = "0.3", default-features = false, features = ["executor", "std"] }
# commonware-cryptography's ed25519 PrivateKey is the static-key type
# the encrypted transport handshake takes. Pulled in here behind the
# `enc-transport` feature so the keystore-driven key-loading path in
# remote_dispatch can build a key from raw bytes without touching
# `mkit-transport-enc`'s internals.
commonware-cryptography = { workspace = true, features = ["std"], optional = true }
buffa = "0.9.1"
# `mkit add -A`/`add .` hashes untracked/modified worktree files in
# parallel (commands/add.rs) — each file's read+BLAKE3 pass is
# independent and `WriteBatch` (mkit-core's batch.rs) was already built
# to accept concurrent writers (short-locked staged-dedup check, file
# I/O outside the lock). Native-only: mkit-core stays wasm-clean, so
# this lives in the CLI crate rather than mkit-core.
rayon = "1"

# `wrap_help` is intentionally disabled so help-text output stays
# deterministic across terminal widths. `derive` is enabled so the
# per-command strangler migration (see clap_shim.rs) can use
# `#[derive(Parser)]`; the top-level `HELP_TEXT` constant remains
# the source of truth for `mkit --help` and is pinned by snapshot
# tests in cli.rs + tests/help_snapshot.rs.
clap = { version = "4", default-features = false, features = ["std", "help", "error-context", "usage", "derive", "string"] }
tempfile = "3"
thiserror = "2"
# JSON-RPC parse/emit for the `mkit mcp` stdio server. Zero new supply
# chain: serde_json is already in this binary's normal dependency graph
# via mkit-attest (JCS tests) and the http/s3 transports (reqwest).
serde_json = "1"
serde = { version = "1", features = ["derive"] }
# `mkit self update` (commands/self_update.rs): release download over
# rustls (same feature set + version as mkit-transport-http — zero new
# TLS supply chain), sha256 sidecar check, and single-member tar.gz
# extraction of the new binary. Cert verification is via
# rustls-platform-verifier (OS trust store), reqwest 0.13's own default.
# Not the same trust model as mkit-transport-connect's hyper-rustls
# client, which pins an explicit webpki-roots list instead (see that
# crate's Cargo.toml and SPEC-TRANSPORT-CONNECT.md §7.3) — the two
# transports are independent and don't need to agree.
reqwest = { version = "0.13", default-features = false, features = ["rustls", "blocking", "json"] }
sha2 = "0.11"
# `mkit grant`/`epoch`/`visibility` (grants/): base64url and hex for imported
# wallet and WebAuthn signatures, and secp256k1 point decompression to derive
# the 0x owner address of a keystore key. All three are already in the graph
# (mkit-attest, mkit-keystore), so no new supply-chain root.
base64 = "0.23"
hex = "0.4"
k256 = { version = "0.14", default-features = false, features = ["ecdsa", "std"] }
flate2 = "1"
tar = "0.4"
# Used by `mkit keygen` to generate raw 32-byte secrets for non-ed25519
# algorithms (secp256k1 / p256). Same version mkit-core already pulls in.
# `sys_rng` provides `getrandom::SysRng`, the rand_core 0.10 `TryRng`
# source the bls-threshold trusted-dealer path wraps in `UnwrapErr`.
getrandom = { version = "0.4", features = ["sys_rng"] }
# Wraps the raw 32-byte secrets keygen pulls out of the RNG until they
# land inside the algorithm signer (which has its own scrubbing).
zeroize = "1"
# Used by `mkit key generate --algorithm bls12381-thr` to feed the
# trusted-dealer ceremony with `UnwrapErr(getrandom::SysRng)`. Gated on
# `bls-threshold` so the default build doesn't pull rand_core. 0.10 to
# match the rand_core edge commonware-cryptography 2026.9.0 depends on.
rand_core = { version = "0.10", optional = true }
# Wire-encodes `Share` values for storage in the keystore — same
# version mkit-attest pulls. Tightened to `=` to match every other
# commonware-* pin in the workspace (previously the one outlier).
commonware-codec = { version = "=2026.9.0", optional = true }

# POSIX uid + O_NOFOLLOW for keygen.rs's raw-32 load/save paths
# (secp256k1, p256). The Ed25519 path uses mkit_core::sign which has
# its own libc dependency; the cli's non-Ed25519 paths reach through
# the same primitives, so we add libc here too.
#
# signal-hook installs SIGINT/SIGTERM handlers via sigaction(2) for
# cooperative shutdown. We use only the `flag` module (atomic-bool
# stores; async-signal-safe) so this crate stays
# `#![deny(unsafe_code)]` — see signal.rs.
[target.'cfg(unix)'.dependencies]
libc = "0.2"
signal-hook = { version = "0.4", default-features = false }

[target.'cfg(target_os = "macos")'.dependencies]
mkit-keystore = { version = "0.5", path = "../mkit-keystore", features = ["backend-macos-keychain"] }

[target.'cfg(target_os = "linux")'.dependencies]
mkit-keystore = { version = "0.5", path = "../mkit-keystore", features = ["backend-linux-secret-service", "backend-systemd-creds"] }

[build-dependencies]
# build.rs enforces CLI_VERSION == CARGO_PKG_VERSION at compile time by
# reading src/cli.rs. No external deps — plain file I/O + string match.

[dev-dependencies]
tempfile = "3"
mkit-test-util = { path = "../mkit-test-util" }
mkit-transport-memory = { path = "../mkit-transport-memory" }
# Integration tests for HTTP/S3 scheme dispatch use a local mockito
# server. SSH dispatch tests only cover URL parsing (no live subprocess
# in CI), so the `mkit-transport-ssh` exports are enough on their own.
mockito = "1"
# `attest_roundtrip` tests derive per-algorithm public keys at test time
# to build trust-roots TOML files. Dev-only: the CLI binary itself never
# imports these directly — signing goes through the mkit-attest signers.
ed25519-dalek = { version = "3", default-features = false, features = ["alloc"] }
k256 = { version = "0.14", default-features = false, features = ["ecdsa", "std", "pkcs8"] }
p256 = { version = "0.14", default-features = false, features = ["ecdsa", "std", "pkcs8"] }
# Snapshot testing for CLI golden-output fixtures. See issue #164.
# `filters` feature enables regex-based redaction so version-string
# bumps don't churn snapshots.
insta = { version = "1", features = ["filters"] }
# Stateful end-to-end invariant suite (tests/state_machine.rs): proptest
# generates random op sequences driven through the real `mkit` binary,
# asserting a repo-invariant battery after each op. Same major as
# mkit-core's dev-dep. Bounded by default (32 cases); the nightly job
# raises PROPTEST_CASES under the `state-machine` nextest profile.
proptest = "1"
# tests/remote_dispatch_connect.rs (mkit#701): drives push_all/pull_all
# against a REAL in-process mkit.transport.v1.TransportService server
# (connectrpc's hyper server, memory-backed) rather than a mockito stub —
# the regression gate proving `mkit+http://` round-trips through the
# generated Connect codebase end-to-end. `server` is additive on top of
# `mkit-transport-connect`'s own `client`/`client-tls` feature set (Cargo
# unions dependency features across dependency kinds).
connectrpc = { version = "0.9.0", default-features = false, features = ["server", "gzip", "zstd", "json"] }
# tests/grant_e2e.rs (WP-2.13/2.14, E1): the real `mkit` binary against a REAL
# in-process mkit-server pipeline over the memory stores, so grant selection,
# `epoch bump` and revocation are exercised against the actual verifier. A
# DEV-dependency only: `scripts/check-cli-baseline.sh` audits the normal
# graph, where mkit-server stays at `ssh` + `fs`.
mkit-server = { version = "0.5", path = "../mkit-server", default-features = false, features = ["ssh", "fs", "connect", "memory"] }
http = "1"
tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "net", "time", "sync"] }
futures = "0.3"
# tests/cli_transcripts.rs: declarative `.trycmd` transcript tests for
# simple, deterministic CLI output (error messages, not full help text —
# that's already covered by help_snapshot.rs's insta snapshots). Pins
# exact user-facing text, not just exit codes.
trycmd = "1"
# tests/blackbox_smoke.rs: assert_cmd's fluent Command::assert() API,
# demonstrated on one existing hand-rolled Output-based test as a pattern
# reference for new tests — the existing tests/common/mod.rs `Repo`
# builder (sandboxed temp-dir + fixed signing key + `.ok()`/`.run()`)
# already covers the cargo-test-support-style sandboxed-repo role, so
# this is additive ergonomics on top of it, not a replacement.
assert_cmd = "2"
predicates = "3"
assert_fs = "1"
# tests/ssh_e2e_real.rs's containerized real-sshd test: a genuine
# OpenSSH daemon (lscr.io/linuxserver/openssh-server) via Docker, closing
# the gap the module docs' host-level skeleton left open ("would flake
# across runners" — the container sidesteps exactly that fragility, since
# the daemon's provisioning lives in a pinned, pre-built image instead of
# on the host). `blocking` feature: this suite's tests are plain `#[test]`,
# not async.
testcontainers = { version = "0.27", features = ["blocking"] }

[features]
default = []
# Compile-time exhaustiveness mirror of mkit-attest's bls-threshold —
# pulls the BLS Algorithm variant into scope so the CLI's match arms
# cover it (returning UNAVAILABLE per-site). The release-party signer
# (issue #160) will later replace the inert arms.
bls-threshold = ["mkit-attest/bls-threshold", "mkit-keystore/bls-threshold", "dep:rand_core", "dep:commonware-codec"]
# Enables the `mkit+enc://` client dispatch path in
# `remote_dispatch::open`. Pulls in `mkit-transport-enc` with its `tcp`
# feature (tokio + governor + rand) for `connect_tcp`. The transport is deprecated:
# no maintained server hosts it. See SPEC-TRANSPORT-ENC §6.
enc-transport = ["dep:mkit-transport-enc", "mkit-transport-enc/tcp", "dep:commonware-cryptography", "dep:commonware-codec"]
# Enables `mkit mcp`'s modern serving path (MCP 2026-07-28, via the official
# `rmcp` Rust SDK — see the doc comment above rmcp's Cargo.toml entry). Off by
# default: rmcp brings a hyper server and a tokio runtime, and the default
# build is server-free (scripts/check-cli-baseline.sh; see commands/mcp.rs's
# module doc); the pre-existing hand-rolled stdio
# server stays the default `mkit mcp` implementation either way. Turn on via
# `--features mcp-v2`.
mcp-v2 = ["dep:rmcp", "dep:tokio", "dep:hyper", "dep:hyper-util", "dep:subtle", "dep:http", "dep:tower-service", "dep:http-body-util", "dep:bytes", "tokio/net", "tokio/signal"]
# The git bridge (`mkit git ...`, SPEC-GIT-BRIDGE / SPEC-GIT-IMPORT).
# Default-off while experimental; flip after the mapping has survived
# real mirrors. `git-export` is a back-compat alias from the bridge's
# export-only era.
git-bridge = ["dep:mkit-git-bridge"]
git-export = ["git-bridge"]
# Verifiable sparse-checkout (issue #158). Pulls the
# `mkit-core` sparse module and the matching transport-side fetchers
# into scope. Off by default — the upstream `commonware-storage` is
# ALPHA-tier.
sparse-checkout = [
    "mkit-core/sparse-checkout",
    "mkit-transport-http/sparse-checkout",
    "mkit-transport-s3/sparse-checkout",
]
# Issue #159 — `mkit pack-shard <hash>` producer subcommand
# and shard-aware HTTP / S3 downloads. Default-off because the
# commonware dep stack is large.
pack-shards = [
    "mkit-core/pack-shards",
    "mkit-transport-http/pack-shards",
    "mkit-transport-s3/pack-shards",
]
# Canonical first-parent ancestry snapshots and contextual proofs. Opt in for
# commit/branch/merge/rebase/cherry-pick publication and verified reflog output.
history-mmr = ["mkit-core/history-mmr"]

[package.metadata.docs.rs]
# Build docs with every feature so docs.rs renders the feature-gated API.
all-features = true