The Owner Field Is a Runtime Dependency: What Vim's Succession Taught Open Source
When Bram Moolenaar died in August 2023, Vim did not lose a file. It lost a permission bit. Moolenaar was the sole owner of the vim/vim GitHub organization, and...
The Owner Field Is a Runtime Dependency
When Bram Moolenaar died in August 2023, Vim did not lose a file. It lost a permission bit. Moolenaar was the sole owner of the vim/vim GitHub organization, and on GitHub only the org owner can create roles, set branch protection, and invite maintainers. The code was public and mirrored. The ability to govern it was not.
Christian Brabandt, who had contributed to Vim since 2006, became the main maintainer by circumstance rather than succession plan. His VimConf 2024 keynote describes the first blocking problem precisely: to keep the project moving, someone needed the power to invite other maintainers, and that power was locked behind a single human's account.
GitHub does ship a deceased user policy with pre-designated successors. Brabandt's account of using it is a warning about recovery paths that look complete on paper: invoking the policy deactivates the account, which would have cut off Moolenaar's family from the account they still used. The project instead routed through the family, who manually changed permissions so maintainers could be invited. That worked. It was also undocumented, one-time, and depended on a person being reachable.
Treat this the way you treat any other runtime dependency: if it has no replica, it has no failover.
# Enumerate owners/admins of every org you depend on.
# A single login here is a single point of failure.
gh api "orgs/vim/v2/members?role=admin" --jq '.[].login'
gh api "repos/vim/vim" --jq '{owner: .owner.login, type: .owner.type}'
# type == "User" means the repo is owned by a person, not the org.
The third line is the one that matters. A repository owned by a User account inherits that user's blast radius: their 2FA device, their recovery email, their employer, their lifespan. A repository owned by an Organization inherits the org's. Those are different failure domains, and most projects never choose between them explicitly — the default path just happens to land on whichever account ran git init first.
Your Credentials Outlive Your Code
Brabandt's own slide list of what had to be recovered is longer than the repository. Homepage, domains, SSL certificates, an FTP server, vulnerability-reporting access on Huntr, the charity (ICCF), community management, and the mailing lists. His summary of the situation was blunt: no documented processes.
None of that is code. All of it is load-bearing. A lapsed domain takes the documentation, the release artifacts, and the package-manager redirects with it. A publish token that nobody else holds freezes releases. A vulnerability-report inbox that only one person can read turns a coordinated disclosure window into an uncoordinated one.
- DNS / registrar — one contact email, one renewal card. Expiry is a project-ending event, not a warning.
- Package registry tokens — npm, PyPI, crates.io, Homebrew taps, container registries. A personal API key publishes under a human's identity.
- CI/CD secrets — deploy keys, signing keys, registry credentials held in one person's password manager.
- Communication surfaces — mailing lists, chat admin, the security-report inbox.
- Governance accounts — org owner, billing owner, the account that can delete the org.
You can inventory the machine-readable half of this in a few minutes, which is the part most projects never do:
import socket, ssl, whois # pip install python-whois
def estate_report(domain):
ip = socket.gethostbyname(domain)
cert = ssl.get_server_certificate((domain, 443))
# parse `cert` with `cryptography` for real notBefore/notAfter dates
w = whois.whois(domain)
return {
"domain": domain,
"resolves": ip,
"cert_len": len(cert),
"registrar": w.registrar,
"expires": w.expiration_date, # None here = a bad day is scheduled
"nameservers": w.name_servers,
"admin_email": w.emails,
}
if __name__ == "__main__":
for d in ("vim.org", "neovim.io"):
print(estate_report(d))
The point of the script is not the output. It is that the output is a list of things that currently have exactly one owner, and that list should be reviewed by more than one person.
The Bus Factor Is a Number You Can Measure
Bus factor gets discussed as a feeling. It is arithmetic. The question is not "who knows this code" but "how many accounts can unblock it." Those two numbers diverge constantly, and the second one is the one that pages you.
Start with commit concentration, which is a proxy for where the unblocking power sits:
# Top committers by volume — concentration is the signal, not the leader.
git shortlog -sn --no-merges | head -20
# How many people touched the critical path in the last year?
git log --since="1 year ago" --pretty=%an -- src/ | sort | uniq -c | sort -rn
# Who owns the files that break everything if they rot?
git log --format=format: --name-only --since="2 years ago" \
| grep -v '^$' | sort | uniq -c | sort -rn | head -20
A healthy project does not need a flat distribution. Vim ran for thirty years on a steep curve and shipped a stable editor the whole time. The failure mode is not concentration of expertise — it is concentration of authority. One person writing 60% of the commits is fine. One person holding the only org-owner role, the only publish token, and the only DNS login is a scheduled outage.
Brabandt's talk lists what the project lost beyond test scripts: Moolenaar carried, in his head, the strategy of the project — what belonged in Vim and what did not. That is the unwritten half of a benevolent-dictator model, and it does not survive the person. Applying the same replication guarantees you give a database to a human's decision authority prevents exactly that gap, a point worth reading alongside [Your Best Engineer Is a Single Point of Failure](https://diama.dev/blog/your-best-engineer-is-a-single-point-of-failure).
BDFL Is a Design Choice, Not a Default
Vim's model was a benevolent dictator for life. Neovim's was not, and the difference is measurable in how each project absorbed the same shock. Neovim forked from Vim in 2014 for reasons that read, a decade later, as a governance argument rather than a feature argument.
The original Neovim rationale, still in the project wiki, is explicit: with one person responsible for a large codebase, that person must be conservative about merging, because every merged patch becomes their responsibility. The fork's stated goals were to split maintenance across multiple developers and make contribution cheap. De Arruda's async job-control patch had sat unreviewed; the fork was the response.
The original maintainer of Neovim moved on in 2015. A new lead took over. The project kept shipping. That handoff was uneventful because the model never depended on one account.
Contrast the two failure surfaces:
- BDFL — fast, coherent direction; single point of decision. Continuity requires the dictator to plan their own exit, which is the hardest thing to ask of a founder.
- Maintainer team — slower consensus, more coordination cost; continuity is structural. Anyone can be replaced without a permission fight.
Vim has now effectively migrated from the first model to the second, under duress. Brabandt's summary of where it landed was maintenance mode — and that is a legitimate end state, not a failure. A stable editor with a slow cadence is a product. A stable editor with no one who can cut a release is a dead one.
The Handoff Protocol Nobody Writes Down
Vim's recovery worked because a long-time contributor happened to be active, the family happened to cooperate, and GitHub happened to have a policy. Three contingencies, none guaranteed. A handoff protocol replaces luck with a checklist that is boring to write and cheap to run.
Brabandt's slide list of gaps maps directly onto controls you can put in place before anyone dies:
- Dual org ownership. No organization should have a single owner. The minimum is two, in different time zones, with independent recovery methods. This is the exact gap that blocked Vim for weeks.
- Role-scoped, shared secrets. Publish tokens, DNS credentials, and signing keys live in a shared vault with a break-glass procedure — not in one person's password manager. Access is role-scoped, so a maintainer's laptop compromise does not leak the release signing key.
- A written owner manifest. One file listing every external surface, its owner, and its recovery path. Brabandt's phrase no documented processes is the problem statement; the manifest is the fix.
- Shadow cutover. Once a quarter, a second person performs a real release — tag, build, sign, publish — end to end, with the primary maintainer watching but not touching the keyboard. If they cannot, the manifest is wrong.
- Succession in the governance layer. GitHub's pre-designated successor, registrar transfer-on-death, and a documented org-owner rotation. Test the policy once; the deactivation side effect is exactly the kind of thing you discover at the worst time.
CODEOWNERS is the cheapest of these to start, because it lives in the repository next to the code it protects:
# .github/CODEOWNERS — dual owners on the critical path.
# A single name here is a documentation of a future outage.
/src/ @maintainer-a @maintainer-b
/runtime/ @maintainer-b @maintainer-c
/.github/ @maintainer-a @maintainer-c
# Org-level governance (owner role) is NOT in this file.
# Track it in the owner manifest with a second human named.
The comment is the load-bearing part. CODEOWNERS covers review, not the org owner role, and conflating the two is how a project looks governed while still having a single point of failure one level up.
The Transition Held. Measure It.
The test of any continuity plan is what ships after the shock. Vim 9.1 landed in January 2024, dedicated to Moolenaar. Vim 9.2 landed in February 2026 — more than two years later — with enums, generics, and tuples in Vim9 Script, a linematch diff algorithm, experimental Wayland support, and an XDG Base Directory fix. A team that could not invite maintainers in August 2023 shipped a major release two and a half years later.
That is the measurable outcome of the recovery. It is not a triumphant number — 9.1 to 9.2 is a slow cadence, and the project self-describes as maintenance mode. But "slow and alive" is a different failure class from "frozen and abandoned," and the difference was governance, not engineering skill.
The lesson generalizes past Vim. Your code is the artifact; the owner field, the tokens, and the DNS record are the runtime. Nobody audits the runtime until it stops. Brabandt's keynote is a postmortem of that audit, performed under pressure, one permission bit at a time — and the projects that read it and inventory their own owner surface now will not have to.