Updates the visible 'how trustworthy is this' signal across all three
touchpoints after the verifier sweep landed 22 modules confirmed in
real Linux VMs:
README.md
- Badge: '28 verified + 3 ported' → '22 VM-verified / 26'.
- Headline tagline: emphasizes the 22-of-26 empirical confirmation.
- 'Corpus at a glance' restructured: tier counts unchanged, but the
stale '3 ported-but-unverified' subsection is replaced by a new
'Empirical verification' table breaking the 22 records down by
distro/kernel.
- 'Status' section refreshed for v0.6.0 reality: 88 tests + 22
verifications + mainline kernel fetch + --explain + KEV/CWE/ATT&CK
metadata + 119 detection rules. The four still-unverified entries
(vmwgfx, dirty_cow, dirtydecrypt, fragnesia) are listed with their
blocking reasons.
docs/index.html
- Hero stats row gets a new '22 ✓ VM-verified' chip (emerald-styled
via new .stat-vfy CSS class), keeping modules/KEV/rules siblings.
- Hero tagline calls out '22 of 26 CVEs empirically verified'.
- Meta description + og:description updated.
- Bento card 'Verifier ready' rewritten as '22 modules empirically
verified' with concrete distro/kernel breakdown; styled with new
.bento-vfy class for emerald accent (matches the stat chip).
- Timeline 'shipped' column adds the verifier wins; 'in flight'
swapped to current open items (drift fixes, packagekit provisioner,
custom <=4.4 box for dirty_cow).
docs/og.svg + docs/og.png
- 4-chip stats row instead of 3: 31 modules · 22 ✓ VM-verified · 10
★ in CISA KEV · 119 detection rules. Tagline now '22 of 26 CVEs
verified in real Linux VMs.' Re-rendered to PNG via rsvg-convert.
skeletonkey.c (binary)
- --list footer now prints '31 modules registered · 10 in CISA KEV
(★) · 22 empirically verified in real VMs (✓)'. Counts computed
from the registry + cve_metadata + verifications tables at runtime
(so it stays accurate as more verifications land — the JSONL
refresh propagates automatically).
No code logic changed; only surfacing.
One command that answers 'should we worry about this CVE here,
what would patch it, and what would the SOC see if someone tried
it'. Renders, for the specified module:
- Header: name + CVE + summary
- WEAKNESS: CWE id and MITRE ATT&CK technique (from CVE metadata)
- THREAT INTEL: CISA KEV status (with date_added if listed) and
the upstream-curated kernel_range
- HOST FINGERPRINT: kernel + arch + distro from ctx->host plus
every relevant capability gate (userns / apparmor / selinux /
lockdown)
- DETECT() TRACE (live): runs the module's detect() with verbose
stderr enabled so the operator sees the gates fire in real
time — 'kernel X is patched', 'userns blocked by AppArmor',
'no readable setuid binary', etc.
- VERDICT: the result_t with a one-line operator interpretation
that varies by outcome (OK / VULNERABLE / PRECOND_FAIL /
TEST_ERROR each get their own framing)
- OPSEC FOOTPRINT: word-wrapped .opsec_notes paragraph (from
last commit) showing what an exploit would leave behind on
this host
- DETECTION COVERAGE: which of auditd/sigma/yara/falco have
embedded rules for this module, with pointers to the
--module-info / --detect-rules commands that dump the bodies
Targeted at every audience the project is meant to serve:
- Red team: opsec footprint + 'would this even reach' verdict
in one screen
- Blue team: paste-ready triage ticket with CVE / CWE / ATT&CK /
KEV header and detection-coverage matrix
- Researchers: the live trace shows the reasoning chain
(predates check, kernel_range_is_patched lookup, userns gate)
that drove the verdict — auditable without reading source
- SOC analysts / students: a single self-contained briefing per
CVE, no cross-referencing needed
Implementation:
- New mode MODE_EXPLAIN, new flag --explain MODULE
- cmd_explain() composes the page from the existing module
struct, cve_metadata_lookup() (federal-source triage data),
ctx->host (cached fingerprint), and a live detect() call
- print_wrapped() helper word-wraps the long .opsec_notes
paragraph at 76 cols / 2-space indent
- Help text + README quickstart + DETECTION_PLAYBOOK single-host
recipe all updated to mention --explain
Smoke tests:
- macOS: --explain nf_tables shows full briefing; trace says
'Linux-only module — not applicable here'; verdict
PRECOND_FAIL with the generic-precondition interpretation
- Linux (docker gcc:latest): --explain nf_tables on a 6.12 host
fires '[+] nf_tables: kernel 6.12.76-linuxkit is patched';
verdict OK with the 'this host is patched' interpretation
- Both: --explain nope (unknown module) returns 1 with a clear
'no module ... Try --list' error
- Both: 87 tests still pass (33 kernel_range + 54 detect on Linux,
33 + 0 stubbed on macOS)
Closes the metadata + opsec + explain trio. The three together
answer the 'best tool for red team, blue team, researchers, and
more' framing.
Standalone Python script that pulls Debian's security-tracker JSON
and compares each module's hardcoded kernel_patched_from table
against the fixed-versions Debian actually ships. Surfaces real
drift the no-fabrication rule needs us to fix:
MISSING — Debian has a fix on a kernel branch we have no entry
for. Module's detect() would say VULNERABLE on a host
that's actually patched.
TOO_TIGHT — Our threshold is later than Debian's earliest fix on
the same branch. Module would call a patched host
VULNERABLE. False-positive on production fleets.
INFO — Our threshold is earlier than Debian's. We're more
permissive; usually fine (we tracked a different
upstream-stable cut), but flagged for review.
Three output modes:
default (text) — human-readable report on stderr
--json — machine-readable for CI / dashboards
--patch — unified-diff-style proposed C-source edits
--refresh — bypass the 12h cache TTL and re-fetch
Implementation:
- urllib (no pip deps) fetches the ~70MB tracker JSON.
- Cached at /tmp/skeletonkey-debian-tracker.json with 12h TTL.
- Parses every modules/*/skeletonkey_modules.c for the .cve = '...'
field + the kernel_patched_from <name>[] = { {M,m,p}, ... } array.
- Per CVE, builds {debian_release -> upstream_version_tuple} from
the tracker's 'releases.*.fixed_version' field (stripping Debian
-N / +bN / ~bpoN suffixes to recover the upstream version).
- Groups by (major, minor) branch; flags MISSING / TOO_TIGHT / INFO.
- Exits non-zero when MISSING or TOO_TIGHT findings exist (suitable
for a CI 'detect-drift' job).
First-run output found drift in 17 of 20 modules with kernel_range
tables — operator-reviewable. NOT auto-applied; this commit only
ships the diagnostic tool, not the suggested fixes.
README's Contributing section now points at the tool.
This release captures the session's reliability + accuracy work
on top of v0.5.0:
- Shared host fingerprint (core/host.{h,c}): kernel/distro/userns
gates / sudo + polkit versions, populated once at startup; every
module consults ctx->host instead of doing its own probes.
- Test harness (tests/test_detect.c, make test): 44 unit tests over
mocked host fingerprints, wired into CI as a non-root step.
- --auto upgrades: auto-enables --active, per-detect 15s timeout,
fork-isolated detect + exploit so a crashing module can't tear
down the dispatcher, per-module verdict table + scan summary.
- --dry-run flag (preview without firing; --i-know not required).
- Pinned mainline fix commits for the 3 ported modules
(dirtydecrypt / fragnesia / pack2theroot) — detect() is now
version-pinned with kernel_range tables, not precondition-only.
- New modules: dirtydecrypt (CVE-2026-31635), fragnesia
(CVE-2026-46300), pack2theroot (CVE-2026-41651).
- macOS dev build works for the first time (all Linux-only code
wrapped in #ifdef __linux__).
- docs/JSON_SCHEMA.md: stable consumer contract for --scan --json.
Version bump:
- SKELETONKEY_VERSION = '0.6.0' in skeletonkey.c
- README status line updated with the v0.6.0 changelog
- docs/JSON_SCHEMA.md example refreshed
Adds docs/JSON_SCHEMA.md documenting the shape and stability promises
of the JSON document --scan --json emits on stdout. The schema is
already what the binary produces — this commit pins the contract so
fleet-scan / SIEM consumers can rely on it across releases.
What it covers:
- Top-level object: { version, modules } and field stability.
- Per-module entry: { name, cve, result } with type + stability.
- The 6-value result enum (OK / TEST_ERROR / VULNERABLE /
EXPLOIT_FAIL / PRECOND_FAIL / EXPLOIT_OK) and what each means
semantically.
- Process exit-code semantics for --scan (worst observed result
becomes the exit code — lets a SIEM treat the binary exit as a
single-host alert level).
- Bash + jq one-liners for the common fleet-roll-up patterns.
- A recommended Python consumer pattern with the forward-compat
guidance (ignore unknown fields, treat unknown result strings as
TEST_ERROR-equivalent).
- Explicit stability promises: which fields cannot change without
a major-version bump, what may be added in future minor
releases, what consumers MUST tolerate.
Verified against the live binary: --scan --json produces exactly
the documented shape (top-level keys {modules, version}; per-module
keys {cve, name, result}; result values come from the documented
enum). 31 modules / 30 unique CVEs at v0.5.0.
README's 'Sysadmins' audience row now links the schema doc:
'JSON output for CI gates ([schema](docs/JSON_SCHEMA.md))'.
Both modules' detect() was precondition-only because we didn't know the
mainline fix commits at port time. Debian's security tracker now
provides them — pinning here turns detect() into a proper version-
based verdict (still with --active for empirical override).
dirtydecrypt (CVE-2026-31635):
- Fix commit a2567217ade970ecc458144b6be469bc015b23e5 in mainline 7.0
('rxrpc: fix oversized RESPONSE authenticator length check').
- Debian tracker confirms older stable branches (5.10 / 6.1 / 6.12) as
<not-affected, vulnerable code not present>: the rxgk RESPONSE-
handling code was added in 7.0.
- kernel_range table: { {7, 0, 0} }
- detect() pre-checks 'kernel < 7.0 -> SKELETONKEY_OK (predates)' then
consults the table. With --active, the /tmp sentinel probe overrides
empirically (catches pre-fix 7.0-rc kernels the version check
reports as patched).
fragnesia (CVE-2026-46300):
- Fix in mainline 7.0.9 per Debian tracker ('linux unstable: 7.0.9-1
fixed'). Older Debian-stable branches (bullseye 5.10 / bookworm 6.1
/ trixie 6.12) are still marked vulnerable as of 2026-05-22 - no
backports yet.
- kernel_range table: { {7, 0, 9} }
- detect() keeps the userns + carrier preconditions, then consults
the table: 7.0.9+ -> OK; older branches without an explicit backport
entry -> VULNERABLE (version-only). --active confirms empirically.
- Table is intentionally minimal so distros that DO backport in the
future flow into 'patched' once their branch lands an entry; until
then, the conservative VULNERABLE verdict on unfixed branches is
correct.
Other changes:
- module struct .kernel_range strings updated from 'fix commit not
yet pinned' to the actual pinned-version prose.
- module_safety_rank bumped 86 -> 87 for both modules (version-pinned
detect is now real; still below the verified copy_fail family at
88 so --auto prefers verified modules when both apply).
- Both modules now #include core/kernel_range.h inside their
#ifdef __linux__ block.
- MODULE.md verification-status sections rewritten: detect() is now
version-pinned; only the exploit body remains unverified.
- CVES.md note + inventory rows updated: dropped the 'precondition-
only' language for the pair; all three ported modules now have
pinned fix references.
- README ⚪ tier description + module list aligned to the new state.
Both detect()s smoke-tested in docker gcc:latest on kernel 6.12.76-
linuxkit: dirtydecrypt correctly reports OK ('predates the rxgk code
added in 7.0'); fragnesia + pack2theroot correctly report
PRECOND_FAIL (no userns / no D-Bus in container). Local macOS + Linux
builds both clean.
Adds the third ported module — Pack2TheRoot, a userspace PackageKit
D-Bus TOCTOU LPE — and spends real effort hardening --auto so its
detect step gives an accurate, robust verdict before deploying.
pack2theroot (CVE-2026-41651):
- Ported from the public Vozec PoC
(github.com/Vozec/CVE-2026-41651). Original disclosure by the
Deutsche Telekom security team.
- Two back-to-back InstallFiles D-Bus calls (SIMULATE then NONE)
overwrite the cached transaction flags between polkit auth and
dispatch. GLib priority ordering makes the overwrite deterministic,
not a timing race; postinst of the malicious .deb drops a SUID bash
in /tmp.
- detect() reads PackageKit's VersionMajor/Minor/Micro directly over
D-Bus and compares against the pinned fix release 1.3.5 (commit
76cfb675). This is a high-confidence verdict, not precondition-only.
- Debian-family only (PoC builds its own .deb in pure C; ar/ustar/
gzip-stored inline). Cleanup removes /tmp .debs + best-effort
unlinks /tmp/.suid_bash + sudo -n dpkg -r the staging packages.
- Adds an optional GLib/GIO build dependency. The top-level Makefile
autodetects via `pkg-config gio-2.0`; when absent the module
compiles as a stub returning PRECOND_FAIL.
- Embedded auditd + sigma rules cover the file-side footprint
(/tmp/.suid_bash, /tmp/.pk-*.deb, non-root dpkg/apt execve).
--auto accuracy improvements:
- Auto-enables --active before the scan. Per-module sentinel probes
(page-cache /tmp files, fork-isolated namespace mounts) turn
version-only checks into definitive verdicts, so silent distro
backports don't fool the scan and --auto won't pick blind on
TEST_ERROR.
- Per-module verdict printing — every module's result is shown
(VULNERABLE / patched / precondition / indeterminate), not just
VULNERABLE rows. Operator sees the full picture.
- Scan-end summary line: "N vulnerable, M patched/n.a., K
precondition-fail, L indeterminate" with a separate callout when
modules crashed.
- Distro fingerprint added to the auto banner (ID + VERSION_ID from
/etc/os-release alongside kernel/arch).
- Fork-isolated detect() — each detector runs in a child process so
a SIGILL/SIGSEGV in one module's probe is contained and the scan
continues. Surfaced live while testing: entrybleed's prefetchnta
KASLR sweep SIGILLs on emulated CPUs (linuxkit on darwin); without
isolation the whole --auto died at module 7 of 31. With isolation
the scan reports "detect() crashed (signal 4) — continuing" and
finishes cleanly.
module_safety_rank additions:
- pack2theroot: 95 (userspace D-Bus TOCTOU; dpkg + /tmp SUID footprint
— clean but heavier than pwnkit's gconv-modules-only path).
- dirtydecrypt / fragnesia: 86 (page-cache writes; one step below the
verified copy_fail/dirty_frag family at 88 to prefer verified
modules when both apply).
Docs:
- README badge / tagline / tier table / ⚪ block / example output /
v0.5.0 status — all updated to "28 verified + 3 ported".
- CVES.md counts line, the ported-modules note (now calling out
pack2theroot's high-confidence detect vs. precondition-only for
the page-cache pair), inventory row, operations table row.
- ROADMAP Phase 7+: pack2theroot moved out of carry-overs into the
"landed (ported, pending VM verification)" group; added a new
"--auto accuracy work" subsection documenting the dispatcher
hardening landed in this commit.
- docs/index.html: scanning-count example bumped to 31, status line
updated to mention 3 ported modules.
Build verification: full `make clean && make` in `docker gcc:latest`
with libglib2.0-dev installed: links into a 31-module skeletonkey
ELF (413KB), `--list` shows all modules including pack2theroot,
`--detect-rules --format=auditd` emits the new pack2theroot section,
`--auto --i-know --no-shell` exercises the new banner + active
probes + verdict table + fork isolation + scan summary end-to-end.
Only build warning is the pre-existing
`-Wunterminated-string-initialization` in dirty_pipe (not introduced
here).
Three-agent rigorous review of the dirtydecrypt + fragnesia ports plus
repo-wide doc consistency, followed by a full Linux build verification.
dirtydecrypt (NOTICE + detection rules):
- NOTICE.md: removed an unsupported "Zellic co-founder" detail and a
fabricated disclosure-date narrative; tightened phrasing of the
Zellic + V12 credit; noted that upstream poc.c carries no
author/license header of its own.
- Embedded auditd + sigma rules and detect/sigma.yml broadened to
cover every binary in dd_targets[] (added /usr/bin/mount,
/usr/bin/passwd, /usr/bin/chsh) and added the b32 splice rule, so
the embedded ruleset matches the on-disk reference and the carrier
list the exploit actually targets.
- Exploit primitive verified byte-for-byte against the V12 PoC
(tiny_elf[] identical, all rxgk/XDR/fire/pagecache_write logic
token-identical). docker gcc:latest compile of the Linux path:
COMPILE_OK, zero warnings.
fragnesia: review found no defects. Exploit primitive byte-identical
to the V12 PoC (shell_elf[] 192 bytes identical, AF_ALG GCM keystream
table + userns/netns/XFRM + receiver/sender/run_trigger_pair all
faithful). The deliberate omissions (ANSI TUI, CLI arg parsing) drop
nothing exploit-critical. docker gcc:latest compile: COMPILE_OK; full
project build links into a working skeletonkey ELF and --list shows
the module registered correctly.
Repo docs (README.md / CVES.md / ROADMAP.md):
- Chose to keep "28 verified" as the headline; the two ported
modules are represented as a separate clearly-labelled tier
("ported-but-unverified") that is explicitly excluded from the
28-module verified counts. README + CVES.md + ROADMAP.md now tell
one consistent story.
- Filled a pre-existing documentation gap: sudo_samedit, sequoia,
sudoedit_editor, vmwgfx were registered + built but absent from
CVES.md's inventory + operations tables. Added rows synthesized
from each module's .cve / .summary / .kernel_range fields.
- ROADMAP Phase 8 "7 🟡 PRIMITIVE modules" → "14"; added a "Landed
since v0.1.0" group; moved vmwgfx out of the stale carry-overs.
docs site (docs/index.html):
- Stat box "28 / total modules" → "28 / verified modules" (the 14+14
breakdown now sums to the headline consistently).
- Terminal example "scanning 28 modules" → "scanning 30 modules"
(was factually wrong — the binary literally prints module_count()
which is 30).
- Status line: updated to mention the 2 ported-but-unverified
modules and mirror the README phrasing.
- docs/LAUNCH.md left as a dated v0.5.0 launch snapshot.
Build verification: `docker run gcc:latest make clean && make` —
links into a 30-module skeletonkey ELF on Linux. macOS dev box still
hits the pre-existing dirty_pipe header gap; unchanged.
.gitignore: added /skeletonkey to exclude the top-level build
artifact (the existing modules/*/skeletonkey only covered per-module
binaries; the root one was getting picked up by `git add -A`).
Module counts were stale: 13 🟢 + 11 🟡 → corrected to 14 🟢 + 14 🟡
(sudoedit_editor is new 🟢; sudo_samedit + sequoia + vmwgfx are
new 🟡 from the v0.5.0 batch).
Added 'Who it's for' table — red team / sysadmin / blue team / CTF
each get a row.
Added 'Corpus at a glance' section with explicit module lists per
tier, replacing the prose paragraph that buried the names.
Tightened Quickstart — removed duplicate one-liner block, single
canonical command set.
Worked example switched from fictional dirty_pipe to the actual
--auto output shape (pwnkit pick on a vulnerable Ubuntu 5.15).
Honest 'Status' framing — acknowledges no empirical end-to-end
validation yet, calls it the next roadmap item. Replaces the
aspirational 'CI-tested across a distro matrix' claim.
Added 'How it works' (was 'Architecture' + 'Build & run' merged
into a clearer flow) and 'The verified-vs-claimed bar' section
explaining why most modules ship without per-kernel offsets.
README.md: badges (release / license / module-count / platform),
sharpened hero stating value prop in one sentence, audience
framing for red team / sysadmin / blue team.
CONTRIBUTING.md (new): what we accept (offsets, modules, detection
rules, bug reports) and what we don't (untested EXPLOIT_OK,
fabricated offsets, 0days, undisclosed CVEs).
docs/LAUNCH.md (new): ~600-word HN/blog launch post. Copy-paste
ready. Explains the verified-vs-claimed bar + --auto + the
operator-populated offset table approach.
GitHub repo description + 11 topics set via gh repo edit so the
repo is discoverable in topic searches (linux-security,
privilege-escalation, cve, redteam, blueteam, etc.).
Previous staircase pattern was just trailing decoration — not real
key teeth. Redesigned the bit as a hanging rectangle with two
clearly-projecting notch-teeth on its right edge (the part that
engages a lock's wards). Switched to box-drawing chars for the bit
since they make sharper notches than 8/b/d glyphs; bow stays
ornate-ASCII style.
Bump 0.4.3 → 0.4.4.
Previous banner had a SKELETONKEY block-letter art that competed
with the skeleton-key drawing for visual attention. Simplified:
the key art is now the focal point, and SKELETONKEY is rendered
as plain spaced text below the drawing.
Slight refinement to the key art: bow is a bit larger (888 instead
of 88) to feel more substantial. Bit/teeth pattern unchanged.
Bump 0.4.2 → 0.4.3.
The v0.4.1 box-drawing key was minimalist — round bow, line shaft,
small bit. Replaced with a more detailed ornate skeleton-key
silhouette in the classic ASCII-art-of-keys tradition:
- Round bow with internal "hole" rendered via stylized 8/b/d/'
pattern (suggests the decorative loop you'd grip)
- Long shaft running right across the banner
- Bit at the end with a staircase notch pattern (the iconic
"key-tooth" descent showing the wards that engage the lock)
Same height as the previous banner. SKELETONKEY block letters
below unchanged.
Bump 0.4.1 → 0.4.2.
Replace the previous "circle + shaft + curl" silhouette (which read
more like a magnifying glass) with a proper skeleton-key anatomy:
- BOW: round decorative loop with center hole (the part you hold)
- SHAFT: long horizontal rod (= the body of the key)
- BIT: notched tooth hanging down from the shaft end (the part
that engages the lock — the iconic key-tooth profile)
Same change in skeletonkey.c BANNER and README.md.
Bump 0.4.0 → 0.4.1.
The whole point of an LPE tool is going from unprivileged to root,
but the Quickstart was leading with `sudo iamroot --scan`. Fix:
- Drop sudo from --scan / --audit / --exploit / --detect-rules.
These work without root (--scan reads /proc + /etc; --audit
walks the FS via stat; --exploit IS the privilege escalation;
--detect-rules emits to stdout).
- Keep sudo only where it's actually needed: --mitigate (writes
/etc/modprobe.d + sysctl) and tee'ing rule files into
/etc/audit/rules.d/.
- Add a worked example showing `id` as uid=1000, then
`iamroot --exploit dirty_pipe --i-know`, then `id` as uid=0.
- Fix the Build & run section's `sudo ./iamroot` too.
iamroot.c: bump IAMROOT_VERSION from 0.1.0-phase1 → 0.1.0
README.md: replace "bootstrap phase" status with v0.1.0 corpus
breakdown (13🟢 / 7🟡 across 2016→2026 timeline)
CVES.md: redefine 🟡 to mean "primitive fires + groom + witness,
stops short of cred-overwrite chain — refuses to claim
root unless empirically demonstrated"; flip 7 entries
from 🔵 → 🟡; add the two missing 🟢 entries
(cgroup_release_agent, overlayfs_setuid); extend the
operations matrix from 7 → 20 rows.
ROADMAP.md: mark all Phase-7 items landed; add Phase 8 covering
full-chain promotions (nf_tables / xtcompat / af_packet
prioritized — each has a public reference exploit;
IAMROOT's no-fabricated-offsets rule means each needs
an env-var offset table or System.map auto-resolve).
Build clean on Debian 6.12.86; iamroot --version reports 0.1.0.
For 'people should say just use iamroot' framing, the install gate is
the single biggest discoverability bottleneck. This commit makes it:
curl -sSL https://github.com/KaraZajac/IAMROOT/releases/latest/download/install.sh | sh
.github/workflows/release.yml:
- Triggers on semver tag push (v*.*.*) + manual dispatch.
- Matrix build for x86_64 (gcc) and arm64 (aarch64-linux-gnu-gcc cross).
- Per-arch sha256sum alongside the binary.
- Auto-generates release notes pointing at CVES.md / ROADMAP.md and
including the install one-liner with the version-specific URL.
- Publishes via softprops/action-gh-release@v2.
install.sh (also uploaded as a release artifact, so the curl|sh
above is stable):
- Detects arch (x86_64 / aarch64 → arm64).
- Pulls iamroot-<arch> + iamroot-<arch>.sha256 from the requested
version (default: latest).
- Verifies sha256 via sha256sum or shasum -a 256.
- Installs to /usr/local/bin/iamroot (or $IAMROOT_PREFIX). Uses sudo
iff /usr/local/bin isn't already writable.
- Prints quickstart hints + ethics pointer at the end.
- Env knobs: IAMROOT_VERSION, IAMROOT_PREFIX, IAMROOT_REPO.
README.md gains a 'Quickstart' section at the top with the four
canonical commands: install, --scan, --audit, --detect-rules,
fleet-scan. Lands the 'curl|bash and go' UX as the first thing
visitors see.