atherhubather.hub
Back to Guides
SUNC
9 min read
May 11, 2026

The SUNC cache library

Roblox caches its instance references in a C-level lookup table. That cache is invisible to most scripts, but when you're doing anti-detection work or trying to give a function an alternate identity, you need to manipulate it. The SUNC cache library is how you do that.

Ather
Ather
Lead developer at Atherhub. Writes about Roblox internals, Luau, script engineering, and platform security.Last updated May 11, 2026

Why a cache exists at all

Every time a script accesses a Roblox instance — say, game:GetService("Players") — the engine could create a fresh Lua userdata value, but that would be wasteful: the same service would have a different identity on every call. Instead, Roblox interns instances in a C-side cache keyed by the underlying object pointer. The first lookup creates the Lua-side handle, every subsequent lookup returns the same one.

That's why game:GetService("Players") == game:GetService("Players") is reliably true. It's also why an instance that gets destroyed and recreated will (usually) produce a different Lua handle on lookup — the cache entry was invalidated when the C object went away.

cache.iscached

The simplest entry point. Asks: is this instance currently in the cache?

luau
cache.iscached(instance: Instance): boolean
Returnsboolean
True if the instance is in the C-side cache (the normal state), false otherwise.
print(cache.iscached(game.Workspace))  --> true

You almost never call this in isolation. It's most useful as a debugging tool when you've been calling cache.invalidate and want to confirm the invalidation actually landed.

cache.invalidate

Remove an instance from the cache. The instance still exists in the underlying C engine; what changes is that the next Lua lookup of the same C object will create a new Lua handle instead of returning the old one.

luau
cache.invalidate(instance: Instance): ()
Returnsvoid
No return value. The instance is removed from the lookup table; existing references continue to work.
local players = game:GetService("Players")
cache.invalidate(players)

local fresh = game:GetService("Players")
print(players == fresh)  --> false  (different Lua handle for the same C object)

This is the trick that creates two distinct Lua values for the same underlying instance. players and fresh point at the same Roblox service, but Lua equality is false. Useful when you want to hold onto a "hidden" reference that won't equal anything the game subsequently retrieves.

Don't leak invalidated handles
The instance you invalidated is still real, but the cache is gone. If you stash both references and rely on identity elsewhere, you can confuse your own code as readily as you confuse detection.

cache.replace

Substitute one instance for another in the cache, so that lookups for the original return your substitute instead.

luau
cache.replace(original: Instance, replacement: Instance): ()
Returnsvoid
No return value. Future Lua lookups that would have resolved to original now resolve to replacement.
local fake = Instance.new("Folder")
cache.replace(workspace.RealFolder, fake)

print(workspace.RealFolder == fake)  --> true

This is the heavier sibling of invalidate. Use it sparingly: it changes what the rest of the script sees from a fundamental data-model lookup, so any code that walks the workspace will get the replacement instead of the real instance.

cache.clonefunction (cloneref)

The most commonly-used cache primitive in real scripts is cloneref — sometimes spelled cache.clonefunction in older docs, butcloneref in SUNC. It returns a new Lua handle pointing at the same underlying instance, without invalidating the cache.

luau
cloneref(instance: Instance): Instance
ReturnsInstance
A second Lua handle for the same C object. cloneref(x) ~= x, but both refer to the same instance and both produce identical property reads.
local players = game:GetService("Players")
local alias = cloneref(players)

print(players == alias)              --> false
print(alias.LocalPlayer == players.LocalPlayer)  --> true

The everyday use case: keep a private reference to a service that the game can't fingerprint against the cached version. You can stash cloneref(game:GetService("CoreGui")) at script startup; later, if the game replaces the cached CoreGui (or hooks identity comparisons against it), your alias still works.

compareinstances: identity-safe comparison

Because cloneref makes == unreliable for "is this the same instance" checks, SUNC ships a dedicated comparator.

luau
compareinstances(a: Instance, b: Instance): boolean
Returnsboolean
True if a and b refer to the same underlying C object, regardless of how many cloneref or cache.invalidate calls have happened in between.
local a = game:GetService("Players")
local b = cloneref(a)

print(a == b)                  --> false
print(compareinstances(a, b))  --> true

Use compareinstances any time your script needs to check "is this the same Roblox object?" without worrying about which handle each variable holds.

A real-world pattern: hidden gui parents

Here's a classic combined use of cloneref and cache.invalidate. The goal: parent a UI under CoreGui without leaving traces that the game can enumerate.

luau
local coreGui = cloneref(game:GetService("CoreGui"))
local screen = Instance.new("ScreenGui")
screen.Name = "_"  -- intentionally empty-looking
screen.Parent = coreGui
screen.ResetOnSpawn = false

-- The game enumerates game:GetService("CoreGui"):GetChildren().
-- That call returns the cached CoreGui, whose children are scanned —
-- but our alias was cloned, so this code path doesn't expose the alias
-- as a fingerprintable identity for our screen.

The combination matters because both coreGui and the cached CoreGui still refer to the same instance — so the screen is genuinely parented there. The handle our script holds is just a separate Lua-side view of it, which is enough to avoid certain identity-based detections.

What the cache doesn't protect you from

It's tempting to think of cloneref as invisibility, but it isn't. A game that walks :GetChildren() on the parent still sees your instance — handles are about identity, not visibility. If you parent something under CoreGui, it's findable by anyone who walks CoreGui's children. The cache library helps with identity comparisons; concealment is a separate problem.

Wrap-up

The cache library is small but it sits between Lua and a C-side detail most scripts never touch. Use cloneref for private aliases, cache.invalidate when you want subsequent lookups to produce a fresh handle, and compareinstances any time identity matters. cache.replace is the nuclear option — useful, but rarely needed.

Ather
Written by Ather

Ather is the lead developer behind Atherhub. He's been writing Luau and Roblox tooling for the better part of a decade, with a focus on the messy interface between game-script internals and the platforms that host them. Have feedback on this article? Drop it in the Discord.