I can share what I have done, and yes using a dictionary<id, ref> is fine. (DISCLAIMER: I have written my own netcode, but I think you could use the same ideas with NGO.)
Every Asset in my game that instantiates will self registers in its Start() to a singleton registry of Asset identifiers, which is simply a Dictionary<ushort, AssetPrefab>. The identifiers have the range [0100…FFFF]. (Player identifiers hold the range [0000…00FF]). They are automatically generated by the code whenevere an Asset is instantiated.
The identifier value to be assigned to the next Asset resets to 0x0100 at the start of each match prior to the loading of the map itself. The map loading must instantiate any Assets that are part of the map; then the Pawns and their loadout Assets are instantiated later during Pawn spawning. All Clients have the same map and same loadouts, so their identifiers are deterministic at the time of instantiation.
I would never allow typing in an identifier during development nor during game play. That doesn’t make sense. (And at the moment, I don’t see the identifier in the inspector, but that is something I have on my TODO list as a read only field.)
If I want to reference a class of Assets, say a frag grenade, I would use the enum Identifiers.Frag. I cannot compile the code if I misspell the enum item, so I cannot typo a bug into the code.
As for RPC, I haven’t implemented an RPC feature at this time in my netcode, because I haven’t really needed one yet. I set a property value and the netcode layer replicates it across the wire as I need it to. (In practice, a property setter can be thought of as an RPC call for my netcode.)
Hope this helps.
EDIT 8/26/23: Some updates. I changed the way my pawn spawns with loadouts. Instead of deterministically deriving identifiers for the loadouts, I send a spawn message to all clients well ahead of the spawn action with all loadout types and their identifiers. All Clients then know what to spawn and what identifiers to assign the pawn and the loadouts attached to the spawn. This makes for a flexible spawn mechanism where I can have any number and any types of loadouts upon spawning a pawn. And the pawn instantiates on the Clients upon receiving their first Pawn state message from the Authority.
I have implemented a read only field with the Asset identifier in the inspector, it wasn’t difficult to do, just had to follow a video, about 30 minutes max implementation.
I still use property setters as a pseudo RPC call mechanism, warming up to the idea of a natural syntactical RPC method, but not sure how to implement it without doing the NfGO approach, which I am not a fan of. Their approach seems to be assuming too much making a rigid and thus fragile approach.