Introduction

OwlKnit is a modern framework, successor to Knit, built to organize and structure your Server/Client logic in a way that's clean, secure and maintainable, without fighting Roblox's networking model.

If you've used Knit before, OwlKnit will feel immediately familiar. If you haven't, don't worry this documentation assumes no prior framework knowledge.

Services & Controllers

In OwlKnit, your game logic is split into two kinds of module:

Entity Runs on Role
Service Server Business logic, database access, security, global state
Controller Client UI, input handling, visual effects, talking to Services

A Service owns the truth of your game, inventories, currencies, match state. A Controller owns the experience menus, HUD, camera work, tweens. Neither should reach into the other's job.

Services and Controllers never talk to each other directly through require. Every interaction goes through Owl's own abstractions Signal, Property and typed Client methods, which keeps the boundary between server-authoritative logic and client-side presentation clean and enforced, not just conventional.

A quick taste

Here's what a minimal Service looks like. Don't worry about the details yet, every part of this is covered in Services and Lifecycle.

DataService.lua
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Owl = require(ReplicatedStorage.OwlKnit.Owl)
 
local DataService = Owl.CreateService({
    Name = "DataService",
    Dependencies = {},
    Client = {
        PointsChanged = Owl.CreateSignal(),
        State = Owl.CreateProperty("Menu"),
    },
})
 
return DataService

That's it no manual RemoteEvent instances to wire up, no FindFirstChild chains to guard against timing bugs. PointsChanged and State are already typed, already replicated and already accessible from any Controller through Owl.GetService("DataService").

Why the strict boundary?

It's tempting, early on, to just require() a Service module straight from a LocalScript and call a function on it. Roblox will happily let you do that for anything sitting in ReplicatedStorage, but nothing stops a malicious client from doing the exact same thing, calling your functions directly and skipping whatever validation you meant to run first.

By routing everything through Owl's Signal, Property and Client-method abstractions, every cross-boundary call passes through a single, well-defined surface, one that Middleware, RateLimiter and TypeChecker can all hook into consistently, instead of you re-implementing validation by hand in every Service.

Where to go next

  • New to Owl? Head to Installation to add the package and start the framework on both sides.
  • Coming from Knit? Owl vs Knit covers exactly what changed and why.
  • Want the full picture first? Lifecycle explains the OwlInit > OwlStart > OwlDestroy sequence every Service and Controller follows.