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
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/clearIntervalto 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 tracking —
getMasterUptime()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