# Overview

URL: https://zerodev.ca/docs/medusa/developer-documentation

> Architecture, lifecycle, directory layout, and core exports for building Medusa modules.



Everything Medusa executes at runtime lives inside a module. All native features (starting with the core module) run as modules. Custom modules load identically, receive the identical runtime primitives, and have full access to Medusa's database, Discord gateway, HTTP router, web dashboard, audit logs, and localization systems.

<Callout type="info">
  This documentation is the complete reference for building modules. For bot deployment and server configuration instructions, refer to the [Setup Guide](/docs/medusa/setup) and [Configuration Guide](/docs/medusa/configuration).
</Callout>

## Module Architecture

Each directory located directly in `modules/` represents an isolated module. On boot, Medusa scans the directory, imports `main.js`, constructs the exported module class, and recursively registers and loads every subsystem located within `src/` and `resources/`.

```text
modules/
  example/
    main.js
    resources/
      cards/
      config/
      dashboard/
        pages/
      emojis/
      lang/
      leaderboards/
      logs/
      nodes/
      placeholders/
      posts/
    src/
      clocks/
      commands/
      events/
      handlers/
      models/
      routes/
```

### Loading Lifecycle

Medusa initializes modules only after the internal API is listening and the dashboard reports ready. If either fails to start, boot stops before any module registers. Modules then load in two strict phases:

1. **Registration Phase**: Medusa verifies licensing, installs any missing `dependencies.node` packages, initializes schemas, loads configuration groups, language dictionaries, logging categories, leaderboard providers, post providers, automation flow nodes, database models, HTTP routes, and custom dashboard pages.
2. **Loading Phase**: Medusa initializes handlers, Discord slash and prefix commands, Discord gateway events, and scheduled clocks. Medusa then triggers `onStartup()`, syncs database tables, mounts HTTP routes on the API server, and registers cron schedules.

### Per-Server Modules

Every installed module loads once for the whole bot, but each server can turn modules on or off from the dashboard Modules page. Changes apply immediately, with no restart. When a module is off in a server, Medusa:

* Leaves its slash commands out of that server's command sync and blocks its prefix commands and autocomplete.
* Answers its buttons, select menus, and modals (`medusa:<component>:<module>:...` ids) with the core `moduleDisabled` warning.
* Skips its Discord and internal events when the payload resolves to that server (`guildId`, `guild`, or `message.guildId`).
* Hides its dashboard pages, configs, logging groups, leaderboard metrics, and `/help` entries for that server, and its routes answer `404` to dashboard requests scoped to that server.
* Skips its automation nodes in flows running in that server.

Clocks run once for the whole bot, so a clock that loops over servers or posts into channels must check the server itself:

```js
for (const guild of this.medusa.discord.client.guilds.cache.values()) {
    if (!this.medusa.modules.enabled(this.module, guild.id)) continue;
}
```

Dashboard requests carry the selected server in the `x-guild-id` header, so module routes can read it from `request.headers["x-guild-id"]`.

When Medusa shuts down or a module is deloaded, Medusa stops all running clocks, triggers `onShutdown()`, unmounts routes, and closes resources cleanly.

## Subsystem Layout

Medusa loads files automatically based on folder location and filename conventions:

| Directory                    | Loaded File Pattern                                                | Base Class     | Export Name       |
| ---------------------------- | ------------------------------------------------------------------ | -------------- | ----------------- |
| `main.js`                    | Root module entrypoint                                             | `Modules`      | Module Class      |
| `src/models/`                | Every `*.js` file directly in folder                               | `Models`       | Model Class       |
| `src/handlers/`              | Every `*.js` file directly in folder                               | `Handlers`     | Handler Class     |
| `src/commands/`              | Every `*.js` file directly in folder                               | `Commands`     | Command Class     |
| `src/events/`                | Every `*.js` file directly in folder                               | `Events`       | Event Class       |
| `src/clocks/`                | Every `*.js` file directly in folder                               | `Clocks`       | Clock Class       |
| `src/routes/`                | Every `route.js` file at any nested path                           | `Routes`       | Route Class       |
| `resources/config/`          | Every `*.js` file directly in folder                               | `Configs`      | Config Class      |
| `resources/logs/`            | Every `*.js` file directly in folder                               | `Logs`         | Log Class         |
| `resources/lang/`            | English classes in `en/*.js`, translations in `translations.json`  | `Langs`        | Lang Class        |
| `resources/leaderboards/`    | Every `*.js` file directly in folder                               | `Leaderboards` | Leaderboard Class |
| `resources/posts/`           | Every `*.js` file directly in folder                               | `Posts`        | Post Class        |
| `resources/placeholders/`    | Every `*.js` file directly in folder                               | `Placeholders` | Placeholder Class |
| `resources/nodes/`           | Every `*.js` file directly in folder                               | `Nodes`        | Node Class        |
| `resources/dashboard/pages/` | Every `page.js` file at any nested path                            | `Pages`        | Page Class        |
| `resources/emojis/`          | Every image file (`.png`, `.gif`, etc.) and optional `emojis.json` | Image / JSON   | Emojis            |

## Base Module Class

Every module entrypoint (`modules/<name>/main.js`) must export a default or named class extending `Modules` imported from `#medusa/modules`.

```js title="modules/example/main.js"
import { Modules } from "#medusa/modules";

export class ExampleModule extends Modules {
    constructor(medusa) {
        super(medusa, {
            name: "example",
            displayName: "Example",
            icon: "puzzle",
            author: "Zero Development",
            version: "1.0.0.0",
            description: "Production ready example module for Medusa.",
            dependencies: {
                hard: "",
                soft: "",
                node: []
            }
        });
    }

    permissions() {
        return [
            {
                id: "example:manage",
                name: "Example ( Manage )",
                description: "Administrative access to configure and manage example items.",
                group: "Example",
                module: "example"
            }
        ];
    }

    async onStartup() {
        this.medusa.console.log(this.medusa.console.levels.startup, `${this.getName()} module initialized successfully.`);
    }

    async onShutdown() {
        this.medusa.console.log(this.medusa.console.levels.info, `${this.getName()} module deloaded.`);
    }
}
```

### Module Metadata Properties

The configuration object passed to `super(medusa, meta)` accepts the following properties:

| Property       | Type       | Required | Description                                                                                                                                                                                                                                                                  |
| -------------- | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`         | `string`   | Yes      | Unique module identifier. Must match directory name in lowercase.                                                                                                                                                                                                            |
| `displayName`  | `string`   | No       | Display title shown on the web dashboard (defaults to `name`).                                                                                                                                                                                                               |
| `icon`         | `string`   | No       | Lucide icon identifier shown across dashboard module, configuration, and logging lists (defaults to `"puzzle"`).                                                                                                                                                             |
| `author`       | `string`   | Yes      | Name of the module author or organization.                                                                                                                                                                                                                                   |
| `version`      | `string`   | Yes      | Semantic version string (e.g. `1.0.0.0`).                                                                                                                                                                                                                                    |
| `description`  | `string`   | Yes      | Short explanation of module capabilities.                                                                                                                                                                                                                                    |
| `dependencies` | `object`   | No       | Dependency map containing `hard`, `soft`, and `node` dependencies. `node` is a `string[]` of npm package names the module imports. They are installed automatically when the module registers, before any of its handlers load, so they never go in the root `package.json`. |
| `formerly`     | `string[]` | No       | Previous module ids. On startup, stored config, language overrides, log settings, per-server toggles and rank permissions saved under an old id move to the current id, and keys inside them that start with the old id are renamed to its camelCase form.                   |

### Renaming a Module

Rename the folder and `name`, then list the old id in `formerly` so existing servers keep their settings:

```js title="modules/join-to-create/main.js"
super(medusa, {
    name: "join-to-create",
    formerly: ["voicehub"],
    displayName: "Join to Create"
});
```

Rename config groups and log categories that started with the old id to the camelCase form of the new id (`voicehub` becomes `joinToCreate`, `voicehubEvents` becomes `joinToCreateEvents`) so the migrated values line up. Give renamed models a `formerly` table name too.

If the old folder is still on disk, the loader skips it with a warning so both ids never load together. For official modules, the release build records every rename in `package.json` under `formerly`, and the auto-updater installs the renamed module into its new folder and deletes the old one. A folder listed in `MODULE_FROZEN` is never deleted.

### Module Methods

The `Modules` class provides the following built-in methods:

| Method              | Return Type     | Description                                                                             |
| ------------------- | --------------- | --------------------------------------------------------------------------------------- |
| `getName()`         | `string`        | Returns the module display name or identifier.                                          |
| `getIcon()`         | `string`        | Returns the module icon name.                                                           |
| `getAuthor()`       | `string`        | Returns the module author string.                                                       |
| `getVersion()`      | `string`        | Returns the module version string.                                                      |
| `getFormerly()`     | `string[]`      | Returns the previous module ids declared in `formerly`.                                 |
| `getDescription()`  | `string`        | Returns the module description.                                                         |
| `getDependencies()` | `object`        | Returns the dependency configuration object.                                            |
| `permissions()`     | `Array<object>` | Returns custom interaction permission nodes available for assignment in the dashboard.  |
| `onStartup()`       | `Promise<void>` | Invoked after all module subsystems, database models, routes, and commands have loaded. |
| `onShutdown()`      | `Promise<void>` | Invoked during bot shutdown or module unloading.                                        |

## Library Exports: `#medusa/modules`

Modules import all required base classes from the `#medusa/modules` import alias:

```js
import {
    Modules,
    Commands,
    Events,
    Clocks,
    Models,
    Routes,
    Handlers,
    Pages,
    Configs,
    Langs,
    Logs,
    Leaderboards,
    Posts,
    Nodes
} from "#medusa/modules";
```

## Cross-Module Communication

Every subsystem receives the root `medusa` instance. The `this.medusa.modules` manager allows any module to access any registered subsystem across the entire bot:

```js
const economyHandler = this.medusa.modules.handlers.get("economy", "balances");
const ticketsModel = this.medusa.modules.models.get("ticket", "ticket_tickets");
const moderationRoute = this.medusa.modules.routes.get("moderation", "cases");
const coreCommand = this.medusa.modules.commands.get("core", "ping");
const configuredCurrency = this.medusa.modules.config.value("economy", "settings", "currencySymbol");
const localizedWelcome = this.medusa.modules.lang.phrase("core", "messages", "welcome", { user: interaction.user.id });
```

### Module Registry Accessors

| Accessor                               | Signature                                     | Description                                                      |
| -------------------------------------- | --------------------------------------------- | ---------------------------------------------------------------- |
| `this.medusa.modules.models.get`       | `(module, name) => Models`                    | Retrieves a database model instance.                             |
| `this.medusa.modules.handlers.get`     | `(module, name) => Handlers`                  | Retrieves a business logic handler instance.                     |
| `this.medusa.modules.routes.get`       | `(module, name) => Routes`                    | Retrieves an HTTP route instance.                                |
| `this.medusa.modules.commands.get`     | `(module, name) => Commands`                  | Retrieves a registered Discord command instance.                 |
| `this.medusa.modules.commands.find`    | `(name) => Commands`                          | Searches all loaded modules for a command by name.               |
| `this.medusa.modules.events.get`       | `(module, name) => Events`                    | Retrieves a Discord gateway event instance.                      |
| `this.medusa.modules.clocks.get`       | `(module, name) => Clocks`                    | Retrieves a scheduled clock instance.                            |
| `this.medusa.modules.config.get`       | `(module, name) => Configs`                   | Retrieves a configuration group instance.                        |
| `this.medusa.modules.config.value`     | `(module, name, key) => any`                  | Reads a specific configuration setting value.                    |
| `this.medusa.modules.config.values`    | `(module, name) => object`                    | Reads all key-value pairs for a configuration group.             |
| `this.medusa.modules.lang.get`         | `(module, name) => Langs`                     | Retrieves a language group instance.                             |
| `this.medusa.modules.lang.phrase`      | `(module, name, key, tokens) => string`       | Resolves a localized phrase with token replacement.              |
| `this.medusa.modules.lang.embed`       | `(module, name, key, tokens) => EmbedBuilder` | Resolves a localized embed template with tokens.                 |
| `this.medusa.modules.logs.get`         | `(module, name) => Logs`                      | Retrieves a logging category instance.                           |
| `this.medusa.modules.pages.get`        | `(module, name) => Pages`                     | Retrieves a custom dashboard page instance.                      |
| `this.medusa.modules.pages.list`       | `() => Array<object>`                         | Returns all registered custom dashboard pages.                   |
| `this.medusa.modules.leaderboards.get` | `(module, name) => Leaderboards`              | Retrieves a leaderboard provider instance.                       |
| `this.medusa.modules.leaderboards.all` | `() => Array<object>`                         | Returns all registered leaderboard providers.                    |
| `this.medusa.modules.posts.get`        | `(module, name) => Posts`                     | Retrieves a post provider instance.                              |
| `this.medusa.modules.posts.all`        | `(guildId) => Array<object>`                  | Returns every post provider from modules enabled in that server. |
| `this.medusa.modules.placeholders.get` | `(module, name) => Placeholders`              | Retrieves a placeholder provider instance.                       |
| `this.medusa.modules.placeholders.all` | `(guildId) => Array<object>`                  | Returns every placeholder provider from enabled modules.         |
| `this.medusa.modules.nodes.get`        | `(module, name) => Nodes`                     | Retrieves an automation node instance.                           |
| `this.medusa.modules.nodes.all`        | `() => Array<object>`                         | Returns all registered automation nodes.                         |
