Skip to main content

TRNamespaces&Tokens

A namespace is an optional string that identifies a replicator's "class" so it can be discovered by name. Tokens layer opt-in collision safety on top of that.

Namespaces are optional

Pass Namespace when you want to find the replicator by string later:

-- Named — discoverable by its namespace string.
ServerReplicator.new({ Namespace = "Inventory", Data = ..., Targets = {} })

ClientReplicator.ForEach("Inventory", function(replicator) ... end)

Omit it for an anonymous replicator. Anonymous replicators still work fully — they're just intentionally unreachable by string search. Find them by Id, by tags, or with a predicate instead:

-- Anonymous — reachable only by Id, tags, or predicate.
local rep = ServerReplicator.new({
	Data = ...,
	Targets = {},
	Tags = { Kind = "Ephemeral" },
})

ClientReplicator.ForEach({ Kind = "Ephemeral" }, function(replicator) ... end)

See TR Discovery & Targeting for the full set of search conditions.

Opt-in collision safety with TOKEN

In a large codebase, two unrelated modules might accidentally pick the same namespace string. A ReplicationToken claims a name exclusively so that can't happen silently. Claim it once (usually at the top of a module), store it, and pass it as the Namespace:

local PlayerToken = ServerReplicator.TOKEN("PlayerData")

ServerReplicator.new({ Namespace = PlayerToken, Data = ..., Targets = {} })

-- Release the name once every replicator using it is destroyed:
ServerReplicator.TOKEN.destroy(PlayerToken)

TOKEN also exposes TOKEN.new(name) (identical to calling TOKEN(name)) and TOKEN.get(name) to fetch an already-registered token.

:::caution Collision rules The ownership ledger enforces these — each throws rather than failing silently:

  • TOKEN("Name") throws if "Name" is already owned by another token.
  • TOKEN("Name") throws if live replicators already use "Name" as a raw string.
  • Passing the raw string "Name" as a Namespace throws once a token owns it — pass the token object instead.
  • TOKEN.destroy(token) throws if any replicator using that token is still alive.

Because of the second rule, claim your token at module load, before any replicator with that name is created. :::

When should I use a token?

Situation Recommendation
Small project, or a namespace only ever used in one place A plain string is fine — tokens add nothing.
Shared library, or a name used across several modules Use a TOKEN so accidental reuse fails loudly.
Discovery on the client Either works — a token matches the same string a raw namespace would.

Tokens are purely a server-side authoring safeguard; on the wire and on the client a token is just its name string.


See also

Show raw api
{
    "functions": [],
    "properties": [],
    "types": [],
    "name": "TR Namespaces & Tokens",
    "desc": "A **namespace** is an optional string that identifies a replicator's \"class\" so it\ncan be discovered by name. Tokens layer opt-in collision safety on top of that.\n\n### Namespaces are optional\n\nPass `Namespace` when you want to find the replicator by string later:\n\n```lua\n-- Named — discoverable by its namespace string.\nServerReplicator.new({ Namespace = \"Inventory\", Data = ..., Targets = {} })\n\nClientReplicator.ForEach(\"Inventory\", function(replicator) ... end)\n```\n\nOmit it for an **anonymous** replicator. Anonymous replicators still work fully —\nthey're just intentionally unreachable by string search. Find them by `Id`, by tags,\nor with a predicate instead:\n\n```lua\n-- Anonymous — reachable only by Id, tags, or predicate.\nlocal rep = ServerReplicator.new({\n\tData = ...,\n\tTargets = {},\n\tTags = { Kind = \"Ephemeral\" },\n})\n\nClientReplicator.ForEach({ Kind = \"Ephemeral\" }, function(replicator) ... end)\n```\n\nSee [TR Discovery & Targeting](/api/TR%20Discovery%20&%20Targeting) for the full\nset of search conditions.\n\n### Opt-in collision safety with `TOKEN`\n\nIn a large codebase, two unrelated modules might accidentally pick the same\nnamespace string. A `ReplicationToken` claims a name **exclusively** so that can't\nhappen silently. Claim it once (usually at the top of a module), store it, and pass\nit as the `Namespace`:\n\n```lua\nlocal PlayerToken = ServerReplicator.TOKEN(\"PlayerData\")\n\nServerReplicator.new({ Namespace = PlayerToken, Data = ..., Targets = {} })\n\n-- Release the name once every replicator using it is destroyed:\nServerReplicator.TOKEN.destroy(PlayerToken)\n```\n\n`TOKEN` also exposes `TOKEN.new(name)` (identical to calling `TOKEN(name)`) and\n`TOKEN.get(name)` to fetch an already-registered token.\n\n:::caution Collision rules\nThe ownership ledger enforces these — each throws rather than failing silently:\n\n- `TOKEN(\"Name\")` throws if `\"Name\"` is already owned by another token.\n- `TOKEN(\"Name\")` throws if live replicators already use `\"Name\"` as a raw string.\n- Passing the raw string `\"Name\"` as a `Namespace` throws once a token owns it —\n  pass the **token object** instead.\n- `TOKEN.destroy(token)` throws if any replicator using that token is still alive.\n\nBecause of the second rule, claim your token at module load, **before** any\nreplicator with that name is created.\n:::\n\n### When should I use a token?\n\n| Situation | Recommendation |\n| --- | --- |\n| Small project, or a namespace only ever used in one place | A plain string is fine — tokens add nothing. |\n| Shared library, or a name used across several modules | Use a `TOKEN` so accidental reuse fails loudly. |\n| Discovery on the client | Either works — a token matches the same string a raw namespace would. |\n\nTokens are purely a **server-side** authoring safeguard; on the wire and on the\nclient a token is just its name string.\n\n---\n### See also\n\n- **[TR Getting Started](/api/TR%20Getting%20Started)** — where `Namespace` fits in the config table.\n- **[TR Discovery & Targeting](/api/TR%20Discovery%20&%20Targeting)** — matching by namespace, token, tags, or predicate.",
    "source": {
        "line": 85,
        "path": "lib/tablereplicator/src/Docs/TR_Namespaces_And_Tokens.luau"
    }
}