An unofficial Baldur’s Gate 3 expansion built by 140 volunteers and funded by 1,700 patrons, with me as CTO.
Project Type
Unofficial Baldur’s Gate 3 expansion. A volunteer studio in active development, specs under NDA.
Role
CTO and Technical Director. Two roles, one person.
Timeline
Ongoing, across 140+ contributors: writers, artists, engineers, composers and QA.
Skills & Tools
Repository architecture, pipeline engineering, version control on Git LFS, engine implementation in BG3, technical documentation, licensing and governance, and cross-department unblocking.
Path to Menzoberranzan is an active production. Technical architecture, custom toolchain specifications, in-engine system implementations and unreleased content are not discussed here. They live under NDA for good reason. What follows covers publicly announced project scope, publicly available press and screenshots, and the leadership decisions I can speak to without compromising the work of 140+ contributors who trust this project to stay quiet. There is a great deal more I am proud of and cannot yet say.
Menzoberranzan has never existed in a Baldur’s Gate game. So a hundred and forty people are building it for free.
An unofficial expansion for Baldur’s Gate 3, run at studio scale with none of the studio.
The progress reel. The tutorial level, taking form.
Path to Menzoberranzan is a full custom campaign set deep in the Underdark, in the city the franchise was always too cautious to fully render. Original narrative, original art, original audio, original cinematics, original engineering. It is not a mod in the sense of a texture swap or a balance patch. It is a production, with departments, pipelines, documentation, and the kind of technical debt that can silently crater a year of work.
Everyone on it is a volunteer who does not have to care and does anyway. That is the strangest and most sustaining thing about it, and it is also the fact that decides every technical call I make. I came in as CTO and Technical Director , which sounds like one job with two names and is not.
The city, as it currently stands in engine.
140 people. Zero salary. An undocumented engine. A 21 year old holding the infrastructure.
In a funded studio, bad infrastructure costs money. In a volunteer studio, it costs people.
The constraints here do not exist in a paid studio, and they compound. There is no documentation from the engine developer, no on-call support, no ops team, and no way to compel anybody to do anything. The Baldur’s Gate 3 Toolkit exposes a fraction of what the engine actually does. The rest is detective work: pattern recognition, edge cases, anomalies that behave one way on Tuesday and differently on Friday for reasons that take hours to isolate.
Nobody wrote it down
Larian publishes a toolkit. They do not publish how it works. Lighting inheritance, atmosphere resets, story node behaviour, actor transforms. All of it is learned by breaking things until you understand what you broke.
No ops, no on-call
Contributors sit across a dozen time zones on whatever hours they have left. When something breaks at 2am in one country it is a working afternoon in another, and nobody is paid to pick up.
Everything is binary
Models, textures, audio, cinematic timelines. None of it is text, none of it diffs cleanly, and all of it grows without mercy.
No margin for error
A contributor whose work keeps breaking for reasons they do not control does not file a ticket. They quietly stop showing up, and you find out weeks later.
Athkatla from Baldur’s Gate 2, rebuilt in the BG3 engine. Via TheGamer, January 2025.
The repo was 2.5 GB and heading for 80. We had one weekend and a rewrite that could not be undone.
People senior to me said you cannot retrofit Git LFS onto a repo that was not built for it. They were wrong, and then they were very nearly right.
Baldur’s Gate 3 modding is a binary-heavy discipline, and a repository that clones in three minutes becomes one that clones in three hours and then does not clone at all. That is not a developer experience problem. That is a contributor retention problem, and we were about eighteen months from it.
Git LFS keeps large files outside the repository proper. Contributors get a small pointer file in their clone and the real binary arrives on demand. The repo stays light, the workflow stays sane. The catch is that if you did not start that way, adopting it means rewriting every commit that ever touched a binary, and a history rewrite invalidates every open branch and every existing clone on every machine on the project.
Which is why it had to be a jam. Freeze the world on a Friday, rewrite, verify, thaw by Sunday. Before anybody touched anything I wrote fourteen steps, a rollback plan, a troubleshooting guide and an FAQ for the rest of the board: audit everything in history over 10 MB, provision on Hetzner, stand up Forgejo, move the objects to Backblaze B2, freeze the open pull requests, rewrite, verify, communicate, and archive the old instance. It was not a quick decision. It was a surgery plan.
Git, not GitHub
GitHub’s LFS bandwidth pricing does not survive this asset scale. Self-hosting Forgejo on Hetzner is far cheaper for a team mostly in Europe, with fast storage and no per-seat surprises, and it hands us the LFS policy instead of renting it.
Objects in B2
The bytes live in object storage, not on the git box. Storage grows without anybody touching the server, and the bill stays survivable for a project with no revenue and no intention of having any.
Not Perforce
Perforce is what AAA studios use and it is better at binaries than anything else. It also assumes server engineers, an IT department and a licensing budget. Here, ops is one person between lectures. It would have been a beautiful system nobody could maintain.
Then every binary in the project died at once.
The rewrite itself went fine. The migration walked the whole history and replaced every matching blob with a pointer. Then we brought the repo across to the new server the obvious way, a mirror clone, and it reported complete success. Every ref, every commit, every tag, no errors.
This is what a 48 MB character model looked like on every machine that pulled afterwards.
A valid, well formed, three line text file where a character used to be.
A mirror clone copies every ref and every git object, and it does not copy a single LFS object. LFS objects do not live in the git object store. They move over a separate transfer API to a separate backend, and nothing in a mirror push goes near them. The new server had perfect history in which every binary was a valid pointer to something it had never been given.
Nothing errored during the migration. Git reported success at every step, because from git’s point of view it had succeeded. A pointer file is legal content. It only surfaced when the Toolkit tried to load a model and got ASCII.
The read in the room was that the history rewrite had eaten the binaries, and that this was the proof that retroactive LFS does not work on a repo that was not set up for it from the start. It was not. Nothing had been lost. Every object was still sitting in the local LFS cache on the machine that ran the migration, and still on the old instance that step fourteen had told us to archive rather than delete.
Then everyone ran git lfs pull , the working trees hydrated, and the assets came back at full size.
The part worth noticing is not the commands. It is that the recovery was written into the plan before the incident, by somebody who did not know what was going to go wrong and assumed something would. Step fourteen cost nothing on the Friday. On the Sunday it was the entire project.
Clone time, before and after.
Where it was going, and where it went instead.
CTO and Technical Director. Same person. Completely different jobs.
One of them prevents a collapse in six months. The other one prevents a department going idle this afternoon.
This distinction usually gets collapsed into one vague “technical lead” title until something catastrophic happens. We formalised it early because we hit the scale where leaving it implicit was actively dangerous. At 140 contributors, a single ambiguous decision can propagate silently through the pipeline for weeks before it surfaces as months of wasted work.
CTO
“Prevents future collapse.”
Long-term system stability and architecture
Repository structure, LFS rules, versioning conventions
Custom toolchain design and governance
Licensing strategy (PolyForm Shield 1.0.0)
Cross-department dependency mapping
Risk prevention before hazards form
Technical Director
“Prevents present paralysis.”
In-engine implementation, BG3 Toolkit daily
Debugging undocumented engine anomalies in real time
Unblocking cinematics, quests, audio and environments
Inheritance chains, story logic, atmosphere systems
Translating architecture into workflows people can follow
Rapid diagnosis under production pressure
The CTO asks whether this decision hurts us in six months. The Technical Director asks why this cinematic is broken right now and how to fix it before the department goes idle. Neither question can wait for the other. They run in parallel, constantly, across different timescales and different failure modes. Merging them into one technical lead does not simplify the work. It just means both fail quietly until they fail loudly.
Character creation screen concept art, by junjoba, Environment Art Lead.
The technical spine of a 140 person project lived in one person’s working memory.
So I wrote it down, and that document is the most load-bearing thing I have made here.
The visible part of technical leadership is debugging. The dramatic moment where the cinematic is broken and you find the one misaligned atmosphere preset that caused it. That part is real and it takes real skill. The invisible part is the architecture: the documents, the conventions, the tracking rules, the licensing decisions, the pipeline constraints that stop one department’s work silently invalidating another’s over a month.
Hen’s Brain: Technical Leadership Manual exists because institutional knowledge living in one head is not an inconvenience at this scale, it is an existential risk. It defines the two domains, their failure modes, their escalation paths, and which decisions fall unambiguously into each. It is not theory. It is operational infrastructure, and it is written so the project survives illness, burnout, exams and time zones.
The manual, redacted.
What 140 volunteers looks like when you draw it.
Licensing sits in the same category. The custom toolchains are protected under PolyForm Shield 1.0.0, which prevents commercial appropriation of work built by volunteers, for free, for a community. That is a legal architecture decision rather than a creative one, and it is the sort of thing that is cheap to do early and impossible to do late.
There is a whole category of this I am proud of and not allowed to show you.
You get the infrastructure decisions. You do not get the blueprints.
The actual technical architecture, the custom systems, the in-engine implementations that took months to work out: none of that is here. The public face of this project is screenshots and progress updates. The private face is engineering decisions I cannot share, because the project is not finished and the work belongs to a hundred and forty people rather than to me.
That is the right call, and it is a strange thing, to have done work you cannot show. But the discipline of keeping quiet about active development, of understanding that the story gets told when it is done and not while it is happening, is its own kind of skill. You do not perform progress. You make it, quietly, and you wait.
What this project taught me that a classroom couldn’t.
01
Name the domains or watch them collapse
CTO and Technical Director are not titles. They are categorical separations that stop one person failing at two completely different jobs at once, in two completely different timescales.
02
Write the undo before the do
Archive, not delete. The step that rescued the migration cost nothing on the Friday and was the whole project by Sunday. Design the recovery while you are calm and assuming you will be wrong about something.
03
Infrastructure is community
In a volunteer studio, bad tooling does not just slow people down. It drives them away. The repository is the environment everybody lives in, so design it like somewhere people have to want to stay.
04
Right tool, not best tool
Perforce is excellent and it is overkill for a team with no ops staff and no budget. Git, LFS and Forgejo solve the problem we actually have instead of the one in the textbook.
Follow the project
Path to Menzoberranzan posts progress publicly on r/BaldursGate3. Updates are sporadic because the work is hard and nobody is being paid to do it on a schedule. When it ships it will be worth the wait. Until then, the city waits in the dark.
→ Progress thread on r/BaldursGate3
The internet noticed.