Quick install
curl -fsSL https://majordomus.dev/install.sh | sh
Then, in the repository you want supervised:
majordomus init
init adds what the layer needs and leaves what the repository already has alone. The one
exception is a README.md that does not mention AGENTS.md: init appends a short
paragraph naming it, because doctor fails a README that cannot lead a reader to the
agent bootstrap. init lists the paragraph with everything else it created. It creates
no README where there is none, and a personal layer leaves the project's README alone.
Install and initialise in one command
curl -fsSL https://majordomus.dev/install.sh | sh -s -- --init
--init runs majordomus init in the current directory using the executable that was just
installed, by its absolute path, so it does not depend on your PATH having been reloaded.
If installation succeeds and initialisation fails, the installer says so in those words and
leaves the working installation in place.
Personal mode
curl -fsSL https://majordomus.dev/install.sh | sh -s -- --personal
For using the tool as your own in a repository other people share: your team's, a
client's, an open-source project's. --personal runs majordomus init --personal in the
current directory. It creates the same layer a plain init creates and changes nothing the
repository shares.
- No tracked file is changed and nothing is added to
.gitignore. The layer is covered by a marked block in the git directory'sinfo/exclude, a file git never commits, pushes or clones. majordomus init --personalreadsgit statusbefore it writes and exits 10 unless it reads the same afterwards. The installer then compares the status again by itself and printsVerified (personal).majordomus updateandmajordomus capture installcover each file before they write it and leave a file the project already tracks alone.majordomus doctorre-verifies the cover on every run.- The layer is this checkout's. It does not travel with a clone, a push or a stash.
When the trial has made its case, hand the layer to the team:
majordomus init --extend --shared
It removes the cover, adds the two .gitignore lines a plain init adds, and prints what
is now the repository's to commit. It commits nothing; that step is yours. A file that is
one person's by its own name, such as CLAUDE.local.md, stays covered and stays yours.
To stop instead, delete the block between # >>> majordomus personal >>> and
# <<< majordomus personal <<< and the files it lists.
Personal mode keeps a layer apart from what a repository shares. It is not access control,
and it is not a way around an organisation's rules about tools. The decision is ADR 0145;
PERSONAL_MODE.md is the whole account: what it is for, what stays
visible, and the way from a personal layer to a shared one.
Verify
majordomus --version
majordomus doctor
doctor reports whether the layer resolves, whether every enforcement it claims is actually
wired, and which hook lines are still missing.
What it does to your machine
Nothing outside two places, and never as root:
$HOME/.local/share/majordomus/versions/<version>/ the release, unpacked
$HOME/.local/bin/majordomus a launcher
$HOME/.local/bin/majordomus-mcp a launcher for the MCP server
The launchers are two-line shell scripts that exec the versioned tree. No shell profile is
edited, no package manager is invoked, and sudo is never run: if a directory is not
writable the installer says which one and stops.
Supported platforms
| Platform | Architecture | libc | Rust target | Status |
|---|---|---|---|---|
| macOS | ARM64 | — | aarch64-apple-darwin | supported |
| macOS | x86_64 | — | x86_64-apple-darwin | supported |
| Linux | x86_64 | glibc | x86_64-unknown-linux-gnu | supported |
| Linux | x86_64 | musl | x86_64-unknown-linux-musl | supported |
| Linux | ARM64 | glibc | aarch64-unknown-linux-gnu | supported |
| Linux | ARM64 | musl | aarch64-unknown-linux-musl | supported |
| Windows | x86_64 | — | x86_64-pc-windows-msvc | unavailable |
That table is generated from share/distribution.yaml, which is the one place a platform is
declared. The installer resolves your machine to a row of it from uname -s, uname -m and,
on Linux, the C library it finds; the release build matrix is generated from the same rows,
so a platform listed here is a platform a release actually built.
Windows x86_64 (x86_64-pc-windows-msvc) — The command a person runs is bash: bin/majordomus and lib/*.sh are POSIX shell, and the archive carries them beside the executable. Until that half has a Windows answer, publishing a Windows archive would ship an executable nothing can drive.
Choosing where it goes
curl -fsSL https://majordomus.dev/install.sh | sh -s -- --install-dir "$HOME/bin"
curl -fsSL https://majordomus.dev/install.sh | sh -s -- --prefix "$HOME/opt/majordomus"
or, equivalently:
MAJORDOMUS_INSTALL_DIR="$HOME/bin" curl -fsSL https://majordomus.dev/install.sh | shPinning a version
curl -fsSL https://majordomus.dev/install.sh | sh -s -- --version v0.30.2
A pinned installation resolves https://majordomus.dev/releases/v0.30.2.json, which names the exact artifact and its
sha256 digest, and therefore installs the same bytes every time. An unpinned
installation resolves https://majordomus.dev/releases/latest.json, which is the latest stable release and moves
forward as releases are published. Use the pinned form in CI.
Using it in CI
curl -fsSL https://majordomus.dev/install.sh | sh -s -- --version v0.30.2
export PATH="$HOME/.local/bin:$PATH"
majordomus --version
The installer needs no interaction and asks none: everything it does is decided by its arguments and environment. Pin the version so that a new release cannot change a build that used to pass.
Upgrading and reinstalling
Run the same command again. The installer is idempotent:
- the same version already installed — it says so and exits 0;
- an older version installed — it upgrades, and the previous installation keeps working until the moment the launcher is replaced;
- a newer version installed and no version pinned — it refuses to move you backwards, and
says how to do it anyway (
--force, or--version).
Uninstalling
rm -f $HOME/.local/bin/majordomus $HOME/.local/bin/majordomus-mcp
rm -rf $HOME/.local/share/majordomus
That is the whole of it. It removes the tool. It does not remove any repository's
.ai/ directory: your policy, rules, decisions, plan and history are the repository's, not
the tool's, and no command of this project deletes them.
Releases
| Release | Published | Channel | Artifacts | Metadata |
|---|---|---|---|---|
v1.0.0-alpha.5 | 2026-10-22 | prerelease | 6 | v1.0.0-alpha.5.json |
v1.0.0-alpha.4 | 2026-10-21 | prerelease | 6 | v1.0.0-alpha.4.json |
v1.0.0-alpha.2 | 2026-10-21 | prerelease | 6 | v1.0.0-alpha.2.json |
v1.0.0-alpha.1 | 2026-10-20 | prerelease | 6 | v1.0.0-alpha.1.json |
v0.30.2 (latest) | 2026-10-19 | stable | 6 | v0.30.2.json |
v0.30.1 | 2026-10-19 | stable | 6 | v0.30.1.json |
v0.30.0 | 2026-10-18 | stable | 6 | v0.30.0.json |
v0.29.0 | 2026-10-17 | stable | 6 | v0.29.0.json |
v0.28.0 | 2026-10-17 | stable | 6 | v0.28.0.json |
v0.27.0 | 2026-10-16 | stable | 6 | v0.27.0.json |
v0.26.0 | 2026-10-14 | stable | 6 | v0.26.0.json |
v0.25.0 | 2026-10-14 | stable | 6 | v0.25.0.json |
v0.24.1 | 2026-10-14 | stable | 6 | v0.24.1.json |
v0.23.0 | 2026-10-13 | stable | 6 | v0.23.0.json |
v0.22.0 | 2026-10-13 | stable | 6 | v0.22.0.json |
v0.21.0 | 2026-10-12 | stable | 6 | v0.21.0.json |
v0.20.0 | 2026-10-10 | stable | 6 | v0.20.0.json |
v0.19.1 | 2026-10-10 | stable | 6 | v0.19.1.json |
v0.19.0 | 2026-10-09 | stable | 6 | v0.19.0.json |
v0.18.0 | 2026-10-09 | stable | 6 | v0.18.0.json |
v0.17.0 | 2026-10-08 | stable | 6 | v0.17.0.json |
v0.16.0 | 2026-10-08 | stable | 6 | v0.16.0.json |
v0.15.0 | 2026-10-07 | stable | 6 | v0.15.0.json |
v0.14.0 | 2026-10-06 | stable | 6 | v0.14.0.json |
v0.13.2 | 2026-10-06 | stable | 6 | v0.13.2.json |
v0.13.1 | 2026-10-04 | stable | 6 | v0.13.1.json |
v0.13.0 | 2026-10-04 | stable | 6 | v0.13.0.json |
v0.12.0 | 2026-10-03 | stable | 6 | v0.12.0.json |
v0.11.0 | 2026-10-02 | stable | 6 | v0.11.0.json |
v0.10.0 | 2026-09-29 | stable | 6 | v0.10.0.json |
v0.9.0 | 2026-09-25 | stable | 6 | v0.9.0.json |
v0.8.0 | 2026-09-20 | stable | 6 | v0.8.0.json |
v0.7.0 | 2026-09-15 | stable | 6 | v0.7.0.json |
v0.6.0 | 2026-09-12 | stable | 6 | v0.6.0.json |
v0.5.0 | 2026-09-11 | stable | 6 | v0.5.0.json |
v0.3.1 | 2026-09-08 | stable | 6 | v0.3.1.json |
Each release publishes one archive per supported target plus a SHA256SUMS file, and
its metadata is served as JSON beside the installer. The installer resolves a release from
that JSON, never from GitHub's API or HTML, so a change in GitHub's pages cannot break an
installation. The one call to GitHub it can make is the optional provenance check below,
through the GitHub CLI.
Security model
What is protected, and how:
- The metadata is fetched over HTTPS, with redirects restricted to HTTPS. A base URL that is not HTTPS is refused unless two explicit environment variables say otherwise, which exists so the installer's own tests can serve fixtures.
- The artifact is verified before it is unpacked. The metadata carries a sha256 digest and a size; the installer checks both and, on a mismatch, discards the download and stops. There is no "warn and continue".
- The artifact URL is checked against the project's own release host before anything is fetched from it, and every value read out of the metadata is matched against the shape it must have. Nothing read over the network reaches a command line, a path or the shell.
- The archive is inspected before extraction. Absolute paths,
..traversal, symbolic and hard links, devices, and any entry outside the archive's own directory are refused by name and nothing is unpacked. - The installation is atomic. The new tree is unpacked beside the old one and its executable is run once, from a temporary location, to prove it reports the version that was resolved. Only then is the launcher replaced, by a rename. Any failure before that leaves the previous installation exactly as it was.
- Nothing is elevated.
sudoappears nowhere in the installer.
What remains trusted: GitHub Pages serving the metadata, GitHub Releases serving the
artifacts, and the release pipeline that produced both. Checksums bind the artifact to the
metadata; they do not, on their own, prove who wrote the metadata. That is what provenance
is for: every release archive since v0.20.0 is attested by the release workflow (earlier ones
carry their checksums only), and where the GitHub CLI
is installed and signed in the installer asks it whether this archive was built by this
repository's workflow. The closing report says which it verified — provenance (…) and checksum, or checksum only with the reason. MAJORDOMUS_REQUIRE_PROVENANCE=1 installs
nothing short of verified provenance. By hand: gh attestation verify <archive> --repo <repository>. What each path verifies is in docs/DISTRIBUTION.md.
Reading it before running it
curl … | sh runs a script you have not read. If you would rather read it first:
curl -fsSL https://majordomus.dev/install.sh -o install-majordomus.sh
less install-majordomus.sh
sh install-majordomus.sh
The installer is one file with no dependencies beyond curl or wget, tar, and one of
sha256sum or openssl. Its top half is generated from the distribution model and
its bottom half is behaviour; both are reviewed in this repository and linted by
shellcheck in CI.
Troubleshooting
majordomus: command not found after installing. The launcher directory is not on your
PATH. The installer prints the exact line to add; it does not edit your shell profile.
"does not provide a prebuilt binary for". Your machine resolved to no supported target. The message names what was detected and lists what is supported. There is no automatic fallback to building from source: that would need the toolchain this installer promises not to require.
"the downloaded archive does not match its digest". Stop. Nothing was installed. Run it again; if it happens twice, the artifact or the metadata is wrong and forcing it would be the wrong response.
Behind a proxy. curl and wget read the usual https_proxy environment; the
installer adds nothing of its own.
Seeing what it would do.
curl -fsSL https://majordomus.dev/install.sh | sh -s -- --dry-run
curl -fsSL https://majordomus.dev/install.sh | sh -s -- --verbose
--dry-run resolves the platform, the release and every path, prints them, and changes
nothing.
Building from source
For contributors, not for installation. The repository's own tooling is documented in
CONTRIBUTING.md:
git clone https://github.com/korczis/prismatic-majordomus majordomus
cd majordomus
cargo build --release --manifest-path apps/majordomus-cli/Cargo.toml
The shell tool needs no build at all: bin/majordomus runs from a checkout. Use the
installer for everything else — a source checkout is a development environment, not an
installation.
How this works underneath
The architecture, the trust path, the release procedure and how to add a platform are in
DISTRIBUTION.md.