Skip to content

Core Concepts

Five primitives. Everything else in the framework — networking, persistence, kits, anti-exploit — is built out of these.

Boots once per machine — server and client each run one. It owns the scheduler, hooks, bus, and services. ServerKernel adds sessions, networking, and everything privileged.

-- src/Server/Main.server.luau already does this; shown for orientation
local ServerKernel = require(game:GetService("ServerScriptService").ChloeKernelServer)
local Kernel = ServerKernel.boot()
-- ...Bootstrap registers services here...
Kernel:start()
-- anywhere else on the server, after boot:
local Kernel = ServerKernel.current()

Deep dive: Kernel & Boot.

Your game’s systems, registered before start(), booted in dependency order. Every init runs before any start, so start can safely use any dependency. Cycles crash loudly with the full chain (A -> B -> C -> A).

Kernel:registerService({
Name = "Quests",
Dependencies = { "Inventory" },
init = function(self, kernel)
self.Kernel = kernel
self.Active = {}
end,
start = function(self)
self.Inventory = self.Kernel:getService("Inventory") -- guaranteed initialized
end,
stop = function(self) end, -- reverse boot order on shutdown
})

Deep dive: Services.

One per player, created on join, destroyed on leave. Anything bound to a session is torn down automatically — connections, instances, functions.

Kernel:onSession(function(session)
session.Data.Streak = 0 -- per-visit scratch
session:bind(session.Player.CharacterAdded:Connect(onSpawn)) -- auto-disconnected on leave
session:bind(function() print("left:", session.Player.Name) end)
end)

session.Data is per-visit scratch; session.Profile is persistent data once a DataDriver is attached. onSession also fires for players already in the game, so late-registering systems never miss anyone.

Deep dive: Sessions.

Named validation chains. Handlers mutate a context or cancel by returning false. Fail-closed: a crashing validator rejects the action. Every network intent passes through one.

Kernel.Hooks:on("Intent.BuyItem", function(context)
if context.Session.Profile.Data.Coins < Prices[context.Args[1]] then
return false -- rejected; the handler never runs
end
end, 10) -- lower numbers run earlier
local Passed, Context = Kernel.Hooks:fire("Intent.BuyItem", { Session = s, Args = { itemId } })

Deep dive: Hooks · every built-in hook point: reference.

Topic pub/sub so systems communicate without importing each other. Wildcards subscribe to whole families.

Kernel.Bus:subscribe("Combat.Kill", function(topic, killer, victim) end)
Kernel.Bus:subscribe("Combat.*", function(topic, ...) end) -- whole family
Kernel.Bus:publish("Combat.Kill", killerSession, victimSession)

Deep dive: Signals & Bus · every built-in topic: reference.

A single player action touches all five. A client fires UseItem; the NetDriver checks the rate limit, decodes the buffer against the channel schema, and fires the Intent.UseItem hook chain with the player’s session in context; validators check server-side state; the handler mutates session.Profile.Data; the change publishes on the bus; a replica picks it up and schedules a delta broadcast on the scheduler under the frame budget — and nothing in that path trusted a byte the client sent.

The security consequences of this shape are spelled out in Architecture & Security.