SKILL.md
At e15865e
Preview
Text · 15.7 KiBname: mkit
description: >
Drive the 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
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.
Setup
# 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.
Mental model: like git, with four differences that matter
- Object IDs are 64-hex BLAKE3, not 40-hex SHA-1. A git SHA pasted into
mkit will never resolve. Use
--short[=N]/rev-parse --shortfor abbreviations. Short-prefix lookups work like git. - The repo marker is
.mkit/, not.git/. Layout is parallel:.mkit/objects/,.mkit/refs/,.mkit/HEAD,.mkit/config. - Safety guards over git's destructive defaults. Data-losing operations
refuse without
-f, and most accept-n/--dry-runto preview:reset --hard,clean,restore,branch -D(still refuses the current branch),push --force(prefer--force-with-lease),gc. - Authorship is cryptographic. The signed author defaults to your signing
key's public key (an
ed25519:<hex>identity) — no config needed.user.identityoverrides it (ed25519:<hex>/mid:<N>);user.name/user.emailare accepted as git-compat aliases but are non-authoritative — they never set who signed.
- Partial and full disclosure (no git counterpart):
mkit prove,mkit verify-proof,mkit closure export, andmkit closure verifyprove 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). Seedocs/VERIFY.mdfor the verifier-facing guide;docs/CLI.mdfor 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).
Signing keys
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 | deleteGrants, epochs and visibility (private repos and delegated writes)
An 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)--capisread,read,writeorwrite; writes need--refs pattern=flags(flags fromcufd). A server that can't check fast-forwards (every current deployment) applies an update only withf, so usecuf.- The owner signs with the mkit key by default; a wallet or passkey signs by
--print-statement, then--statement-file f --signature HEX(or--webauthn-assertion, which needsgrant.webauthn_rpin the user config). - Among usable grants the higher epoch wins; a grant issued for a future epoch outranks a live one until the owner bumps the epoch. A write-only grant never lets you read a private repository.
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.
Attestation (in-toto v1 plus DSSE)
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.
Content-addressed object inspection
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)Global flags (before the subcommand, like git)
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 paginatesBranch switching
mkit 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 nameRemotes and transports
Remote 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).
Durability: pack-shards
mkit pack-shard <hash> # Reed-Solomon erasure-code a stored pack into shards
# (requires the `pack-shards` build feature)Prefer MCP over raw shell (when available)
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.
Rules for agents
- Run
mkit keygenbefore the first commit (or use an Ed25519 keystore key). Commits/signed tags/attestations always sign; without a key they fail. - Never invoke the interactive variants — they block on
$EDITORor stdin and will hang you: plaincommit(always pass-m), annotated/signedtag -a/-s(pass-m),rebase -i, andadd -p. Use the non-interactive forms. - No pager, ever.
log/diff/show/blameprint straight to stdout and exit — capture them directly; you don't need--no-pageror to pipe tocat. - Parse machine output, not prose. Many commands take
--format=json(log,branch,blame,remote,config,reflog) andstatustakes--porcelain[=v1|v2]. Use-zfor NUL-terminated paths. Note several commands put human prose on stderr and reserve stdout for machine output. - Branch on exit codes, not stderr text (see table) — distinguish a usage
typo (
64) from a retryable transient (75) without scraping messages. - Preview destructive ops with
-n/--dry-run, commit them with-f. - Treat ids as 64-hex. Don't hardcode 40-char SHA assumptions.
NO_COLOR=1(or piping) disables ANSI;CLICOLOR_FORCE=1forces it.
Exit codes (BSD 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 |
Common issues
| 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. |
Going deeper
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:
- Full command reference:
docs/CLI.mdandman mkit. - Git-parity scope and deliberate divergences:
docs/PARITY.md. - Wire/on-disk formats and subsystems:
docs/specs/SPEC-*.md(objects, signing, attestations, transport, packfile, keystore, …). - Install channels (release archives, hardware signers, WASM/npm):
docs/INSTALL.md.