The engine that gets out of your way.
Tapestry's C# core ships only the primitives a MUD actually needs — entities, events, command routing, a scripting bridge — and leaves the game to packs. Everything is replaceable. Nothing is locked in.
Architecture #
Three primitives carry the whole engine: an entity with components, an event bus that fans out asynchronously, and a command router that turns text into intent. Everything else is a pack.
Entity
Event Bus
Command Router
GMCP & the three-tier accessibility model #
Tapestry treats GMCP as a first-class output channel — not an afterthought. Every system describes its state through a typed schema, and the engine ships three presentation tiers so screen readers, terminal clients, and rich web clients all get appropriate fidelity from the same source of truth.
Plain text
Narrative prose. Always present. Telnet works without negotiation; screen readers get a clean stream.
Structured GMCP
Typed events like Char.Vitals and Room.Info — enough for clients to render panels and a11y feedback.
Rich payload
Optional extended schemas: ASCII maps, inventory diffs, dialog trees. Clients negotiate what they want.
Connections & identity #
The engine ships a built-in account system with email-based identity. Players register once and their session state — character, location, inventory — survives disconnects.
Link-dead reconnection handles the most common MUD frustration gracefully. When a player's connection drops, the engine holds their session for a configurable grace period. On reconnect, they resume silently with no loss of state. The grace period, idle timeout, and reconnect behavior are all tunable in server.yaml.
Pre-auth is opt-in per world. Disable it and players connect directly via telnet or the web client with no account required.
Packs & scripting #
A pack is a manifest plus a folder of YAML and JavaScript. Content is declarative; scripting is reserved for behaviour. The Jint runtime sandboxes pack scripts and exposes a curated API for the engine primitives.
Why Jint?
We chose Jint over a custom DSL or embedded Lua so pack authors can use the tooling they already know — TypeScript types, editor autocomplete, npm-style linters. Scripts run in-process but sandboxed: no filesystem, no network unless the manifest declares it.
Install modes #
Three ways to run the engine. Pick what matches your operational shape.
For most worlds
A docker-compose.yml ships with the repo. Mounts your world directory, zero host .NET install needed.
Direct run
Clone the repo and run with .NET 10 SDK. Fastest iteration loop for contributors and pack authors.
Package manager
The tapestry CLI manages the full project lifecycle: init, install, start, publish, and engine management.
Repo & contribution #
The engine lives on GitHub. We practice spec-driven development: new features start with a written spec and a discussion window before any code lands. We label issues good first issue for newcomers.