Architecture

DjangoMOO’s overarching goals are similar to LambdaMOO’s — a persistent, multi-user, programmable text world — but the implementation is independent and the architectural choices are modern:

  • 100% Python. Both the runtime and the in-game programming language are Python; no MOO bytecode interpreter, no DSL.

  • Django-backed. The ORM, admin, auth, and migration tooling do what would otherwise be hand-rolled.

  • Celery-backed. Verb execution, scheduled tasks, and command parsing all run as Celery tasks, with standard back-pressure and retry semantics. Many deployment targets are supported.

  • Sandboxed. Verb code runs inside Zope’s RestrictedPython, inside a Celery worker process, inside a Django atomic transaction, with a hard time limit on every invocation.

The result is a system that’s highly available (any component can be replicated), horizontally and vertically scalable, and easy to develop on (standard Django/Celery conventions throughout). Docker Compose handles local dev; the same images run in Kubernetes via the Helm chart in extras/helm/.

Antioch — django-moo’s predecessor

A lot of the architecture decisions and proofs of concept for django-moo come from lessons learned building antioch, which started in 1999–2000 — long before Django, modern web frameworks, or robust async tooling existed. Antioch’s hand-rolled equivalents of those layers became more painful to maintain over time, and led to a few operating principles for django-moo:

  • For a niche project, lean on third-party libraries wherever they reduce surface area.

  • Don’t try to compete with modern graphical games; the constraints of text are a storytelling feature, not a limitation.

  • Plan for testability from day one.

Components

        ┌───────────────────────────────────────────────────────────┐
        │  Web (uWSGI)                                              │
        │  - Django admin (wizard ops)                              │
        │  - WebSSH (browser terminal)                              │
        │  - django-allauth registration flow                       │
        └─────────────────────────┬─────────────────────────────────┘
                                  │
        ┌─────────────────────────┴─────────────────────────────────┐
        │  PostgreSQL                                               │
        │  - canonical state (Object, Verb, Property, ACL, Player)  │
        │  - migrations, admin tables                               │
        └─────────────────────────┬─────────────────────────────────┘
                                  │
        ┌─────────────────────────┴─────────────────────────────────┐
        │  Redis                                                    │
        │  - Tier-2 cross-session cache (verb/property lookups)     │
        │  - Django session storage                                 │
        │  - Celery broker (task queue)                             │
        │  - Kombu exchanges (async tells, OOB events)              │
        └─────────────────────────┬─────────────────────────────────┘
                                  │
        ┌─────────────────────────┴─────────────────────────────────┐
        │  Celery workers                                           │
        │  - parse_command / parse_code / invoke_verb               │
        │  - RestrictedPython sandbox + atomic txn + time limit     │
        └─────────────────────────┬─────────────────────────────────┘
                                  │
        ┌─────────────────────────┴─────────────────────────────────┐
        │  AsyncSSH server (port 8022)                              │
        │  - prompt-toolkit TUI (rich mode)                         │
        │  - line-based output (raw mode for MUD clients)           │
        │  - IAC subnegotiation (GMCP/MTTS/MSSP/EOR/CHARSET/MSP)    │
        └───────────────────────────────────────────────────────────┘

Front-end

The primary interface is SSH — connect with any client to port 8022, authenticate by password or registered SSH key (see SSH Key Management), and you get a prompt-toolkit TUI driven by moo/shell/prompt.py. For users who can’t or don’t want to install an SSH client, WebSSH at port 443 provides a browser terminal that bridges to the same SSH listener.

The web port also serves the Django admin (for wizard-level operations) and a registration flow built on django-allauth. The signup form collects standard credentials plus the MOO-specific fields (character name, gender, optional description) and creates a Player row linking the Django user to a freshly-created avatar Object. Templates live under moo/shell/templates/.

For full client/protocol details (rich vs. raw mode, OSC 133, the two coroutines, the Kombu bus), see Inside the Shell Client.

Back-end

A Django management command launches the SSH server (moo_shell), which uses AsyncSSH to accept connections and dispatch each session to a MooPromptToolkitSSHSession. The session embeds a prompt-toolkit Application for the rich TUI; the in-app Python REPL for wizards is ptpython-flavoured.

The web app is served by uWSGI (extras/uwsgi/uwsgi.ini), which also serves Django static files and routes registration / admin requests.

Workers and execution

Every command and verb invocation is a Celery task. This gives:

  • Process isolation — a memory race or runaway loop in a worker cannot affect other concurrent invocations. Workers can be bounded by the OS.

  • Atomic transactions — every task body runs inside transaction.atomic(). Uncaught exceptions roll back the entire task’s database changes.

  • Hard time limit — read from the CELERY_TASK_TIME_LIMIT environment variable in moo/celeryconfig.py (default 3 seconds). When the limit elapses, Celery terminates the worker. Synchronous calls to other verbs share the budget; the time-aware continuation pattern in Advanced Verb Patterns hands work off to a fresh task before the limit hits.

  • Sandbox — Zope’s RestrictedPython compiles verb source under guards for attribute access, item access, builtins, and imports. See Why the Verb Sandbox Exists for the model and Verb Sandbox Security Reference for the full enforcement detail.

In development, a Celery Beat scheduler runs in-process to drive periodic tasks. Production typically separates beat into its own deployment.

Storage

Game state lives in three tiers:

  • PostgreSQL is the source of truth — Objects, Verbs, Properties, ACLs, Players, Mail rows, ancestry cache.

  • Redis holds session storage, the Celery task queue, the per-session Kombu queues for async tells and OOB events, and the Tier-2 attribute cache for verb/property lookups (verb dispatch hits PostgreSQL only on a cache miss).

  • An in-process session cache inside each ContextManager scope is the Tier-1 hot path; repeat reads of the same property within one command are free.

The full architecture, including the AncestorCache denormalised table that replaces recursive CTEs, is documented in Caching.

Out-of-band protocols

For MUD-client compatibility, django-moo speaks GMCP, MTTS/TTYPE, MSSP, GA/EOR, CHARSET, and MSP over the SSH transport. IAC byte sequences pass through SSH transparently; the IAC parser/encoder/ negotiator lives in moo/shell/iac.py. The sshelnet bridge translates plain telnet to SSH for clients that lack native SSH support, so no second listener is needed.

The full protocol stack and which capabilities verb code can emit (via moo.sdk.send_gmcp, moo.sdk.play_sound, etc.) are documented in Accessibility and MUD Client Compatibility.

Where things live

Module

Responsibility

moo.core.models

Django models (Object, Verb, Property, ACL, Player, Mail).

moo.core.code

RestrictedPython compilation, ContextManager, sandbox guards.

moo.core.parse

Lexer + Parser; verb dispatch.

moo.core.tasks

Celery task definitions (parse_command, parse_code, invoke_verb).

moo.shell

AsyncSSH server, prompt-toolkit shell, Kombu consumer, IAC layer.

moo.bootstrap

Dataset initialisation; default/ package and verb files.

moo.sdk

Public verb-author API — exclusive import target for verb code.

moo.settings

Django settings split (base.py, dev.py, local.py, test.py).

extras/helm

Kubernetes deployment chart.

extras/mudlet

Mudlet client package (mapper bridge, SSH launcher).

See also