mkit-cli 0.4.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.
mkit-core = { version = "0.4", path = "../mkit-core" }
mkit-attest = { version = "0.4", path = "../mkit-attest" }
mkit-git-bridge = { version = "0.4", path = "../mkit-git-bridge", optional = true }
mkit-keystore = { version = "0.4", 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.4", 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.4", path = "../mkit-transport-http" }
# `mkit-transport-connect` provides BOTH the native ConnectRPC client —
# the active implementation behind `mkit+https://` / loopback
# `mkit+http://` dispatch (mkit#701) — and the axum-hosted
# mkit.transport.v1 Connect server `mkit serve --http` uses (issue #700).
# Required unconditionally (client dispatch, not feature-gated); its own
# `server`/`axum` Connect features are also unconditional (see
# `rust/crates/mkit-transport-connect/README.md`), so no `http-transport`
# feature gate is needed on this dependency itself.
mkit-transport-connect = { version = "0.4", path = "../mkit-transport-connect" }
mkit-transport-s3 = { version = "0.4", path = "../mkit-transport-s3" }
mkit-transport-ssh = { version = "0.4", path = "../mkit-transport-ssh" }
# Optional: encrypted-stream transport. Off by default so the
# baseline `mkit-cli` build doesn't pay tokio's compile cost. 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.4", path = "../mkit-transport-enc", optional = true }
# Optional: `mkit serve --http`'s axum-hosted mkit.transport.v1 Connect
# server (issue #700, SPEC-TRANSPORT-CONNECT). Off by default so the
# baseline `mkit-cli` build doesn't pay axum/hyper's compile cost, mirroring
# `enc-transport`. Turn on via `--features http-transport`.
# ConnectRPC + axum are pulled in directly (not just transitively through
# mkit-transport-connect, which already unconditionally depends on both)
# so `commands/serve/http.rs` can build the bearer-auth `Interceptor` and
# drive `axum::serve` itself. Feature set mirrors `mkit-transport-connect`'s
# own server-side (server + axum), version-pinned to the same "0.8" the
# rest of the workspace's connectrpc consumers use.
connectrpc = { version = "0.8.1", default-features = false, features = [
    "server",
    "axum",
    "gzip",
    "json",
], optional = true }
axum = { version = "0.8", default-features = false, features = [
    "http1",
    "http2",
    "tokio",
], optional = true }
tokio = { version = "1", default-features = false, features = [
    "rt-multi-thread",
    "net",
    "time",
    "sync",
    "macros",
], optional = true }
# Constant-time bearer-token comparison for the `--http-token` auth gate —
# 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 the bearer-auth interceptor — already resolved
# transitively (reqwest pulls it), not a new supply-chain root.
http = { version = "1", optional = true }
mkit-rpc = { version = "0.4", path = "../mkit-rpc" }
# 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.8.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"
# `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.
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "blocking", "json"] }
sha2 = "0.10"
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.7.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.7.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.4", path = "../mkit-keystore", features = ["backend-macos-keychain"] }

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

[target.'cfg(windows)'.dependencies]
mkit-keystore = { version = "0.4", path = "../mkit-keystore", features = ["backend-windows-credential"] }

[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 = "2", default-features = false, features = ["std"] }
k256 = { version = "0.13", default-features = false, features = ["ecdsa", "std", "pkcs8"] }
p256 = { version = "0.13", 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.8.1", default-features = false, features = ["server", "gzip", "zstd", "streaming", "json"] }
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://` dispatch path in `remote_dispatch::open`
# and the `mkit serve --listen-enc <addr>` listener flag. Pulls in
# `mkit-transport-enc` with its `tcp` feature (tokio + governor +
# rand). See SPEC-TRANSPORT-ENC §6 item 3.
enc-transport = ["dep:mkit-transport-enc", "mkit-transport-enc/tcp", "dep:commonware-cryptography", "dep:commonware-codec"]
# Enables `mkit serve --http <addr>` (issue #700): hosts
# `mkit.transport.v1.TransportService` over axum, backed by `FileTransport`.
# `mkit-transport-connect` itself is an unconditional dependency (it also
# backs `mkit+https://` client dispatch, mkit#701) but its OWN server-side
# axum/hyper-server bits are behind ITS `server` cargo feature (off by
# default there too), so this feature both flips that on and pulls in the
# CLI's own directly-used `connectrpc`'s server+axum features, `axum`, and
# a multi-thread `tokio` runtime for `commands/serve/http.rs`. Off by
# default so the baseline `mkit-cli` build doesn't pay axum/hyper's compile
# cost, mirroring `enc-transport`. See SPEC-TRANSPORT-CONNECT.md.
http-transport = ["mkit-transport-connect/server", "dep:connectrpc", "dep:axum", "dep:tokio", "dep:subtle", "dep:http"]
# 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",
]
# Issue #157 — record every ref-write in an append-only,
# branch-scoped Merkle Mountain Range journal under
# `<mkit_dir>/history/<sanitized_branch>/`. Off by default because
# the commonware journaled-MMR stack pulls in tokio + commonware-
# runtime; opt in via `--features history-mmr` on builds that need
# `mkit-core::history::CommitHistory::open_at` wired into commit /
# branch / merge / rebase / cherry-pick ref advances.
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