Skip to main content

Authority

A server-authoritative state object whose fields replicate to clients through attributes on an instance. You create it on both sides from the same module with the same default table; the server owns the values and the clients receive them.

How it works:

  • Each key of properties becomes a field. Its initial value is the matching attribute on parent if one exists, otherwise the default you passed in.
  • Server: writing authority.Key = value stores the value and calls parent:SetAttribute("Key", value), which Roblox replicates. All defaults are written to the attributes as soon as the object is created. Only string keys can be attributes; other keys are kept locally with a warning.
  • Client: writing any field errors. Reading returns the latest value, which is kept in sync by listening to parent.AttributeChanged, so clients see server writes without any remotes.
  • Both sides also pick up attribute changes made outside the object (for example from the Studio properties panel) for keys that are currently tracked.
-- ReplicatedStorage/MatchState.luau (required by both server and client)
local Authority = require(path.to.Authority)

return Authority(workspace, {
	Phase = "Lobby",
	TimeLeft = 60,
})
-- Server
local MatchState = require(ReplicatedStorage.MatchState)
MatchState.Phase = "Playing"      -- sets workspace attribute "Phase", replicates
MatchState.TimeLeft -= 1

-- Client
local MatchState = require(ReplicatedStorage.MatchState)
print(MatchState.Phase)           -- "Playing" once replicated
workspace:GetAttributeChangedSignal("TimeLeft"):Connect(function()
	print(MatchState.TimeLeft)
end)

Setting a field to nil on the server removes the attribute and the object stops tracking that key, so prefer sentinel values over nil for optional state. Do not use Destroy as a key; it is reserved for the cleanup method.

Credits: written by KashTheKing. No third-party dependencies.

Installation and guide: Authority package page.

Types​

AuthorityObject​

interface AuthorityObject {
Destroy: (self: Authority<A>) → ()--

Disconnects the attribute listener and disables the object. See Authority:Destroy.

}

The object returned by Authority.new: all the fields of your properties table A (read on both sides, written on the server only) plus Destroy. It is a userdata proxy, not a table, so you cannot iterate it or use rawget/rawset on it.

Functions​

new​

Authority.new(
parent: Instance,--

The instance whose attributes carry the state. It must replicate to the clients that need the values (e.g. under workspace or ReplicatedStorage).

properties: A--

Dictionary of default values. Keys must be strings to replicate; values must be attribute-compatible types.

) → Authority<A>--

A proxy exposing properties' fields plus Destroy.

Creates an Authority whose fields mirror attributes on parent. Call it on both the server and the client with the same defaults. The module itself is this function, so use Authority(parent, properties).

On the server every default is immediately written to parent as an attribute (existing attributes win over defaults), and subsequent writes to the object replicate the same way. On the client the object is read-only and follows the server's attribute values.

Errors

TypeDescription
"Parent must a valid instance"`parent` is not an Instance.
"Properties must be a valid dictionary"`properties` is not a table.
"Client attempted to change server key"Raised later, when a client assigns to any field of the returned object.

Destroy​

Authority:Destroy() → ()

Stops listening to parent.AttributeChanged and removes every metamethod from the proxy, so any later field access errors. The attributes already written to parent are left in place. Call it when the state is no longer needed, for example when parent is about to be destroyed.

Show raw api
{
    "functions": [
        {
            "name": "Destroy",
            "desc": "Stops listening to `parent.AttributeChanged` and removes every metamethod from the proxy, so\nany later field access errors. The attributes already written to `parent` are left in place.\nCall it when the state is no longer needed, for example when `parent` is about to be destroyed.",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 80,
                "path": "packages/src/Authority/init.luau"
            }
        },
        {
            "name": "new",
            "desc": "Creates an Authority whose fields mirror attributes on `parent`. Call it on both the server\nand the client with the same defaults. The module itself is this function, so use\n`Authority(parent, properties)`.\n\nOn the server every default is immediately written to `parent` as an attribute (existing\nattributes win over defaults), and subsequent writes to the object replicate the same way. On\nthe client the object is read-only and follows the server's attribute values.",
            "params": [
                {
                    "name": "parent",
                    "desc": "The instance whose attributes carry the state. It must replicate to the clients that need the values (e.g. under `workspace` or `ReplicatedStorage`).",
                    "lua_type": "Instance"
                },
                {
                    "name": "properties",
                    "desc": "Dictionary of default values. Keys must be strings to replicate; values must be attribute-compatible types.",
                    "lua_type": "A"
                }
            ],
            "returns": [
                {
                    "desc": "A proxy exposing `properties`' fields plus `Destroy`.",
                    "lua_type": "Authority<A>"
                }
            ],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "\"Parent must a valid instance\"",
                    "desc": "`parent` is not an Instance."
                },
                {
                    "lua_type": "\"Properties must be a valid dictionary\"",
                    "desc": "`properties` is not a table."
                },
                {
                    "lua_type": "\"Client attempted to change server key\"",
                    "desc": "Raised later, when a client assigns to any field of the returned object."
                }
            ],
            "source": {
                "line": 99,
                "path": "packages/src/Authority/init.luau"
            }
        }
    ],
    "properties": [],
    "types": [
        {
            "name": "AuthorityObject",
            "desc": "The object returned by [Authority.new](#new): all the fields of your `properties` table `A`\n(read on both sides, written on the server only) plus `Destroy`. It is a userdata proxy, not a\ntable, so you cannot iterate it or use `rawget`/`rawset` on it.",
            "fields": [
                {
                    "name": "Destroy",
                    "lua_type": "(self: Authority<A>) -> ()",
                    "desc": "Disconnects the attribute listener and disables the object. See [Authority:Destroy](#Destroy)."
                }
            ],
            "source": {
                "line": 68,
                "path": "packages/src/Authority/init.luau"
            }
        }
    ],
    "name": "Authority",
    "desc": "A server-authoritative state object whose fields replicate to clients through attributes on an\ninstance. You create it on both sides from the same module with the same default table; the\nserver owns the values and the clients receive them.\n\nHow it works:\n- Each key of `properties` becomes a field. Its initial value is the matching attribute on\n  `parent` if one exists, otherwise the default you passed in.\n- **Server**: writing `authority.Key = value` stores the value and calls\n  `parent:SetAttribute(\"Key\", value)`, which Roblox replicates. All defaults are written to the\n  attributes as soon as the object is created. Only string keys can be attributes; other keys\n  are kept locally with a warning.\n- **Client**: writing any field errors. Reading returns the latest value, which is kept in sync\n  by listening to `parent.AttributeChanged`, so clients see server writes without any remotes.\n- Both sides also pick up attribute changes made outside the object (for example from the\n  Studio properties panel) for keys that are currently tracked.\n\n```lua\n-- ReplicatedStorage/MatchState.luau (required by both server and client)\nlocal Authority = require(path.to.Authority)\n\nreturn Authority(workspace, {\n\tPhase = \"Lobby\",\n\tTimeLeft = 60,\n})\n```\n\n```lua\n-- Server\nlocal MatchState = require(ReplicatedStorage.MatchState)\nMatchState.Phase = \"Playing\"      -- sets workspace attribute \"Phase\", replicates\nMatchState.TimeLeft -= 1\n\n-- Client\nlocal MatchState = require(ReplicatedStorage.MatchState)\nprint(MatchState.Phase)           -- \"Playing\" once replicated\nworkspace:GetAttributeChangedSignal(\"TimeLeft\"):Connect(function()\n\tprint(MatchState.TimeLeft)\nend)\n```\n\nSetting a field to `nil` on the server removes the attribute and the object stops tracking that\nkey, so prefer sentinel values over `nil` for optional state. Do not use `Destroy` as a key; it is\nreserved for the cleanup method.\n\n**Credits:** written by KashTheKing. No third-party dependencies.\n\nInstallation and guide: [Authority package page](/docs/packages/authority).",
    "source": {
        "line": 58,
        "path": "packages/src/Authority/init.luau"
    }
}