Architecture

Technical architecture and system design of PipeWorks MUD Server.

Overview

PipeWorks MUD Server uses a modern three-tier architecture:

  • FastAPI backend - RESTful API server

  • Gradio frontend - Web-based client interface

  • SQLite database - Persistent data storage

All components are written in Python 3.12+ using modern best practices.

Three-Tier Design

┌─────────────────────────────────────────────────────────────┐
│                    Gradio Web Interface                      │
│                     (Client Layer)                           │
│              http://localhost:7860                           │
└────────────────────────┬────────────────────────────────────┘
                         │ HTTP/HTTPS
                         ▼
┌─────────────────────────────────────────────────────────────┐
│                    FastAPI REST API                          │
│                    (Server Layer)                            │
│              http://localhost:8000                           │
└────────────────────────┬────────────────────────────────────┘
                         │
        ┌────────────────┴────────────────┐
        ▼                                 ▼
┌──────────────────┐           ┌──────────────────┐
│  Game Engine     │           │  SQLite Database │
│  (Core Layer)    │◄──────────┤  (Persistence)   │
│                  │           │                  │
│ - World/Rooms    │           │ - Players        │
│ - Items          │           │ - Sessions       │
│ - Actions        │           │ - Chat Messages  │
└──────────────────┘           └──────────────────┘

Modular Client Architecture

The Gradio client uses a fully modular design:

src/mud_server/client/
├── app.py                    # Main entry point (~180 lines)
├── api/                      # API client layer
│   ├── base.py              # BaseAPIClient - common HTTP patterns
│   ├── auth.py              # Authentication operations
│   ├── game.py              # Game operations
│   ├── settings.py          # Settings and server control
│   ├── admin.py             # Admin operations
│   └── ollama.py            # Ollama AI integration
├── ui/                       # UI utilities
│   ├── validators.py        # Input validation (100% coverage)
│   └── state.py             # Gradio state builders
├── tabs/                     # Tab modules
│   ├── login_tab.py         # Login interface
│   ├── register_tab.py      # Registration interface
│   ├── game_tab.py          # Main gameplay interface
│   ├── settings_tab.py      # Settings and server control
│   ├── database_tab.py      # Admin database viewer
│   ├── ollama_tab.py        # AI model management
│   └── help_tab.py          # Help documentation
├── utils.py                  # Shared utilities
└── static/styles.css        # Centralized CSS

Benefits:

  • Clear separation between API logic, validation, and UI

  • 100% test coverage on API and UI utility modules (191 tests)

  • API clients work outside Gradio (CLI tools, tests, scripts)

  • Easy to extend with new features or tabs

System Components

Backend (FastAPI)

Located in src/mud_server/api/:

  • server.py - App initialization, CORS, routing

  • routes.py - All API endpoints

  • models.py - Pydantic request/response models

  • auth.py - Session management

  • password.py - Bcrypt password hashing

  • permissions.py - Role-based access control

Game Engine

Located in src/mud_server/core/:

  • engine.py - GameEngine class with all game logic

  • world.py - World, Room, Item dataclasses

  • bus.py - Event bus for game event handling

  • events.py - Event type constants

Event Bus Architecture

The event bus provides publish-subscribe infrastructure for game events. It follows these key principles:

Synchronous Emit: Events are committed to the log before handlers are notified. This ensures deterministic ordering via sequence numbers.

Immutable Events: Once emitted, events cannot be changed. They represent facts about what happened (Ledger truth).

Plugin-Ready: The bus is designed to support future plugin systems where plugins react to events but cannot intervene or block them.

┌─────────────────────────────────────────────────────────────────┐
│                      Event Bus                                   │
│                                                                  │
│    Engine.move()                                                 │
│         │                                                        │
│         ▼                                                        │
│    bus.emit("player:moved", {...})                              │
│         │                                                        │
│         ├── 1. Increment sequence (deterministic ordering)       │
│         │                                                        │
│         ├── 2. Create immutable MudEvent                         │
│         │                                                        │
│         ├── 3. Append to event log (COMMITTED)                   │
│         │                                                        │
│         └── 4. Notify handlers (async execution allowed)         │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Event types follow “domain:action” format in past tense (e.g., player:moved, item:picked_up) to emphasize they record facts, not requests.

Database Layer

Located in src/mud_server/db/:

  • database.py - SQLite operations, schema, CRUD

Data Flow

Request Flow:

  1. Client - User interacts with Gradio interface

  2. API Call - Client sends HTTP request to FastAPI

  3. Session Validation - Server validates session and permissions

  4. Command Parsing - Server parses command and arguments

  5. Game Logic - Engine executes command

  6. Database - Engine reads/writes to SQLite

  7. Response - Server returns result to client

  8. Display - Client updates interface

Technology Stack

Component

Technology

Purpose

Backend

FastAPI 0.125+

REST API framework

Frontend

Gradio 6.2+

Web interface

Server

Uvicorn 0.38+

ASGI server

Database

SQLite 3

Data persistence

Auth

Passlib + Bcrypt

Password hashing

Testing

pytest 8.3+

Test framework

Linting

Ruff 0.8+

Code quality

Formatting

Black 24.10+

Code style

Type Checking

Mypy 1.13+

Static analysis

Key Design Patterns

Session Management

  • UUID-based sessions stored in memory and database

  • Session tuples: (user_id: int, role: str, character_id: int | None)

  • Activity tracking updated on each API call

  • Validation decorator: @validate_session()

Role-Based Access Control

Four roles with hierarchical permissions:

  • Player - Basic gameplay

  • WorldBuilder - Player + content creation

  • Admin - WorldBuilder + user management

  • Superuser - Admin + role management, full access

Command Pattern

  • Command parsing splits into cmd and args

  • Router delegates to appropriate engine method

  • Response model: CommandResponse with success/message

  • Error handling via HTTP exceptions

Repository Pattern

  • Database layer isolated in database.py

  • CRUD operations as separate functions

  • JSON serialization for complex types

  • Connection management via context managers

Database Schema

Users Table

  • id (INTEGER, PRIMARY KEY)

  • username (TEXT, UNIQUE)

  • password_hash (TEXT)

  • email_hash (TEXT, UNIQUE, NULLABLE) - placeholder for hashed emails

  • role (TEXT) - Player, WorldBuilder, Admin, Superuser

  • is_active (INTEGER)

  • is_guest (INTEGER)

  • guest_expires_at (TIMESTAMP, NULLABLE)

  • account_origin (TEXT) - e.g. legacy, visitor, admin

  • created_at (TIMESTAMP)

  • last_login (TIMESTAMP)

  • tombstoned_at (TIMESTAMP, NULLABLE) - set for deactivated users; guest accounts are deleted on expiry

Characters Table

  • id (INTEGER, PRIMARY KEY)

  • user_id (INTEGER, NULLABLE)

  • name (TEXT, UNIQUE)

  • inventory (TEXT) - JSON array of item IDs

  • is_guest_created (INTEGER)

  • created_at (TIMESTAMP)

  • updated_at (TIMESTAMP)

Character Locations Table

  • character_id (INTEGER, PRIMARY KEY)

  • room_id (TEXT)

  • updated_at (TIMESTAMP)

Sessions Table

  • session_id (TEXT, UNIQUE) - UUID

  • user_id (INTEGER)

  • character_id (INTEGER, NULLABLE)

  • created_at (TIMESTAMP)

  • last_activity (TIMESTAMP)

  • expires_at (TIMESTAMP, NULLABLE)

  • client_type (TEXT)

Chat Messages Table

  • id (INTEGER, PRIMARY KEY)

  • character_id (INTEGER, NULLABLE)

  • user_id (INTEGER, NULLABLE)

  • message (TEXT)

  • room (TEXT)

  • recipient_character_id (INTEGER, NULLABLE)

  • timestamp (TIMESTAMP)

Security Considerations

Authentication

  • Password hashing: Bcrypt via passlib (intentionally slow, ~100ms per hash)

  • Password policy: NIST SP 800-63B aligned with comprehensive validation

  • Session IDs: UUID v4 (cryptographically random, hard to guess)

  • Session validation: Every API call validates session and extracts role

  • Role-based access: Four-tier permission system (Player < WorldBuilder < Admin < Superuser)

Password Policy

The STANDARD password policy enforces:

  • Minimum 12 characters (NIST recommended)

  • Common password rejection (150+ known weak passwords blocked)

  • Leet-speak detection (p@ssw0rd detected as “password” variant)

  • Sequential character detection (abc, 123, xyz patterns blocked)

  • Repeated character detection (aaa, 1111 patterns blocked)

Three policy levels available: BASIC (8 chars), STANDARD (12 chars), STRICT (16 chars + complexity).

See Security for complete details.

Known Limitations

  • SQLite concurrency limits for high-traffic deployments

  • No email verification (email hashes are placeholders for future use)

  • No two-factor authentication

Performance

Current Capacity

  • ~50-100 concurrent players (SQLite limitation)

  • No caching (every request hits DB)

  • Synchronous DB operations

  • In-memory sessions (fast but not persistent)

Scaling Considerations

For larger deployments:

  • Migrate to PostgreSQL for concurrency

  • Add Redis for session storage

  • Implement caching layer

  • Use async database operations

  • Add load balancing