Not really sure how to phrase the title, but to elaborate:
I’m currently working on a game, and while it’s intended to be highly cooperative and PvE, I’m still trying to put into practice some secure coding methods.
I have a library of items currently in ServerScriptService, set up like so:

This library used to be in ReplicatedStorage, which made it much easier to access, but to my understanding is unsafe.
- The Inventory folder houses various information and assets to be used in my spatial grid inventory system
- The Prop folder houses a model of the item that will be placed in workspace when the item is dropped from an inventory, for example
- The Tool folder houses the functional tool of the item to be used in gameplay.
Less importantly, the PropSetup script is there to handle automatic welding of the props for each item at runtime.
Inside each prop is a script to handle being clicked on and sending a request to the inventory to be added, with a parameter passing the “Inventory” folder. This is done through a RemoteFunction; Server → Client → Server:
Server:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
script.Parent.InteractConnect.OnServerEvent:Connect(function(player)
local success = ReplicatedStorage.InventoryComm.AttemptAddItem:InvokeClient(player, ServerScriptService.ItemLibrary.Items.FAL.Inventory)
if success then
script.Parent.Parent:Destroy()
end
end)
Client:
replicatedStorage.InventoryComm.AttemptAddItem.OnClientInvoke = function(item)
print(item)
local ConfigClone = item.Config:Clone()
Client error:
Unfortunately, as I now understand, the client cannot see any objects passed from the server. My old understanding was that you just couldn’t index anything server-only from the client, but that is untrue.
I can get around this by cloning the object from the same server script, parenting it somewhere replicated and then sending that over but I feel like that’s super unlucrative.
I also thought about making two different folders, one placed in ReplicatedStorage with less critical information like inventory information and the prop, and then I thought that if an exploiter had access to the prop, they could “spawn” the prop in and then pick it up so it is placed in the custom inventory, and then equip it to gain access to the tool that way.
What’s the best process here? Did I go the wrong route of hoping to handle a lot of the information on the client (passing the whole “Inventory” folder), rather than only passing what is needed? I’m open to reformat my code, only passing values and whatnot. Thanks in advance.