Catching Up with mooR: 1.0, GitHub, and the 2.0 Series

The last time I wrote a proper update here, I had cut the v1.0-release branch and was talking about the final run toward mooR 1.0. And then I did that thing I apparently do whenever a project reaches an important milestone: I got busy doing the work and neglected to write about it.

Some of that was ordinary work-and-life stuff. Some of it was time spent on other projects, especially Mica, which has become a substantial project in its own right. But I didn’t stop working on mooR and 1.0 did indeed happen. Since then, 1.0 has received a couple of maintenance releases, while the project has moved its development back to GitHub, consolidated Cowbell and Meadow into the main repository, made the normal deployment path a single server process again, begun a substantial reworking of permissions, and — in parallel — gone headfirst into what has become the 2.0 series.

So this is the overdue catch-up post. It is not really a 1.0 launch announcement, because that ship left the harbour while I was failing to write one. It is more an account of what happened, why 1.0 lost some of its thunder, and where the project is going now.

1.0 did, in fact, happen

mooR 1.0.0 was released on June 5, 2026. That was the end of the long beta and release-candidate cycle and the point where mooR finally had a stable release line intended for people who want to run actual worlds rather than follow every turn of development.

Reaching 1.0 was kind of a big thing because it was supposed to be a splash after basically 3.5 years of work. mooR began in 2022 as a rewrite of LambdaMOO that I thought might take a few months, and instead became a compiler, virtual machine, multithreaded transactional object database, network server, web platform, deployment system, and a growing collection of tools around all of that. The 1.0 release puts a stable boundary around the result: a release branch with a supported database format, compatibility with existing LambdaMOO worlds, and packages and containers that people could use without signing up for whatever experiment happened to be underway on main that week.

There should probably have been a triumphant blog post at the time. There was not. By then I was already distracted by the next round of work, which is perhaps the most on-brand outcome possible for me.

1.0.1 followed on June 14 and corrected a group of permission and LambdaMOO-compatibility problems around object, property, and verb operations. 1.0.2 followed on July 22 with a smaller set of fixes, including an objdef parser stack overflow, incorrect handling of JSON null, and a task double-commit path after a resume timeout.

At the moment, 1.0.2 is the release I recommend for anyone who wants the conservative path and only wants to run an existing core ported from LambdaMOO or ToastStunt (my Cowbell core requires main).

The v1.0-release branch will continue to be the stable line for necessary corrections, while new architecture and language work happens separately on main.

How 1.0 lost its thunder

The slightly ridiculous part is that getting 1.0 out of the door gave me permission to make deeper changes again. Once there was a stable branch that did not have to absorb every idea I wanted to pursue, development on main accelerated almost immediately. The work became substantial enough that calling it 1.1 stopped making sense. main now represents the development path toward mooR 2.0.

Basically I got very tired of delaying necessary renovations for the purpose of getting 1.0 out the door. And just went for it.

This does not mean 1.0 was a false finish line or that it has already become obsolete. The whole point of cutting that line was to allow both things to exist: a dependable server for people running worlds now, and room to improve some of the architecture without pretending those changes are risk-free maintenance updates.

The full changelog for main is already fairly long, and it includes a collection of new builtins, web APIs, deployment changes, and tooling. But the most important 2.0 work is not the length of that list. Several of the central subsystems have been pulled apart and rebuilt in ways that make the server faster and the architecture less tangled.

Also it’s way faster.

Making mooR simple to run again

One of the most important changes in 2.0 is also, in some ways, a return to what people expected in the first place. LambdaMOO and ToastStunt were monoliths: you ran a server process and it contained the pieces required to run your world. That model was familiar, easy to explain, and appropriate for the great majority of MOO installations.

When I designed mooR, I deliberately went in a more complicated direction. I had ideas about scaling some of my own projects across machines, so I separated the daemon, network hosts, and workers into independently deployable processes connected through RPC. There are real advantages to that architecture, and the split-process deployment remains available for installations that actually need it.

But I let those scaling ideas dictate the default experience. Most people arriving at mooR were not trying to build a distributed service. They wanted to start a server, import or create a world, and connect to it. Instead they had to understand a collection of services, how those services talked to one another, and which combination of them they needed. It caused needless confusion for new users and made packaging, configuration, and deployment harder than the common case justified.

So main now has a single-process moor server that runs the runtime, telnet host, and web host together, with the option to run the curl worker in-process as well. For most users, one backend process is the whole deployment. Internally, mooR still keeps useful boundaries between those components, and the distributed mode has not gone away, but you no longer have to adopt that complexity just to get started.

This is probably the biggest practical improvement in the 2.0 series. The performance and database work matter, but so does making mooR feel like the thing people thought they were downloading: a MOO server you can simply run.

Moving beyond the wizard footgun

Classic MOO permissions are, frankly, not very good. The ownership-and-ACL model around objects, properties, and verbs is coarse, and privileged code has often dealt with its limits by making a task a wizard. That works, but it also gives the task blanket authority over the entire world. Code that only needed permission to do one specific thing was handed permission to do essentially anything.

That model is full of footguns, but mooR cannot simply remove it. Compatibility with LambdaMOO worlds and existing cores means the classic permission flags, ownership rules, and wizard checks have to keep working. The 2.0 work therefore adds a more granular capability system alongside them and provides a path for new and updated core code to stop depending on blanket privilege.

Capabilities are attached to a task independently of the principal the task is acting as. Trusted policy code can authorize a particular operation and let the task continue as an ordinary player, carrying only the abilities it was explicitly given: perhaps permission to write a particular object or property, program or call a verb, move or recycle an object, or invoke a selected builtin. The task does not have to become a wizard, and the grant does not quietly authorize unrelated work.

This is a fairly drastic reworking of how privileged operations can be structured in mooR, and it is still underway. The old ACL model remains as the compatibility foundation, but core authors can build narrower policy boundaries on top of it and progressively move sensitive code away from all-or-nothing wizard authority. That should make worlds easier to secure, easier to reason about, and much less dependent on carefully avoiding the sharp edges of a forty-year-old permissions model. To see it in action, read the Cowbell sources.

Separating the VM from the kernel

The interpreter machinery has been extracted from moor-kernel into a dedicated moor-vm crate. The VM now contains the activation, frame, opcode, stack, unwind, and execution-state machinery, while interaction with the surrounding world goes through a much narrower callback boundary. Just code structure stuff.

Reworking the scheduler

The task scheduler used to route lifecycle operations through a single-threaded, channel-driven event loop. That architecture was understandable and safe, but it also meant that concurrent work kept passing through an unnecessary serialization point, with one-shot channels and allocations along the way.

The 2.0 scheduler coordinates directly over shared task-lifecycle state instead. This removes a surprising amount of machinery from common scheduling operations and makes the path between a running task and the scheduler much more direct. The practical result is lower overhead and less contention. As a result while 1.0 was fairly fast under concurrent load; 2.0 is faster.

Making concurrent commits less pessimistic

There has also been another substantial round of work in the database commit path. mooR uses optimistic serializable transactions, which means concurrent tasks are allowed to proceed and the database has to determine at commit time whether their work actually conflicts. The difficult part is avoiding both incorrect merges and unnecessary retries.

Transactions on main can now rebase when intervening commits touched different relations, and in many cases when they touched different keys in the same relation. Bloom-filter-based overlap checks let the database skip expensive conflict work when the relevant keys clearly do not intersect, and lock-free tombstone tracking removes another source of contention from the read path.

The consequence is not a flashy new feature. It is that independent work is more likely to remain independent all the way through commit, which means less false conflict, less repeated execution, and better scaling under actual concurrent load.

Replacing the compiler frontend

Finally, the old Pest-based parser has been replaced by a hand-written Pratt parser. The new compiler frontend is substantially faster, gives us much more direct control over precedence and syntax, and removes a large layer of indirection from a part of the system I spend a great deal of time working on.

This also provided a useful point to simplify the language configuration. Lexical scopes and list and range comprehensions are now baseline mooR language behaviour rather than features that can be selectively disabled. The compiler still has to understand older programs and databases, but 2.0 is less interested in carrying permanent switches for every intermediate stage of mooR’s development.

There is plenty of other work on main: batch world-state operations, indexed object queries, structural value diff and hash tools, and server-side graph and grid algorithms that Cowbell and game-like worlds can use without reinventing them in slow MOO loops. But those are better covered in their own posts, or by the changelog, than by turning this one into a wall of release notes.

We moved back to GitHub

The other large piece of news is that mooR, Cowbell, Meadow, and the related tools are now together under github.com/timbran-project/moor. GitHub is once again the source of truth for the code, issues, pull requests, and releases.

There are two reasons for this, one practical and one political.

The practical reason is that Codeberg was simply too unreliable for what the project needed. I (mostly) like Forgejo, and I wanted Codeberg to work as the project’s home, but too many ordinary development and release tasks were being built around a service I could not count on being responsive or available. At some point the values of a forge stop compensating for the amount of operational uncertainty it adds to a project.

The political reason is more awkward, because on a great many things I agree with Codeberg. I support free software and strong copyleft. mooR is AGPL for a reason. I share many of Codeberg’s objections to the direction of the commercial software industry, and I still despise a great deal about GitHub: its centralization, its ownership by Microsoft, and Microsoft’s behaviour around Copilot and the work of free-software developers were part of why I moved away in the first place. And I felt very strongly about getting off American infrastructure given current affairs and my own nationality.

Moving back should not be read as an endorsement or a sudden change of heart about what GitHub is.

But Codeberg has been moving toward a strongly dogmatic anti-AI position that leaves very little room for nuance. The immediate trigger was a policy proposal and member discussion about excluding projects characterized as mostly generated by AI tools. The surrounding public discussion made the intended direction, and the hostility toward projects on the other side of that line, difficult to ignore.

There are real ethical, environmental, labour, licensing, and quality problems around generative AI. I do not think skepticism about it is irrational, and I do not think every use of these systems is automatically good. But collapsing all of those questions into a test of ideological purity is not a useful contribution policy, and it is incompatible with how these projects are developed.

mooR was hand-written for roughly three years before the current generation of coding agents existed. Its architecture and direction did not fall out of a prompt. At the same time, I now use agentic tools constantly, as do other contributors. They help implement functionality, investigate bugs, write tests, work through unfamiliar parts of the repository, and maintain documentation. I also reject plenty of what they produce, redirect them when they misunderstand the architecture, and remain responsible for every change I accept.

That for me is the boundary that makes sense here: people own what they submit. Changes are reviewed and tested on their technical merits. AI-assisted work is allowed, but an agent is not an authoritative source and “the tool wrote it” is not an excuse for code that is wrong, unmaintainable, unlicensed, or unexplained. That model requires judgment and accountability. A blanket host-level position against projects that make substantial use of these tools does not leave room for it.

It’s quite possible I would not run afoul of Codeberg’s policies at all. But I found the tone of the public response when people probed these policy questions questionable.

So I moved the project. I would prefer a world in which the obvious home for a free-software project were a reliable, free-software-oriented forge with room for this kind of nuance. At present, for this project, GitHub is the workable choice. Not because I love it (“SlopHub” indeed) but because it’s where people expect it to be. Looking back a year it was probably a mistake to move.

The monorepo returns

The move also gave me an opportunity to reverse another decision I made a few months ago: splitting Cowbell (our next-generation mooR-specific core) and Meadow (our web front end) out into separate repositories. They now live alongside the server again, with Cowbell under cores/ and the React and Flutter Meadow clients under clients/.

SpongeBob looking extremely pleased about the return of the monorepo

The split had a reasonable motivation at the time. mooR was approaching 1.0, while Cowbell and Meadow were moving at their own pace. I did not want a client release, or a change to the core, to be coupled to the server’s release schedule. Separate repositories seemed like the clean way to enforce those boundaries and let each project version itself independently.

In practice it mostly added complexity. A change that crossed the server API, web SDK, Meadow, and Cowbell became a collection of coordinated commits and branch updates. CI had to fetch or publish the right versions of things in the right order. Local setup needed scripts to pull the other repositories into place. Packaging and deployment needed to reconstruct a compatible set of components that had already been developed together but were now pretending not to know about each other.

This was also slightly perverse because I am generally a fan of monorepos. These are not unrelated products that happen to share an organization name. Cowbell is where new mooR capabilities get used for reals, Meadow is the primary interface through which people experience those capabilities, and the SDK and schemas between them change with the server. Being able to make one atomic change across those boundaries is much simpler than manufacturing independence where it does not really exist.

Putting everything in one repository does not mean forcing everything onto one release cadence. Path-specific CI, component tags, and release workflows can provide that separation without making development and integration somebody else’s problem.

Things are substantially simpler as a result of this. CI can test the combinations that are actually in the tree, packaging can build the server and the matching Meadow assets without fetching a graph of external repositories, and deployment scripts have one source checkout to reason about. And changes that genuinely span the engine, core, and clients can land together or fail together.

So… the repository split was a mistake. My concern behind it was legit, but separate repositories were the wrong mechanism for addressing it. Monorepo it is.

Where 2.0 stands

main is now explicitly the 2.0 development line. That label deserves a caveat: it is a moving target and does not carry the same compatibility promise as 1.0.2. There may be further changes to internal APIs, deployment layouts, and language behaviour before a 2.0 release is tagged.

In any case, it’s already relatively stable in daily use, and Cowbell development exercises it continuously. If you want to build against the newer architecture, work on Cowbell, test the new APIs, or contribute to where mooR is going, main is the branch to use.

The choice is therefore fairly simple. Use 1.0.2 or v1.0-release if you want the conservative server line and plan on using LambdaCore or a variant of it. Use main if you want the 2.0 development path, want to use Cowbell and are comfortable following it as it evolves.

The project is in a slightly odd but healthy position. 1.0 exists and is being maintained. 2.0 is already delivering improvements that I did not want to hold back behind the appearance of a finished milestone. The repository move is complete. And, despite the lack of blog posts, the work has continued at roughly the pace anyone who knows me should probably have expected.

If you want to help, try the stable release, run main, bring an existing world over, build something with Cowbell, or file a bug with a real reproduction. You can see the current system in action at Timbran Hotel, our public demonstrator MOO. The development changelog has the full technical list, and the Discord community remains the best place to talk through what you are building.

And at some point I should write a proper update about Mica, too.

← Back to Blog