mkit CLI — a content-addressed version control tool with BLAKE3
object IDs, Ed25519-signed commits, and native in-toto/DSSE attestation. Use
this when working in a .mkit/ repository, making signed commits, managing
signing keys, producing or verifying supply-chain attestations, inspecting
content-addressed objects, or syncing over mkit+ssh/https/s3/file
transports. mkit mirrors git's CLI/UX, so git muscle memory applies — this
skill focuses on the parts that are not like git.mkit is a git-like CLI that produces signed, content-addressed objects.
Every commit is Ed25519-signed and named by its BLAKE3 hash, so object chains
are self-verifying independent of where they're stored, and any commit can
carry attestations (in-toto v1 Statements in DSSE envelopes) that downstream
services verify against a trust-roots registry.
If you already know git, you can drive add/commit/log/branch/merge/
rebase/stash/diff/status etc. by reflex. Spend your attention on the
four differences below and the differentiator commands — that's where mkit is
not git.
# Install the CLI from crates.io (the public channel). The binary is `mkit`.
cargo install mkit-cli
# ^ NOTE: install `mkit-cli`, NOT `mkit` — `mkit` on crates.io is a
# different, unrelated crate. Do not run `cargo install mkit`.
# (Signed release archives are also published on GitHub Releases; crates.io
# is simply the most convenient install channel.)
mkit --version # must print exactly: mkit <X.Y.Z>
# Start a repo and make your first signed commit:
mkit init # creates .mkit/ in the current directory
mkit keygen # generate an Ed25519 signing key (signing_key path; default .mkit/keys/default.key)
echo hello > hi.txt
mkit add hi.txt
mkit commit -m "first commit" # commits are always Ed25519-signedA signing key is mandatory: commit/tag -s/attest need one. If you skip
keygen, commits fail. commit opens an editor ($GIT_EDITOR, then
$EDITOR, then $VISUAL, falling back to vi/notepad) when -m/-F is
omitted — so in a headless or agent context, always pass -m or -F so it
never blocks on an editor.
--short[=N] / rev-parse --short for
abbreviations. Short-prefix lookups work like git..mkit/, not .git/. Layout is parallel:
.mkit/objects/, .mkit/refs/, .mkit/HEAD, .mkit/config.-f, and most accept -n/--dry-run to preview:
reset --hard, clean, restore, branch -D (still refuses the current
branch), push --force (prefer --force-with-lease), gc.ed25519:<hex> identity) — no config needed. user.identity
overrides it (ed25519:<hex> / mid:<N>); user.name / user.email are
accepted as git-compat aliases but are non-authoritative — they never set
who signed.mkit prove,
mkit verify-proof, mkit closure export, and mkit closure verify
prove a path/chunk/range or a full object set against a commit id, for
an external party with no repository access (a DA provider, a light
client, a browser). See docs/VERIFY.md for the verifier-facing guide;
docs/CLI.md for flags.Accepted-but-no-op / out of scope (so you don't wait on them): log --graph is
accepted but does nothing; submodules, hooks, git notes, and .git/-format
interop are explicit non-goals. Linked working trees ARE supported:
mkit worktree add/list/remove/prune (linked trees share the object store and
refs; each tree keeps its own HEAD/index/stash, and a branch can be checked
out in at most one tree).
Commits and tags are always Ed25519-signed. The commit/tag signing key is Ed25519 and lives at .mkit/keys/default.key:
mkit keygen [--algorithm ed25519|secp256k1|p256] [--force] [--print-pubkey]
# Ed25519 -> .mkit/keys/default.key
mkit verify <rev> # check the signature on a commit, remix, or signed tag
mkit tag -s <name> -m "msg" # signed tag (always pass -m; no -m opens an editor)Instead of the repo-local file, an Ed25519 key in the OS keystore can sign (custody that persists across repos — Keychain / libsecret / systemd-creds / YubiKey):
mkit key generate # also: list | import | export | deleteAn owner can let another key write to (or read a private) repository with a signed
grant; the grantee stores it and mkit push/clone present it automatically.
mkit grant create --cap read,write --grantee <ed25519-pubkey-hex> --repo site \
--refs 'refs/heads/*=cuf' --ttl 7d > grant.txt # owner: prints the grant header
mkit grant add grant.txt # grantee: verify + store it
mkit grant list [--check] # what you hold (--check: stale/future epoch)
mkit epoch show <remote> # the namespace's current epoch
mkit grant revoke <remote> [--prune] # owner: epoch bump, revokes older grants
mkit visibility set <remote> private # owner: public|private (--statement for a wallet/passkey owner)--cap is read, read,write or write; writes need --refs pattern=flags (flags from cufd).
A server that can't check fast-forwards (every current deployment) applies an update only with f, so use cuf.--print-statement, then --statement-file f --signature HEX (or --webauthn-assertion,
which needs grant.webauthn_rp in the user config).mkit grant add (your store) is the client-side grant store; it does not register grants on a server.The signed author is auto-derived from the signing key (difference #4 above); set
user.identity only to pin a different one.
keygen --algorithm secp256k1|p256does NOT make a commit key. It writes a separate attestation signer key (.mkit/keys/<alg>.key) consumed byattest(below). Generating one and then runningcommitfails with "no signing key" — run plainmkit keygenfor the Ed25519 commit key.
Attach signed, verifiable claims (provenance, review, SBOM, …) to a commit:
# Produce a DSSE attestation for HEAD (or --commit <hash>), optionally with a
# predicate document and multiple co-signers (all-or-nothing — any signer
# failure aborts; no partial envelopes are written). Multi-signer is
# shell-only — the MCP tool is single-signer by design:
mkit attest --algorithm ed25519 \
--additional-signer "algorithm=p256,signer=repo-key" \
--predicate-type https://example.com/review/v1 \
--predicate-file review.json
# Verify every attestation on a commit against a trust-roots registry:
mkit verify-attest --commit <hash> --trust-roots ~/.config/mkit/trust-roots.tomlSecurity gate: verify-attest refuses to use an in-repo trust-roots file
unless you pass --trust-roots explicitly — otherwise a hostile clone could
ship its own roots and make verification print "ok" against attacker keys.
Default roots path is $XDG_CONFIG_HOME/mkit/trust-roots.toml. Exit 0 iff
every attestation has ≥1 verified signature, 65 if any failed, 1 if the
commit has no attestations.
mkit hash <file> # hash a file and store it as a blob → prints its id
mkit cat <hash> # dump an object by id
mkit cat-file -t <object> # type | -s size | -p pretty (blob/tree/commit/tag)
mkit cat-file --batch # stream object ids on stdin
mkit tree # snapshot the working dir as a tree object
mkit ls-tree -r <tree-ish> # list tree entries (-r recurse, -z NUL-terminate)
mkit rev-parse --short <rev> # resolve a revision to an (abbreviated) id
mkit rev-list --count <rev> # count commits reachable from <rev> (omit --count to list)
mkit merge-base [--is-ancestor] <a> <b> # common ancestor (or test ancestry: exit 0/1)mkit -C <path> <command> # run as if started in <path> (repeatable)
mkit -c user.email=ci@x.com <command> # one-shot config override (inert/allowlisted
# keys only; security-sensitive keys are refused)
mkit --no-pager <command> # accepted no-op — mkit never paginatesmkit switch <branch> # switch branches (git switch)
mkit switch -c <new> [<start>] # create + switch (-C create-or-reset)
mkit checkout -b <new> [<start>] # same, classic spelling (-B create-or-reset)
mkit branch --show-current # print the current branch nameRemote URLs use the strict mkit+<scheme>:// form only (anything else is
hard-rejected):
| Scheme | Form | Use |
|---|---|---|
mkit+file | mkit+file:///abs/path | local filesystem mirror |
mkit+https | mkit+https://host[:port]/path | HTTP gateway |
mkit+s3 | mkit+s3://endpoint/bucket[/prefix] | S3-compatible store |
mkit+ssh | mkit+ssh://user@host[:port]:path | SSH with the mkit shell |
mkit remote add origin mkit+https://gateway.example/repo
mkit clone [--depth N] [--sparse <pattern>...] mkit+ssh://user@host:path
mkit push [--all] [--force-with-lease] [--dry-run]
mkit pull # or: mkit fetch (download without merging)
# Trust an HTTP/S3 remote for ambient env credentials, or tune SSH policy:
mkit config trusted_remote_endpoint <url>
mkit config ssh.strict_host_key_checking <yes|no|accept-new>
mkit config ssh.identity_file <path>mkit+ssh uses SSH_AUTH_SOCK (standard OpenSSH agent). mkit serve <path>
starts the SSH-transport server (internal/host side).
mkit pack-shard <hash> # Reed-Solomon erasure-code a stored pack into shards
# (requires the `pack-shards` build feature)The CLI ships a local MCP server: mkit mcp [--repository <path>]. If your
harness supports MCP, register it and drive repositories through structured
tool calls instead of shelling out — inputs are validated, no interactive
paths exist, and destructive guards can't be overridden (the server never
passes -f):
claude mcp add mkit-repo -- mkit mcp --repository /path/to/repoIts 21 tools cover the everyday flow (mkit_status, diffs, mkit_log,
mkit_show, mkit_branch, mkit_add, mkit_unstage, mkit_commit,
create-branch/checkout, mkit_init, mkit_keygen, mkit_cat_object) plus
the differentiators (mkit_verify, mkit_attest, mkit_verify_attest,
mkit_prove, mkit_verify_proof, mkit_closure_verify).
What the MCP deliberately does NOT expose — use the shell commands in this
skill for these: remotes (push/pull/fetch/clone), history surgery
(merge/rebase/cherry-pick/revert), tags, destructive worktree ops
(reset --hard/clean/rm), and multi-/external-signer attestation
(--additional-signer, --signer external).
Two boundaries are stricter via MCP than the shell: an attest predicate
file must resolve inside the repo, and a verify_attest trust-roots path
must resolve outside it (in-repo roots are always rejected — hostile-clone
defense). Signing is also pinned: MCP attestations always sign ed25519 with
the repo key unless you explicitly choose otherwise; ambient attest.* config
never steers them.
The shell rules below still apply whenever you do run mkit directly.
mkit keygen before the first commit (or use an Ed25519 keystore key).
Commits/signed tags/attestations always sign; without a key they fail.$EDITOR or stdin
and will hang you: plain commit (always pass -m), annotated/signed tag -a/
-s (pass -m), rebase -i, and add -p. Use the non-interactive forms.log/diff/show/blame print straight to stdout and
exit — capture them directly; you don't need --no-pager or to pipe to cat.--format=json
(log, branch, blame, remote, config, reflog) and status takes
--porcelain[=v1|v2]. Use -z for NUL-terminated paths. Note several commands
put human prose on stderr and reserve stdout for machine output.64) from a retryable transient (75) without scraping messages.-n/--dry-run, commit them with -f.NO_COLOR=1 (or piping) disables ANSI; CLICOLOR_FORCE=1 forces it.sysexits)| Code | Meaning | Code | Meaning |
|---|---|---|---|
| 0 | success | 69 | transport could not connect |
| 1 | general error / no attestations | 73 | cannot create output |
| 64 | wrong args / unknown subcommand | 75 | transient — retry is safe |
| 65 | malformed input (corrupt object/bad hash) | 76 | bad URL scheme / server response |
| 66 | missing / unreadable input | 77 | permission denied |
| 78 | unknown config key / invalid value |
| Symptom | Cause / fix |
|---|---|
cargo install mkit installs the wrong tool | Install mkit-cli; the binary is mkit. |
| A pasted id won't resolve | mkit ids are 64-hex BLAKE3, not git's 40-hex SHA-1. |
commit fails complaining about signing/identity | Run mkit keygen (or set up a keystore key) first. |
"no signing key" right after keygen --algorithm p256/secp256k1 | Those make attestation keys, not the commit key — run plain mkit keygen (Ed25519). |
mkit command appears to hang | You hit an interactive variant opening $EDITOR/a prompt — pass -m, or avoid rebase -i / add -p. |
reset --hard / clean / restore "refuses" | A safety guard — re-run with -f (use -n to preview). |
remote add rejects the URL | Must be mkit+file://, mkit+https://, mkit+s3://, or mkit+ssh://. |
verify-attest won't use the repo's trust-roots | Intentional — pass --trust-roots <path> explicitly. |
commit opens an editor / aborts with no message | Pass -m, or set $EDITOR/$VISUAL. |
If the mkit MCP is connected (mcp.mkit.sh), prefer its tools for
authoritative depth: get_command <name> for a subcommand's full flags,
get_spec <NAME> / list_specs for wire and on-disk formats, search_docs /
search_code to find behavior, and get_file 'docs/CLI.md' for the complete
reference. In a checkout, the same content lives at:
docs/CLI.md and man mkit.docs/PARITY.md.docs/specs/SPEC-*.md (objects, signing,
attestations, transport, packfile, keystore, …).docs/INSTALL.md.