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

SUNC Instance Utilities

The Roblox Instance API is wide but not bottomless. Several properties are flagged as "not accessible to scripts", signal connections are normally write-only, and instances parented to nil aren't reachable from any tree walk. SUNC adds the primitives that let scripts touch all of that — read hidden properties, enumerate connections, find detached instances.

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

gethiddenproperty / sethiddenproperty

Roblox annotates some instance properties as security-locked or internal — they exist in the engine but Lua can't see them through normal access. gethiddenproperty reads those values anyway; sethiddenproperty writes them.

luau
gethiddenproperty(instance: Instance, property: string): (any, boolean)
sethiddenproperty(instance: Instance, property: string, value: any): boolean
Returns(any, boolean)
The property value followed by a boolean indicating whether the property was actually hidden (vs. just a normal accessible property).
local part = workspace:WaitForChild("TestPart")
local value, wasHidden = gethiddenproperty(part, "DataCost")
print(value, wasHidden)  --> 20  true

Common targets: DataCost, internal-only mesh attributes, replication flags, and a handful of debug properties Roblox exposes to internal tooling. None of these are documented externally, so what's actually accessible shifts as the engine evolves.

Setting hidden properties
Writing a hidden property only changes the client-side view of it. If the property is replicated from server, the server's value will overwrite yours on the next tick. Useful for one-frame effects; unreliable for durable state.

getconnections

Every Roblox event keeps a list of connected listeners internally. getconnections hands you that list as an array of connection objects you can inspect, disable, or fire manually.

luau
getconnections(signal: RBXScriptSignal): { Connection }

Each returned Connection has:

  • .Function — the listener closure.
  • .Thread — the coroutine the listener runs on (if applicable).
  • .Connected — whether it's still active.
  • :Fire(...) — invoke this listener immediately with the given arguments.
  • :Disable() / :Enable() — toggle without disconnecting.
  • :Disconnect() — remove the listener entirely.
luau
local part = workspace.LavaBrick
for _, conn in getconnections(part.Touched) do
    print(conn.Function)
    conn:Disable()  -- temporarily stop the lava
end
Returns{ Connection }
One entry per current listener on the signal. Order is the order they connected.

Two things this enables in practice. First, disabling damage on yourself locally by switching off the right .Touched handlers — a server with proper validation will catch the tampering, but in unauthoritative games it works. Second, replaying a signal: call :Fire on a stored connection to run its handler manually, bypassing whatever gating the game put around the real fire site.

firesignal

Fire all listeners of a signal directly, as if the engine had fired the event itself.

luau
firesignal(signal: RBXScriptSignal, ...: any): ()
Returnsvoid
Each connected listener is invoked synchronously with the supplied arguments.
firesignal(workspace.LavaBrick.Touched, workspace.CurrentCamera)
-- Every Touched handler runs as if the camera had touched the brick.

getnilinstances

When an instance has its Parent set to nil, it's still alive — it just isn't in the data model. Roblox uses this to stash instances out of the way: temporary objects, internal state, partially-built structures. getnilinstances enumerates all of them.

luau
getnilinstances(): { Instance }
Returns{ Instance }
Every instance currently parented to nil but still referenced somewhere.
for _, inst in getnilinstances() do
    print(inst.ClassName, inst.Name)
end

The classic use is finding hidden chat handlers, internal replication queues, or temporary GUIs the game has stashed here. A surprising amount of game state ends up here in larger codebases.

fireproximityprompt

ProximityPrompts are normally fired by the player holding the interaction key. This bypasses the player input requirement and fires the prompt as if the local player had completed the interaction.

luau
fireproximityprompt(prompt: ProximityPrompt): ()
Returnsvoid
Fires the prompt's Triggered signal as if the player had completed the prompt's full input gesture.
local prompt = workspace.Chest.Interaction
fireproximityprompt(prompt)  -- equivalent to holding E for the full duration

Note that this only works on the client. The server still receives the trigger event because that's how prompts are replicated, but if the server validates against expected timing (most don't — but some do), the difference can be detected.

getrawmetatable, setrawmetatable, getnamecallmethod

Lower-level metatable access. Roblox normally prevents scripts from changing instance metatables; SUNC unlocks it.

luau
getrawmetatable(obj: any): any
setrawmetatable(obj: any, mt: any): ()
getnamecallmethod(): string
setnamecallmethod(name: string): ()

getrawmetatable(obj) returns the metatable even if it's normally hidden. setrawmetatable lets you replace it. These are the primitives you build hooks on top of — usually through hookmetamethod, which is itself implemented in terms of these.

getnamecallmethod works only inside a namecall hook: it returns the name of the method that triggered the hook. Used to dispatch within a single __namecall override:

luau
local original
original = hookmetamethod(game, "__namecall", newcclosure(function(self, ...)
    local method = getnamecallmethod()
    if method == "Kick" and self == game.Players.LocalPlayer then
        return  -- silently drop only Kick calls
    end
    return original(self, ...)
end))

Reflection helpers: getgenv / getrenv / getsenv

Three environment getters. All return the global table for a different context — useful when you need to add globals visible to specific scripts.

  • getgenv() — the executor's shared global table. Everything you put here is visible to every executor script that runs after.
  • getrenv() — Roblox's own environment. Contains the standard library, services, and game-side globals.
  • getsenv(script: LuaSourceContainer) — the environment of a specific script. Useful for reading a game script's locals after it ran.
luau
getgenv().myCache = {}
-- Visible to every subsequent script run inside the executor

local serverScript = game.ServerScriptService.Combat
local env = getsenv(serverScript)
print(env.MAX_HEALTH)  -- read a local from another script

Putting it together

A small example combining several of these: a "reveal all hidden GUIs" one-liner that pulls every ScreenGui out of nil-parented limbo and reparents it under CoreGui where you can see it.

luau
local coreGui = cloneref(game:GetService("CoreGui"))
for _, inst in getnilinstances() do
    if inst:IsA("ScreenGui") then
        inst.Parent = coreGui
    end
end

One reflection call (getnilinstances), one cache alias (cloneref), three lines, all hidden UIs made visible. That density is the point of these utilities — each one does a single small thing the standard Roblox API doesn't.

Wrap-up

The instance-utility surface is the "long tail" of SUNC: a lot of small functions, each addressing a specific gap in the stock Roblox API. They're the kind of thing you don't miss until you need one, and then you reach for the docs and there it is. Skim them once so you know the shape; come back when a real problem matches.

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.