This is the single release runbook. It consolidates what used to live under
docs/release/ (checklist, signing/verification, supply-chain policy,
reproducibility, crates.io publishing) into one page.
Contents:
- What gets published
- Pre-release checklist
- Cutting a release
- Publishing to crates.io
- Signing and verification
- Supply-chain policy
- Reproducibility
- Required GitHub Actions secrets and variables
- One-time setup
A single vX.Y.Z tag drives four decoupled channels:
- Binaries plus npm wasm via
.github/workflows/release.yml. - crates.io via
.github/workflows/crates-publish.yml. - Docs MCP corpus via
.github/workflows/mcp-release.yml, which indexes the tagged tree for the mkit docs MCP server so the served corpus always matches the published crates. - BSR proto modules (
buf.build/officialunofficial/mkit-rpcandbuf.build/officialunofficial/mkit-repo) via thebuf-pushjob in.github/workflows/crates-publish.yml, so third-party signer integrators andRepoServiceclients canbuf generatetyped bindings from a pinned tag without vendoring this repo. Dormant untilBUF_TOKENis provisioned.
All are gated on the same signed tag. Before release.yml publishes anything,
it verifies that the tag is strict semver, annotated, GPG-signed by an
allowlisted release fingerprint, and points at a commit reachable from
origin/main. It then produces:
-
GitHub Release with native binaries for five targets:
aarch64-apple-darwinx86_64-apple-darwinaarch64-unknown-linux-gnux86_64-unknown-linux-gnux86_64-pc-windows-msvc(.zip,mkit.exe,backend-windows-credentialkeystore feature enabled — see Artifacts attached to every release)
Each archive contains the
mkit/mkit.exebinary, licenses, README, optional changelog,share/man/man1/mkit.1, and shell completions undershare/completions/. Each archive is cosign-signed (keyless OIDC, Rekor logged) and ships alongside per-archive.sig/.crt/.cosign.bundle, an aggregateSHA256SUMS(also cosign-signed), a CycloneDXsbom.cdx.json, aTHIRD-PARTY-NOTICESfile, and a standards-based SLSA build provenance attestation (actions/attest-build-provenance), verifiable withgh attestation verifyorslsa-verifierin addition to cosign. -
npm package
@officialunofficial/mkit-wasm@X.Y.Z. Built withwasm-pack --target bundlerand published withnpm publish --access public. The pkg tarball is also attached to the GitHub Release asmkit-wasm-X.Y.Z-npm.tar.gzfor offline mirroring. npm provenance is enabled withnpm publish --provenance; it binds the package to this GitHub Actions workflow run through GitHub OIDC. -
crates.io — every workspace crate without
publish = false, in dependency order. See Publishing to crates.io.
Homebrew tap status: provisioned, no formula yet. The
officialunofficial/homebrew-taprepository exists and is public, but holds only LICENSE plus README — there is noFormula/directory, sobrew installfails until a formula is pushed. The Distribution step that targets the tap is live and expected to be performed at the next release.Scoop bucket status: not yet provisioned. Unlike the Homebrew tap,
officialunofficial/scoop-bucketdoes not exist yet — creating it (empty bucket repo, thenbucket/mkit.jsonfromcontrib/scoop/mkit.json) is part of the Distribution step below, same pattern as the Homebrew tap's initial setup.
Run top to bottom. Do not skip steps.
-
mainis green in CI (build plus test). -
cd rust && cargo test --workspacepasses on a fresh clone. -
cargo build --releasepasses for each release target:-
--target=aarch64-apple-darwin -
--target=x86_64-apple-darwin -
--target=x86_64-unknown-linux-gnu -
--target=aarch64-unknown-linux-gnu -
--target=x86_64-pc-windows-msvc(requires a Windows host or thewindows-latestCI runner — cross-compiling this target isn't possible here, same reasoning as thekeystore-backendsmatrix inrust.yml:windows-native-keyring-storeiscfg(windows)-gated)
-
-
[workspace.package].versioninrust/Cargo.tomlis bumped to a version not already published (crates.io versions are immutable — a re-publish of an existing version fails the wholecargo publishrun). -
CHANGELOG.mdhas an entry for this version; move items from## [Unreleased]into## [X.Y.Z] - YYYY-MM-DD. - Version bumped wherever it is hard-coded: README install snippets,
contrib/homebrew/mkit.rb,docs/INSTALL.md(three sites: twoVERSION=snippets and the expectedmkit versionoutput), and thevX.Y.Zexamples ininstall.shusage text. - Signing and verification and Reproducibility still accurate for this release.
-
MKIT_RELEASE_GPG_FINGERPRINTSrepo/org Actions variable contains the release tag signing key fingerprint. - The release tag signing public key is published to
keys.openpgp.orgorkeyserver.ubuntu.comso the workflow can import it beforegit verify-tag. -
SECURITY.mddisclosure contact confirmed reachable. - Manually dispatch
rust.yml'skeystore-backendsjob (gh workflow run rust.yml --ref main) and confirm all three legs (Linux Secret Service+systemd-creds+YubiKey, macOS Keychain+YubiKey, Windows Credential Manager) succeed. It staysworkflow_dispatch-only (3-OS matrix cost) and is not exercised by any automatic trigger, so this is the only per-release check that native keystore backends still work.
-
release.ymlsucceeded throughvalidate-release-tagand all five platform builds. -
crates-publish.ymlsucceeded (every publishable crate indexed). -
mcp-release.ymlsucceeded (docs MCP corpus indexed for the tag). - GitHub Release created as a non-draft.
- Archives present:
mkit-X.Y.Z-aarch64-apple-darwin.tar.gz,mkit-X.Y.Z-x86_64-apple-darwin.tar.gz,mkit-X.Y.Z-aarch64-unknown-linux-gnu.tar.gz,mkit-X.Y.Z-x86_64-unknown-linux-gnu.tar.gz, andmkit-X.Y.Z-x86_64-pc-windows-msvc.zip. -
sbom.cdx.jsonpresent. -
THIRD-PARTY-NOTICESpresent. -
SHA256SUMS,SHA256SUMS.sig,SHA256SUMS.crt,SHA256SUMS.cosign.bundlepresent. - Per-archive
.cosign.bundlepresent for every archive (including the.zip). -
mkit-X.Y.Z.provenance.jsonl(SLSA build provenance) present, andgh attestation verify <archive> --repo officialunofficial/mkitsucceeds for at least one archive (including the.zip).
On at least one Linux box, one macOS box, and one Windows box (or
windows-latest via gh workflow run), download the archive for your
platform, then run the mechanized checklist below instead of the steps by
hand:
# Linux / macOS
scripts/release-smoke.sh --archive mkit-X.Y.Z-<target>.tar.gz --version X.Y.Z# Windows
.\scripts\release-smoke.ps1 -Archive mkit-X.Y.Z-x86_64-pc-windows-msvc.zip -Version X.Y.ZThe script covers every item below in one pass except the "Windows
only" install.ps1 one-liner check, which still needs to be run by hand
against a fresh PowerShell session: cosign signature, the SHA256SUMS
entry, mkit version/mkit.exe version against the tag, the bundled man
page and completions, a basic mkit init → mkit keygen → add a file →
mkit commit flow, and (unless --skip-npm/-SkipNpm) npm view @officialunofficial/mkit-wasm@X.Y.Z plus npm audit signatures. It's the same
script used for a pre-flight dry run against a locally packaged archive
before tagging (missing sidecars like the cosign bundle are skipped with a
warning, not a failure, in that case) — this checklist is the spec of
record the script implements, so if the two ever disagree, fix the script:
- Download the archive for your platform.
- Verify the cosign signature (see below).
- Verify
SHA256SUMSmatches the archive. - Extract and run
./mkit-X.Y.Z-<target>/mkit version(Linux/macOS) or.\mkit-X.Y.Z-x86_64-pc-windows-msvc\mkit.exe version(Windows) — version string matches the tag. - Extracted archive contains
share/man/man1/mkit.1,share/completions/mkit.bash,share/completions/_mkit, andshare/completions/mkit.fish. - Basic flow:
mkit init→ add a file →mkit commit. - Windows only:
irm https://mkit.sh/install.ps1 | iexinstallsmkit.exeandmkit versionruns from a fresh PowerShell session. -
npm view @officialunofficial/mkit-wasm@X.Y.Zandnpm audit signatures.
- In
officialunofficial/homebrew-tap, copycontrib/homebrew/mkit.rbintoFormula/mkit.rb, update the version, and replace everyPLACEHOLDER_SHA_*with the matching archive hash from releaseSHA256SUMS. - In
officialunofficial/scoop-bucket(create the bucket repo if it doesn't exist yet — same pattern ashomebrew-tap), copycontrib/scoop/mkit.jsonintobucket/mkit.json, updateversionandurl, and replacePLACEHOLDER_SHA_X86_64_PC_WINDOWS_MSVCwith thex86_64-pc-windows-msvc.ziphash from releaseSHA256SUMS. - Release notes reviewed (auto-generated by
softprops/action-gh-releaseplus the signing snippet). - Pin the release in the repo sidebar; post in relevant channels.
- Once the Homebrew/Scoop steps above are done, dispatch
release-verify.yml(gh workflow run release-verify.yml -f version=X.Y.Z) to check every distribution channel end to end in an isolated environment —cargo install,cargo binstall, npm, the curl-pipe install script (Debian/Ubuntu/Fedora), Homebrew, and Scoop (including the Windowsinstall.ps1one-liner). Not a substitute for the smoke test above (which verifies the archive itself); this verifies every OTHER way a user actually getsmkitonto their machine.
- Open a PR bumping
CHANGELOG.mdwith a fresh## [Unreleased]heading at the top. - File follow-up issues for anything discovered during smoke test.
- Land everything on
main. Confirm CI is green. - Bump
[workspace.package].versioninrust/Cargo.toml(to a version not yet on crates.io). UpdateCHANGELOG.md(moveUnreleaseditems into[X.Y.Z] - YYYY-MM-DD). - Open a release-prep PR, merge it.
- Tag the merge commit on
main:Signed, annotated tags only. The signing key fingerprint must be listed in thegit tag -s vX.Y.Z -m "mkit X.Y.Z" git push origin vX.Y.ZMKIT_RELEASE_GPG_FINGERPRINTSrepository or organization variable and its public key must be available fromkeys.openpgp.orgorkeyserver.ubuntu.com. Verify the exact fingerprint before pushing:The release workflow rejects lightweight tags, invalid signatures, fingerprints not listed ingit tag -v vX.Y.Z
MKIT_RELEASE_GPG_FINGERPRINTS, tags outside the strictvX.Y.Z[-prerelease]form, and tag targets not reachable fromorigin/main. - Watch the workflows.
release.ymljob order is:validate-release-tag→build(× 4 archs) →sbom/third-party-notices(parallel) →release→publish-wasm.crates-publish.ymlrunscargo publish --workspace --lockedin dependency order. - Run the smoke test.
crates.io publishing is tag-driven, owned by
.github/workflows/crates-publish.yml.
On a pushed v*.*.* tag it runs cargo publish --workspace --locked, which
publishes every workspace member without publish = false to crates.io in
dependency order, waiting for each to index before the next. A guard refuses to
publish if the tag does not match the workspace version (mkit-core's version
in rust/Cargo.toml at the tagged tree).
| Publishes to crates.io | Stays off crates.io (publish = false) |
|---|---|
the library crates (mkit-core, mkit-rpc, mkit-attest, mkit-keystore, mkit-git-bridge, mkit-transport-{file,http,memory,s3,ssh,enc}) plus mkit-cli (so cargo install mkit-cli works) |
mkit-wasm (npm-only), mkit-repo-client, mkit-test-util, fuzz, benches. The contrib signers are a separate workspace under contrib/signers/ (not workspace members at all). |
The published crates depend only on each other, forming a closed,
dependency-ordered set; cargo publish --workspace computes that order and
skips any publish = false member.
Notes on the published set:
mkit-cli— published socargo install mkit-cliinstalls themkitbinary; it also ships via the signed GitHub Release archives andcargo install --git. Its library surface is unstable CLI internals, deliberately not a public API — kept out of the pre-publish semver gate (thecargo semver-checksstep incrates-publish.yml). Depend on themkit-*library crates, not onmkit_cli::….- The crates.io
mkitname belongs to an unrelated project; the CLI is published asmkit-cli. Do not runcargo install mkit.
cargo publish authenticates with the CRATES_PACKAGE_KEY org-level secret
(scopes: publish-new plus publish-update), exposed to cargo as
CARGO_REGISTRY_TOKEN. A token (not trusted publishing) is required because
brand-new crates can't use trusted publishing on their first publish. The org
secret must grant this repo access, and its crates.io account must own the
crates (via the makechain team). Migrating to crates.io Trusted Publishing
(OIDC) to drop the long-lived token is a recommended follow-up.
release-plzpublishes nothing, but it is not fully dormant: therelease-plz-releasejob in.github/workflows/release-plz.ymlruns on every push tomain(gated on the repo variableRELEASE_PLZ_ENABLED == 'true', currently true). Withrelease_always = falseinrust/release-plz.tomlit no-ops on publishing (skips with "commit is not from a release PR"), but it still does the checkout/toolchain/App-token work each push. Therelease-plz-prjob isworkflow_dispatch-only, kept as an escape hatch if versioning is ever handed back to release-plz. To truly disable the per-push job, setRELEASE_PLZ_ENABLEDtofalse.
- Immutable versions: crates.io never lets you overwrite a published version. Always bump before tagging; never re-publish an existing version.
- Partial publish: if a publish fails mid-train, the already-landed crates
can't be re-published. Re-runs must
--excludewhat landed (edit the publish step or publish the remainder by hand). - Native deps:
cargo publishbuilds each crate with default features, so nolibpcscliteis needed (mkit-keystore'sbackend-yubikeyis off by default); onlyprotocis required (installed in the workflow formkit-rpc).
Every mkit release is signed with
cosign using keyless OIDC: the
signing identity is the GitHub Actions workflow itself
(.github/workflows/release.yml), authenticated by GitHub's OIDC provider, and
every signature is recorded in the
Rekor transparency log.
Cosign signatures are keyless and bound to the artifact hash, so a stolen
release signature cannot be reused on any other artifact. The installer
(install.sh) enforces this same trust boundary by default.
For each of the five target archives, release archives build the production
mkit-cli target for that platform. The CLI enables the matching keystore
software-protector feature so software keys are encrypted at rest on supported
targets without changing the lean default feature set of the mkit-keystore
library crate. On Windows the CLI additionally enables the native
backend-windows-credential keystore backend unconditionally (via
mkit-cli's [target.'cfg(windows)'.dependencies]), matching the
windows-credential leg of rust.yml's keystore-backends matrix.
| File | Purpose |
|---|---|
mkit-X.Y.Z-<triple>.tar.gz (Linux/macOS) or .zip (Windows) |
Binary, licenses, README, manpage, completions. |
...sha256 |
SHA256 of the archive (convenience). |
...sig |
Raw cosign signature (base64). |
...crt |
Fulcio-issued code-signing certificate. |
...cosign.bundle |
Bundle: sig plus cert plus Rekor entry. |
The Windows archive is not Authenticode-signed (no code-signing certificate); trust is the cosign signature above, same as every other target.
Plus one top-level set for the aggregate:
| File | Purpose |
|---|---|
SHA256SUMS |
Hashes of every archive plus SBOM plus notices. |
SHA256SUMS.{sig,crt,cosign.bundle} |
Cosign signature of SHA256SUMS. |
sbom.cdx.json |
CycloneDX SBOM of the release. |
THIRD-PARTY-NOTICES |
Consolidated third-party license attribution for the Rust dependency graph, generated by cargo about generate (see NOTICE). |
mkit-X.Y.Z.provenance.jsonl |
GitHub-native SLSA build provenance attestation (actions/attest-build-provenance) over the archives. Verified with gh attestation verify or slsa-verifier. |
Install cosign: https://docs.sigstore.dev/cosign/installation/. Then:
VERSION=X.Y.Z
TARGET=aarch64-apple-darwin
ARCHIVE="mkit-${VERSION}-${TARGET}.tar.gz"
cosign verify-blob \
--certificate-identity-regexp '^https://github\.com/officialunofficial/mkit/\.github/workflows/release\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$' \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--bundle "${ARCHIVE}.cosign.bundle" \
"${ARCHIVE}"Expected output: Verified OK. The --certificate-identity-regexp pins the
signature to a tag build of mkit's release workflow; a signature produced by any
other workflow (a fork, a branch build, a locally-run cosign) fails
verification.
The archive's sibling .sha256 file is not an authenticity signal on its own
because it is served from the same origin as the archive. Treat it as
defense-in-depth after cosign, not instead of it.
The .cosign.bundle contains a Rekor log index. To view the public entry, add
--rekor-url "https://rekor.sigstore.dev" to the cosign verify-blob command
above, or search by the archive's sha256 at
https://search.sigstore.dev/?hash=sha256:DIGEST.
The SBOM (sbom.cdx.json) is included in the top-level SHA256SUMS, which is
itself cosign-signed. The chain is:
- Verify
SHA256SUMS.cosign.bundlesignsSHA256SUMS(cosign). - Verify
sbom.cdx.json's sha256 matches its entry inSHA256SUMS.
cosign verify-blob \
--certificate-identity-regexp '^https://github\.com/officialunofficial/mkit/\.github/workflows/release\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$' \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--bundle SHA256SUMS.cosign.bundle \
SHA256SUMS
grep ' sbom.cdx.json$' SHA256SUMS > sbom.cdx.json.sha256
sha256sum -c sbom.cdx.json.sha256 || shasum -a 256 -c sbom.cdx.json.sha256THIRD-PARTY-NOTICES is generated by cargo about generate (config:
rust/about.toml plus rust/about.hbs) from the rust workspace's resolved
dependency graph, and is covered by the same SHA256SUMS chain as the SBOM:
grep ' THIRD-PARTY-NOTICES$' SHA256SUMS > THIRD-PARTY-NOTICES.sha256
sha256sum -c THIRD-PARTY-NOTICES.sha256 || shasum -a 256 -c THIRD-PARTY-NOTICES.sha256cargo about generate fails the third-party-notices job outright if any
dependency's license isn't in rust/about.toml's accepted list — that list
is kept in lockstep with rust/deny.toml's [licenses] allow list (the
license check cargo-deny already runs on every PR via cloudbuild/security.yaml).
.github/workflows/third-party-notices.yml runs the same cargo about generate check on every PR that touches the dependency graph, so an
unapproved license is caught before a release attempt, not during one.
Every archive also carries a standard
SLSA build provenance attestation,
generated by actions/attest-build-provenance
and recorded in the Sigstore public-good transparency log. Unlike the cosign
signature above (an mkit-specific verification path), this attestation is
checkable with off-the-shelf, standards-aware tooling and requires no
mkit-specific parsing:
gh attestation verify mkit-X.Y.Z-<target>.tar.gz --repo officialunofficial/mkitor with slsa-verifier:
slsa-verifier verify-artifact mkit-X.Y.Z-<target>.tar.gz \
--provenance-path mkit-X.Y.Z.provenance.jsonl \
--source-uri github.com/officialunofficial/mkit \
--source-tag vX.Y.ZThis is additive: it does not replace the cosign signature, which remains in place and is independently verified above.
macOS binaries are not Developer ID-signed and not notarized. The trust
boundary is the cosign verification above, not Apple notarization. Users on
Ventura+ may see a Gatekeeper warning when launching a freshly-downloaded binary
from Finder. After you verify the archive, either run mkit from a terminal or
clear quarantine explicitly:
xattr -d com.apple.quarantine /path/to/mkitNotarization is future work: release.yml has no notarization step today
(only a placeholder comment). When it is wired, it will authenticate with
APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, and APPLE_TEAM_ID secrets. If
notarized macOS releases ever ship, the release notes and this document will
say so explicitly.
mkit's supply chain is intentionally narrow. The binary users run traces back to a small, auditable set of inputs. This section is the contract.
- Rust dependencies: fully pinned via
Cargo.lock. Every transitive dep is hash-pinned on each release build. The workspace's direct deps are kept deliberately small — seerust/Cargo.tomland each crate's[dependencies]. - System libs: default release builds link musl (Linux) or the platform C
runtime (macOS). Optional keystore backend features may use platform services
(Security.framework, Windows Credential Manager, D-Bus Secret Service, PC/SC,
systemd-creds). These are off by default, platform-gated, and fail closed when the required service or device is unavailable. The Linux Secret Service feature selects the pure-Rust crypto runtime, not OpenSSL. Productionmkit-clibuilds enable the target-appropriate software protector feature sosoftware:<label>stores encrypted records by default, while themkit-keystorelibrary keeps an empty default feature set. - Build inputs: the Rust toolchain (pinned in
rust-toolchain.toml), the source tree at a tagged Git commit, and the--target=/ profile flags documented under Reproducibility.
Every new direct dependency added to a workspace Cargo.toml must:
- Be version-pinned in
Cargo.tomland reflected inCargo.lock. - Have its source reviewed by two maintainers (sign-off in the PR).
- Have a stated purpose — what does mkit gain, and what would implementing it in-tree cost? The default answer should be "implement in-tree"; deps are the exception.
- Be compatible with MIT OR Apache-2.0. The CI dependency-review action
denies GPL/LGPL families when the repo has Dependency Graph enabled and
ENABLE_DEPENDENCY_REVIEW=1. - Have a cross-platform build. If the dep breaks on one of the four release targets, it doesn't land.
The optional keystore backend and envelope-encryption dependencies (issue #104)
were reviewed for the V1 keystore. All are pinned in Cargo.toml, reflected in
Cargo.lock, sourced from crates.io, accepted by cargo deny, and isolated
from default builds unless listed as always-on.
| Dependency | Use | Gate | License |
|---|---|---|---|
chacha20poly1305 = 0.10.1 |
XChaCha20-Poly1305 software-key envelope encryption | always-on keystore crypto | Apache-2.0 OR MIT |
zeroize = 1.8.2 |
Secret-memory clearing | always-on keystore crypto | Apache-2.0 OR MIT |
security-framework = 3.7.0 |
macOS Keychain bindings | backend-macos-keychain (macOS only) |
MIT OR Apache-2.0 |
keyring-core = 1.0.0 |
Shared keyring abstraction | backend-windows-credential, backend-linux-secret-service |
MIT OR Apache-2.0 |
windows-native-keyring-store = 1.0.0 |
Windows Credential Manager store | backend-windows-credential (Windows only) |
MIT OR Apache-2.0 |
zbus-secret-service-keyring-store = 1.0.0 |
Linux Secret Service store | backend-linux-secret-service (Linux only; OpenSSL-free) |
MIT OR Apache-2.0 |
card-backend-pcsc = 0.5.1 |
PC/SC card discovery | backend-yubikey |
MIT OR Apache-2.0 |
openpgp-card = 0.6.1 |
OpenPGP card protocol | backend-yubikey |
MIT OR Apache-2.0 |
secrecy = 0.10.3 |
PIN handling wrappers | backend-yubikey |
Apache-2.0 OR MIT |
der = 0.8.0 |
DER parsing for PIV certs | backend-yubikey |
Apache-2.0 OR MIT |
yubikey = 0.9.0-pre.0 |
YubiKey PIV discovery plus signing | backend-yubikey |
BSD-2-Clause |
The yubikey prerelease is accepted because V1 PIV support needs mature PIV
APDUs and no stable alternative matched the API; risk is contained by optional
feature gating, existing-key-only support, fail-closed behavior, CI feature
builds, and manual hardware validation. Remaining direct support deps are
exact-pinned (ed25519-dalek, getrandom, k256, p256, sha2, thiserror,
dev-only tempfile).
cargo deny check finishes with advisories ok, bans ok, licenses ok, sources ok. Remaining warnings are duplicate crate versions from ecosystem splits and
optional YubiKey/RustCrypto prerelease transitive stacks; they are tracked as
warnings, not release blockers, because they introduce no unapproved sources,
yanked crates, or denied licenses. Live OS-native keystore tests are opt-in via
MKIT_RUN_NATIVE_KEYSTORE_TESTS=1.
All third-party actions in .github/workflows/ must be pinned to a major
version tag (@v4) or a full SHA. No @main, no @latest. Trusted publishers:
actions/*, dtolnay/rust-toolchain, sigstore/cosign-installer,
anchore/sbom-action, softprops/action-gh-release, ossf/scorecard-action.
Any new action from an untrusted publisher needs the same two-maintainer review
as a Rust dep.
Every release archive is hashed (per-archive SHA256SUMS), signed
(cosign keyless OIDC, tied to this repo's release workflow identity), logged
in the public Rekor transparency log, and inventoried in a CycloneDX SBOM.
Package-manager manifests must reference the GitHub Releases archive URL directly
(never a re-hosted copy); the sha256 in the manifest is the authoritative pin.
A fresh SBOM is generated at release time only (the sbom job in
release.yml); there is no scheduled SBOM regeneration for main. The
weekly cadence is cargo audit via rust-security.yml (Mondays 06:00 UTC),
which sweeps for newly published advisories against the unchanged dependency
tree. Restoring a scheduled SBOM-drift job is an open follow-up. A CVE against
any shipped dep is a P1: patch, test, tag a new release,
open a GitHub Security Advisory citing the CVE, update CHANGELOG.md under
### Security, and coordinate disclosure via the SECURITY.md contact.
A mkit release binary is a function of a small, pinned set of inputs. If all of them match, any machine that can run the same Rust toolchain will produce a byte-identical binary to the one published on GitHub Releases.
- Rust toolchain version — pinned in
rust-toolchain.toml; the workflow installs it verbatim. - Target triple — one of the five release triples, passed as
--target=<triple>. - Build profile —
release. - Source tree hash — the Git commit the tag points at; every source file is tracked in Git.
- Dependency fingerprint —
Cargo.lock, fully pinning every transitive dependency. Anycargo updateis a visible change to that file.
# 1. Clone at the release tag.
git clone --depth 1 --branch vX.Y.Z https://github.com/officialunofficial/mkit.git
cd mkit
# 2. rustup picks up rust-toolchain.toml on first `cargo` invocation.
# 3. Build for your target.
cargo build --release --manifest-path rust/Cargo.toml --bin mkit
# 4. Hash the binary.
shasum -a 256 rust/target/release/mkit
# 5. Compare against SHA256SUMS from the GitHub Release.If the hashes don't match, treat it as a supply-chain incident: open an issue
with your host OS, rustc --version, the sha256 you got, and the expected one.
diffoscope on the two binaries usually isolates the culprit.
There is currently no automated reproducibility check: no CI job builds mkit twice and compares the outputs. The manual procedure above is the verification path. Restoring an automated double-build job is an open follow-up. A divergence between two builds of the same commit means a non-deterministic input has crept in (embedded timestamps, random seeds, absolute paths in debug info, unsorted directory reads) and must be fixed before the next tag.
Binaries built on your local macOS are not expected to match GitHub's macos-14
runner byte-for-byte unless your OS, SDK, and linker versions match. The Linux
x86_64 build is the most reliable reproducibility target for third parties.
Windows (x86_64-pc-windows-msvc) additionally embeds a PDB path and MSVC
linker/toolset version into the binary, so byte-identical reproduction there
also requires matching Visual Studio Build Tools versions — treat it as even
less reliable than the macOS case until that's verified.
| Secret | Purpose | Required for |
|---|---|---|
CRATES_PACKAGE_KEY (org-level) |
crates.io publish token (publish-new plus publish-update) | crates-publish.yml |
CODECOV_TOKEN |
Codecov upload auth — vestigial: no GitHub workflow reads it. Coverage runs on Cloud Build (cloudbuild/coverage.yaml); the live token is the GCP Secret Manager copy |
— (see CODECOV_TOKEN) |
BUF_TOKEN |
Buf Schema Registry API token (write access) — pushes the mkit-rpc/mkit-repo proto modules |
crates-publish.yml's buf-push job (see BUF_TOKEN) |
cosign keyless and npm Trusted Publishing (auth plus provenance) both run on the GitHub OIDC token; no extra secrets are needed for those.
| Variable | Purpose | Required for |
|---|---|---|
MKIT_RELEASE_GPG_FINGERPRINTS |
Space-separated trusted 40-hex GPG fingerprints allowed to sign release tags (public keys must be on keys.openpgp.org or keyserver.ubuntu.com) |
release.yml preflight |
BUF_PUBLISH_ENABLED |
Set to true to arm the buf-push job (job-level if: cannot read secrets, so this variable — not BUF_TOKEN's presence — is the gate) |
crates-publish.yml's buf-push job |
Generate a crates.io API token (scopes: publish-new plus publish-update) on an
account that owns the mkit-* crates (via the makechain team), and add it as
an org-level secret named CRATES_PACKAGE_KEY with this repo granted access.
For the very first publish, ensure each crate name is available or already owned
by the org; crates.io rejects deps on unpublished crates, so the dependency-order
publish in crates-publish.yml handles a clean first release once the token and
version are in place.
Codecov receives the lcov report from the Cloud Build coverage build
(cloudbuild/coverage.yaml, trigger mkit-coverage-main) so the README badge,
trend chart, and PR-diff overlay work. The live token lives in GCP Secret
Manager, not GitHub: no GitHub workflow reads CODECOV_TOKEN anymore, so the
GitHub Actions secret of the same name is vestigial and safe to delete.
- Sign in at https://app.codecov.io/ with the GitHub account that admins the
officialunofficialorg. - Add the
officialunofficial/mkitrepo; copy the "Upload token". - Store it as the
CODECOV_TOKENsecret in GCP Secret Manager and grant the Cloud Build service account access (cloudbuild/coverage.yamlmountsprojects/${PROJECT_ID}/secrets/CODECOV_TOKEN/versions/latest). The upload is optional: if the secret is absent, the upload step is skipped and the report is still produced and logged. - Trigger a run: push to
main(mkit-coverage-mainfires on push tomainonly). The first run seeds the baseline; subsequent runs populate the badge. - Once the repo flips public, Codecov accepts tokenless uploads from public repos via GitHub OIDC — but that applies to GitHub Actions uploads, not Cloud Build, so the Secret Manager token stays.
@officialunofficial/mkit-wasm is the npm package published by the release
workflow, under the officialunofficial npm org (matching the GitHub org —
renamed from the earlier @makechain/mkit-wasm once the repo went public).
publish-wasm authenticates via the GitHub Actions OIDC token directly (npm
Trusted Publishers) — there is
no MKIT_NPM_TOKEN-equivalent secret to provision, rotate, leak, or
revoke for the automated path. This replaced the original
Automation-token setup once the package had at least one successful
token-based release behind it (0.1.0–0.3.0, published as
@makechain/mkit-wasm).
One-time setup (npmjs.com, by an owner of the officialunofficial org):
-
If
@officialunofficial/mkit-wasmhas never been published, either cut a real tag (the workflow's first successful OIDC-authenticated publish claims the name) or publish a placeholder once from a maintainer workstation with a personal token:cd rust wasm-pack build crates/mkit-wasm --release --target bundler --out-dir pkg cd crates/mkit-wasm/pkg npm pkg set name=@officialunofficial/mkit-wasm npm version --no-git-tag-version --allow-same-version 0.0.0-init npm publish --access public --tag init
wasm-pack's raw output is named unscopedmkit-wasm, hence thenpm pkg setstep.--tag initkeeps the placeholder off thelatestdist-tag once a real version publishes (it briefly holdslatesttoo, being the only version, until the first real tag push supersedes it).The npm registry's CDN edge cache can serve a stale 404 for a brand-new scope/package for a few minutes after a successful publish, even though
npm dist-tag ls,npm access list packages, andnpm install(corgi-format endpoint) already see it — don't diagnose a fresh publish as failed from a barenpm view/curl404, and don't re-query the same URL repeatedly (it just refreshes the negative-cache TTL). Wait a few minutes and recheck. -
On the package's
npmjs.compage → Settings → Trusted Publisher, add a GitHub Actions publisher:- Organization or user:
officialunofficial - Repository:
mkit - Workflow filename:
release.yml(filename only, not the full.github/workflows/path) - Environment: leave blank (the job doesn't use a GitHub environment)
- Allowed actions:
npm publish
- Organization or user:
-
Verify with
npm view @officialunofficial/mkit-wasmafter the first OIDC-authenticated publish; confirm provenance withnpm audit signatures.
Requires npm CLI ≥11.5.1 (the workflow pins this explicitly) and GitHub-hosted runners (self-hosted runners aren't supported for Trusted Publishing as of this writing).
buf.build/officialunofficial/mkit-rpc (common.proto/signer.proto/ssh.proto/
verify.proto, from the rust/crates/mkit-rpc/proto module) and
buf.build/officialunofficial/mkit-repo (repo.proto, from the
apps/repo-worker/proto module) are the two named modules in the repo-root
buf.yaml v2 workspace that the buf-push job in crates-publish.yml
publishes on every tagged release (issue #719). Both are pushed private —
signer.proto is the security-critical external-signer wire contract
(SPEC-EXTERNAL-SIGNER.md) and repo.proto is
this repo's internal RepoService contract; flip a module to public only as a
deliberate decision, not a default.
- Sign in (or create an account) at https://buf.build/ under the
officialunofficialorganization. The BSR org name is assumed to match the GitHub org (officialunofficial) — confirm this before the first push, or update thename:field on therust/crates/mkit-rpc/protoandapps/repo-worker/protomodules in the repo-rootbuf.yaml(and thebuf generate/buf pushreferences in this doc and thebuf-pushjob) to the actual BSR org if it differs. - Generate a BSR API token (
buf.build→ Settings → API Tokens) scoped to that organization, with write access. - Add it as an org-level (or repo) secret named
BUF_TOKEN, and set theBUF_PUBLISH_ENABLEDrepo/org variable totrue. Until the variable istrue,buf-pushis a clean skip (if: vars.BUF_PUBLISH_ENABLED == 'true') — same dormant-until-provisioned pattern asRELEASE_PLZ_ENABLED. Two knobs, not one, because a job-levelif:cannot read thesecretscontext to gate onBUF_TOKEN's presence directly. - First push auto-creates both BSR repositories (
--create --create-visibility private); no manual "create the module" step needed. - Verify: after a release tag,
buf generate buf.build/officialunofficial/mkit-repo:vX.Y.Z --template apps/repo-worker/proto/buf.gen.yaml(withbuf registry loginfirst, since the module is private) should produce TypeScript bindings without this repo checked out — this is exactly what thebuf-pushjob's own smoke-test step does on every tagged release.
The Rust crate remains mkit-wasm, but the release workflow publishes the npm
package as @officialunofficial/mkit-wasm, matching the GitHub org this repo
lives under (renamed from @makechain/mkit-wasm once the repo went public —
see npm Trusted Publishing
above). If the scope ever changes again, update
rust/crates/mkit-wasm/README.md's install snippet, the publish-wasm workflow
npm pkg set name=... line, the npm Trusted Publisher config on npmjs.com, and
the npm view smoke-test references in this doc.