Core Components
The flora runtime consists of several key components:Discord Client Layer
Built on Serenity, the Discord client handles:- Gateway event subscription and processing
- REST API calls to Discord
- Bot authentication and connection management
- Event routing to guild isolates
guild_id are dropped at the Discord handler level (apps/runtime/src/discord_handler.rs:74-77).
Bot Runtime
TheBotRuntime manages a pool of worker threads (apps/runtime/src/runtime/mod.rs:34-65):
- Worker pool with 1-64 threads (configurable via
RUNTIME_MAX_WORKERS, default 4) - Guild-to-worker routing using consistent hashing
- Per-worker isolate lifecycle management
- Runtime migration support for load balancing
V8 Isolates
Each guild runs in its own V8 isolate:- One isolate per guild for complete isolation
- Backed by Deno Core for ops and module loading
- Initialized once per worker thread
- SDK bundle loaded into default runtime, shared across guild isolates
V8 is initialized once per process (apps/runtime/src/main.rs:121). Workers are initialized sequentially to avoid V8 race conditions (apps/runtime/src/runtime/mod.rs:68-75).
Deno Core Ops
Ops expose Discord functionality to JavaScript:- Discord actions:
op_send_message,op_edit_message,op_delete_message - Command registration:
op_register_slash_commands - KV storage:
op_kv_get,op_kv_set,op_kv_list,op_kv_delete - Secrets:
op_secrets_get(runtime-scoped or guild-scoped) - Logging: Custom log sink for script output
- Cron:
op_cron_registerfor scheduled tasks
#[op2] macro from Deno Core and extract state via Rc<RefCell<OpState>>.
HTTP API
Axum-based REST API for CLI and tooling (apps/runtime/src/handlers/mod.rs):/deployments/*- Deploy, list, and read guild scripts/logs/*- Query and stream logs (including SSE)/kv/*- KV store management/secrets/*- Secret management/auth/*- Discord OAuth flow/tokens/*- API token management
Storage Layer
Multiple storage backends:| Storage | Technology | Purpose |
|---|---|---|
| Postgres | SQLx | Deployments, tokens, KV metadata |
| Redis/Valkey | Fred | Session cache, deployment cache |
| Sled | Embedded KV | Per-guild key-value storage |
Sled stores are scoped per guild and per store name, backed by local disk under
data/kv (apps/runtime/src/main.rs:103).Data Flow
Deployment Flow
- CLI sends bundled TypeScript to
/deployments - Runtime stores deployment in Postgres + Redis cache
- Runtime routes deployment to appropriate worker by guild ID
- Worker creates/updates guild isolate with new script
- Guild’s cron jobs are cleared and re-registered
Event Dispatch Flow
- Discord gateway event arrives at Serenity handler
- Event payload serialized to JSON
- Guild ID extracted; events without guild ID are dropped
- Event routed to worker via consistent hashing
- Worker dispatches to guild isolate’s event handlers
- Script handlers registered via
on()execute - Ops like
ctx.reply()map back to Discord REST calls
Cron Execution Flow
- Per-worker interval ticks every second (apps/runtime/src/runtime/worker.rs:102-116)
- Cron registry checks for due jobs
- Due jobs dispatched as synthetic events (
__cron:<name>) - Handlers execute with their own timeout (default 5s)
skipIfRunningflag prevents concurrent execution
Configuration
Runtime behavior is controlled via environment variables orconfig.toml (crates/flora_config/src/lib.rs):
Runtime Lifecycle
Startup Sequence
- Load configuration from environment +
config.toml(apps/runtime/src/main.rs:41-46) - Initialize tracing subscriber (apps/runtime/src/main.rs:48-63)
- Connect to Postgres, run migrations (apps/runtime/src/main.rs:76-82)
- Connect to Redis/Valkey with reconnect policy (apps/runtime/src/main.rs:84-98)
- Initialize services: deployments, tokens, KV, secrets, auth (apps/runtime/src/main.rs:100-119)
- Initialize V8 once per process (apps/runtime/src/main.rs:121)
- Initialize worker pool sequentially (apps/runtime/src/main.rs:134-140)
- Load SDK bundle into all workers (apps/runtime/src/main.rs:142-148)
- Restore cached deployments from database (apps/runtime/src/main.rs:149-157)
- Start Discord client and HTTP server concurrently (apps/runtime/src/main.rs:159-207)
Guild Bootstrap
When the bot joins a new guild without a deployment:GuildCreateevent fires (apps/runtime/src/discord_handler.rs:360-367)- Runtime checks for existing deployment
- If none exists, deploys a minimal starter script
- Starter script includes basic prefix command example
Thread Model
- Main thread: Serenity event loop + Axum HTTP server
- Worker threads: One per configured worker (1-64)
- Tokio runtime: Current-thread runtime per worker (apps/runtime/src/runtime/worker.rs:94-97)