Logo
Kythia
Logo
Kythia
Login
Login
Architecture Overview
Minimized (Click to Restore)

Architecture Overview

A deep dive into Kythia's layered, manager-driven architecture powered by kythia-core.


Kythia is built on a "Framework vs. Application" philosophy. The kythia-core package acts as the high-performance engine, while Addons provide feature-specific logic. The two are wired together through a central Dependency Injection (DI) container.


System Architecture

graph TB
    subgraph APP["Application Layer (Your Bot)"]
        BOT["index.js / main entry<br/>──────────────────<br/>Configuration · Dependency Setup · Kythia Init"]
    end

    subgraph CORE["Kythia Core Layer"]
        ORCH["Kythia Orchestrator<br/>──────────────────<br/>Lifecycle · DI Container · Manager Coordination"]

        subgraph MANAGERS["Managers"]
            AM["AddonManager<br/>─────────────<br/>Discovery · Loading<br/>Priority · Dep Graph"]
            IM["InteractionManager<br/>─────────────<br/>Command Routing<br/>Button/Modal/Select<br/>Middleware Exec"]
            EM["EventManager<br/>─────────────<br/>Event Registration<br/>Multi-handler Dispatch"]
            SM["ShutdownManager<br/>─────────────<br/>SIGINT/SIGTERM<br/>Memory Monitor<br/>Graceful Stop"]
            TM["TranslatorManager<br/>─────────────<br/>i18n Locales<br/>Variable Interpolation"]
            MM["MetricsManager<br/>─────────────<br/>prom-client<br/>Command Counters<br/>Cache Hit/Miss"]
        end
    end

    subgraph DB["Database Layer"]
        KM["KythiaModel<br/>─────────────<br/>Hybrid Caching<br/>afterSave Hooks<br/>Tag Invalidation"]
        KMig["KythiaMigrator<br/>─────────────<br/>Batch Tracking<br/>Rollback Support"]
        ML["ModelLoader<br/>─────────────<br/>Auto-Discovery<br/>autoBoot()"]
    end

    subgraph INFRA["Infrastructure"]
        REDIS["Redis<br/>(Primary Cache)"]
        LRU["LRU Map<br/>(Fallback)"]
        SEQUELIZE["Sequelize ORM"]
        DJS["Discord.js Client"]
    end

    BOT --> ORCH
    ORCH --> MANAGERS
    AM --> DB
    KM --> REDIS
    KM --> LRU
    KM --> SEQUELIZE
    KMig --> SEQUELIZE
    ML --> SEQUELIZE
    ORCH --> DJS

Boot Sequence

The kythia.start() method follows a strict, ordered initialization sequence:

flowchart TD
    A([kythia.start]) --> B[Print ANSI Banner]
    B --> C[Check Discord Client Intents & Partials]
    C --> D{legalConfig set?}
    D -- No --> EXIT1([process.exit 1])
    D -- Yes --> E[Init Sentry if configured]
    E --> F[checkRequiredConfig\nbot.token, bot.clientId, db.*]
    F --> G[performLicenseValidation\nKythiaOptimizer.optimize]
    G --> H{License Valid?}
    H -- No --> EXIT2([terminateUnauthorizedProcess])
    H -- Yes --> I[Load Translator\ncore/lang + appRoot/lang]
    I --> J[new AddonManager\nload all addons]
    J --> K{sequelize provided?}
    K -- Yes --> L[connectDatabaseWithRetry\n5 retries × 5s delay]
    L --> M[KythiaMigrator\nrun pending migrations]
    M --> N[bootModels\nautoBoot all addon models]
    N --> O[attachHooksToAllModels\ncache invalidation hooks]
    K -- No --> P
    O --> P[new EventManager\nattach all event listeners]
    P --> Q[new MiddlewareManager]
    Q --> R[new InteractionManager\ninitialize interaction routing]
    R --> S[new ShutdownManager\nregister SIGINT/SIGTERM hooks]
    S --> T[client.login\nDiscord Gateway]
    T --> Z([Bot Running])

    style EXIT1 fill:#c0392b,color:#fff
    style EXIT2 fill:#c0392b,color:#fff
    style Z fill:#27ae60,color:#fff

Core Components

1. InteractionManager

Routes all InteractionCreate events to the correct handler and runs the middleware pipeline:

flowchart TD
    A[Discord: InteractionCreate] --> B{Identify Type}
    B --> CMD[ChatInputCommand\nSlash Command]
    B --> BTN[ButtonInteraction]
    B --> MOD[ModalSubmit]
    B --> SEL[SelectMenu]
    B --> AUTO[Autocomplete]

    CMD --> C[Get command from client.commands]
    C --> D[Run Middleware Pipeline\nbotPermissions · userPermissions\ncooldown · ownerOnly · isInMainGuild]
    D --> E{All middlewares pass?}
    E -- No --> F[Reply with error]
    E -- Yes --> G[command.execute interaction, container]
    G --> H[Report metrics & errors]

    BTN --> K[Find handler by customId prefix-match]
    MOD --> K
    SEL --> K
    K --> L[handler interaction, container]

    AUTO --> M[Find autocomplete handler]
    M --> N[handler.autocomplete interaction]

2. AddonManager

Discovers, validates, and loads all addons in dependency-sorted order (Kahn's algorithm):

flowchart TD
    A([loadAddons called]) --> B[Scan appRoot/addons/\nfilter out _ prefixed dirs]
    B --> C[Parse each addon.json\npriority, dependencies]
    C --> D[Check kythiaConfig.addons\nfor disabled addons]
    D --> E[validateDependencies per addon]
    E --> F{Dep missing or disabled?}
    F -- Yes --> G[Add to disabled set\nlog error]
    F -- No --> H[topologicalSort\nKahn's algorithm + priority tiebreak]
    G --> H
    H --> I[For each addon in order]
    I --> J[Load register.js if exists]
    J --> K[Load commands/ events/ buttons/\nmodals/ select_menus/ tasks/]
    K --> L([return commandDataForDeployment])

3. EventManager

Multiple addons can subscribe to the same Discord event independently. All handlers fire concurrently:

sequenceDiagram
    participant DJS as Discord.js Client
    participant EM as EventManager
    participant H1 as Handler 1 (addon-a)
    participant H2 as Handler 2 (addon-b)

    Note over EM: initialize() — register all events on client
    DJS->>EM: emit('messageCreate', message)
    EM->>EM: get handlers for 'messageCreate'
    par Execute all handlers concurrently
        EM->>H1: handler(message, container)
        EM->>H2: handler(message, container)
    end
    H1-->>EM: done / error (logged, no crash)
    H2-->>EM: done / error (logged, no crash)

Data & Caching Layer

Kythia uses a 3-layer hybrid caching strategy to minimize database round-trips:

flowchart LR
    CODE["Your code\nUser.getCache(query)"] --> KM

    subgraph KM["KythiaModel Cache Layer"]
        direction TB
        GEN["Generate cache key\nSHA256 hash of query"] --> CHECK
        CHECK{"Cache hit?"}
    end

    CHECK -- Redis Hit --> REDIS["Redis\nPrimary Cache"]
    CHECK -- Redis Miss/Down --> LRU["LRU Map\nFallback Cache"]
    CHECK -- Both Miss --> DB["Sequelize Database\n(SQLite / MySQL / PG)"]

    DB --> STORE["Store result in cache\nwith tags:\n'ModelName'\n'ModelName:ID:1'"]
    STORE --> REDIS
    REDIS --> RETURN["Return result"]
    LRU --> RETURN

    DB2["afterSave / afterDestroy hooks"] --> INVAL["Invalidate cache\nby tag prefix e.g. 'User*'"]
    INVAL --> REDIS
    INVAL --> LRU
In kythia.config.js, you can configure whether to use Redis. If Redis is down or disabled, the system automatically falls back to the LRU Map — your code doesn't change at all.

ShutdownManager

Manages graceful shutdown and memory monitoring:

flowchart TD
    A([SIGINT or SIGTERM]) --> B[ShutdownManager.initialize\nregisters signal handlers]
    B --> C[Execute all registered\nshutdownHooks in order]
    C --> D[Close DB connection]
    D --> E[Flush logs / telemetry]
    E --> F([process.exit 0])

Additional features:

  • Patches global setInterval/clearInterval to track all active timers — auto-clears them on shutdown
  • Memory pressure monitor — polls every 5 minutes; warns at 80% and errors at 95% of heap_size_limit
  • Master uptime trackinggetMasterUptime() returns seconds since process start, survives reconnects

Full Command Execution Flow

sequenceDiagram
    actor User
    participant DJS as Discord.js Gateway
    participant IM as InteractionManager
    participant MW as Middleware Pipeline
    participant CMD as Command execute()
    participant CM as KythiaModel (Cache)
    participant DB as Database
    participant MM as MetricsManager

    User->>DJS: /profile
    DJS->>IM: InteractionCreate (ChatInputCommand)
    IM->>IM: client.commands.get('profile')
    IM->>MW: botPermissions check
    MW-->>IM: pass
    IM->>MW: userPermissions check
    MW-->>IM: pass
    IM->>MW: cooldown check
    MW-->>IM: pass
    IM->>CMD: execute(interaction)
    CMD->>CM: User.getCache({ where: { userId } })
    CM-->>CMD: cache HIT — return user
    CMD->>DJS: interaction.reply(...)
    DJS-->>User: Response
    CMD->>MM: kythia_commands_total + 1
    CMD->>MM: kythia_command_duration_seconds observe
Kythia Documentation Sign in →