<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[HangarBay]]></title><description><![CDATA[Obsidian digital garden]]></description><link>http://github.com/dylang/node-rss</link><image><url>site-lib/media/favicon.png</url><title>HangarBay</title><link></link></image><generator>Webpage HTML Export plugin for Obsidian</generator><lastBuildDate>Wed, 04 Feb 2026 04:53:29 GMT</lastBuildDate><atom:link href="site-lib/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Wed, 04 Feb 2026 04:53:27 GMT</pubDate><ttl>60</ttl><dc:creator></dc:creator><item><title><![CDATA[1 - Welcome]]></title><description><![CDATA[ <img alt="Keeper Of Tomes" src="assets/hangarbay.png" referrerpolicy="no-referrer" target="_self" style="max-width: 100%;"> The first secure, Stride3D focused modding system designed to package, validate, and load scripts, assets, and behaviors without trusting user input.HangarBay is a mod development system for Stride3D, created for the XRUIOS but easily usable by other programs. Atop this, the framework of HangarBay makes the system nearly engine agnostic.HangarBay uses Pariah Cybersecurity, promising PQC Cryptographic Resistance.HangarBay separates mod creation, mod validation, and mod execution into clearly defined stages, ensuring unsafe or malformed content never reaches the engine runtime. First, developers create a Mod Type, which is signed publicly and has a ruleset regarding what asset types are/aren't allowed and what DLLs are/aren't allowed. Developers then create the ModType, which allows them to create Mods following the rules set by the application developer. They can validate the ModType through it's Public Signature. Finally, users install the mods onto their systems, with the application handling the mods as the developer wishes. Users can enable/disable and uninstall mods. Scripts are compiled and loaded as DLLs Assets are loaded dynamically at runtime through content manager Because of the way HangarBay works, you can save items as a prefab which can then be loaded into the scene (Scripts and all) HangarBay follows three rules:
Never trust mods
Always validate
Make creation easy, execution strict
It is designed for scale, experimentation, and public mod ecosystems.
“Mods should be powerful, not dangerous.” Pariah Cybersecurity (And it's dependencies)
Stride3D (Or Stride.CommunityTooklit for minimalists)
DouglasDwyer.CasCore
HangarBay does not rely on editor-time compilation.At runtime:
Script DLLs are loaded
Prefabs and assets are deserialized
Components bind automatically
Execution hooks are called
If a mod contains a spinning cube prefab, the cube spins. No special handling required.HangarBay includes several layers of protection to keep mods safe and isolated like: CAS for Simple Yet Effective Policy Setting User-specific HMAC-SHA256 signatures verify mod activation files (.enabled + .sig) — only the user who enabled the mod can make it work. AES-256 encryption protects all stored secrets (including post-quantum signing keys) inside an encrypted secret bank. Each mod runs with its own isolated ContentManager and virtual filesystem root — no asset overwriting or path conflicts between mods. Strict mod type rules enforce allowed / required / forbidden file extensions and DLL names before a mod is loaded. Mod DLLs are checked for: Strong-name / public key match against trusted versions
Exact SHA256 hash comparison Outdated or incompatible mod DLLs can be replaced with newer system versions. Mod type definitions are cryptographically signed using post-quantum algorithms (resistant to future quantum attacks). Mod metadata (images, thumbnails, etc.) includes xxHash values to detect tampering or unauthorized replacement. Secrets are tied to a user passphrase + device fingerprint — keys cannot be easily stolen or moved to another machine. No global asset merging — every mod’s content stays fully separated from the engine and other mods. HangarBay is engine-agnostic by design, focused on Stride3D.However, it is not impossible to make small tweaks and use it on Unity or Godot. In fact, it's likely possible to use with a far greater array of engines thanks to P/Invoke.Mod developers:
Do not touch engine internals Do not write custom loaders Do not need editor plugins They simply:
Choose a Mod Type
Add assets and scripts
Build the mod
Drop it into Mods/
<br>Code: <a data-tooltip-position="top" aria-label="https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-MPL-2.0" rel="noopener nofollow" class="external-link is-unresolved" href="https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-MPL-2.0" target="_self">NON-AI MPL 2.0</a>
Artwork: — NO AI training. NO reproduction. NO exceptions.<br><img style="margin-left: 20px; margin-bottom: 20px;" align="center" src="https://github.com/Walker-Industries-RnD/Malicious-Affiliation-Ban/blob/main/WIBan.png?raw=true" referrerpolicy="no-referrer" target="_self" class="is-unresolved">
Unauthorized use of the artwork — including but not limited to copying, distribution, modification, or inclusion in any machine-learning training dataset — is strictly prohibited and will be prosecuted to the fullest extent of the law.
]]></description><link>1-start-here/1-welcome.html</link><guid isPermaLink="false">1 - Start Here/1 - Welcome.md</guid><pubDate>Wed, 04 Feb 2026 04:42:47 GMT</pubDate><enclosure url="assets/hangarbay.png" length="0" type="image/png"/><content:encoded>&lt;figure&gt;&lt;img src=&quot;assets/hangarbay.png&quot;&gt;&lt;/figure&gt;</content:encoded></item><item><title><![CDATA[2 - Mod Handling (CasCore Policy)]]></title><description><![CDATA[When a mod ships a .dll in its Scripts/ folder, HangarBay does not just load it blindly.
Instead, it loads the DLL through CasCore, which:
Rewrites the DLL's bytecode (using Mono.Cecil) before it ever runs
Inserts runtime security checks around every external field read/write, method call, reflection usage, delegate creation, lambda compilation, etc.
Enforces a whitelist you define per mod type — anything not explicitly allowed throws System.Security.SecurityException at runtime
This means even if a mod author (or a malicious user) slips a bad DLL past validation, it still can't:
Write to files (File.WriteAllText, Directory.CreateDirectory, etc.)
Open network connections (HttpClient, sockets)
Spawn processes (Process.Start)
Use dangerous reflection (Type.InvokeMember, Assembly.Load, Reflection.Emit)
Mess with unsafe memory or unverifiable IL
Default behavior (when no whitelist is set):
Only safe parts of the .NET base class library are allowed (math, collections, strings, basic structs). Mod Type Defines the Whitelist
In ModTypeDetails you set the AllowedTypesOrMembers dictionary:
AllowedTypesOrMembers = new Dictionary&lt;string, List&lt;string&gt;&gt;
{ ["System.Math"] = new() { "*" }, // all math functions ["Stride.Core.Mathematics.Vector3"] = new() { "*" }, // full vector math ["Stride.Engine.TransformComponent"] = new() { "*" }, // position, rotation, scale ["Stride.Engine.Entity"] = new() { "Transform", "GetComponent" }, // limited entity access // Dangerous stuff is blocked by simply not being listed
}; This gets saved in testmod.ModStyleMetaData. When the User Enables the Mod
FrontFacing.EnableMod(modDirectory, modId, userKey) does: Creates .enabled + HMAC .sig (user consent)
Registers isolated ContentManager for assets
Calls LoadModScripts(modDirectory, modId) LoadModScripts Applies CasCore Sandbox
Inside LoadModScripts:
// Load mod type config (contains the whitelist)
var modTypeDetails = LoadModTypeDetailsForMod(modDirectory); // Build policy
var policyBuilder = new CasPolicyBuilder() .WithDefaultSandbox(); // safe BCL basics if (modTypeDetails?.AllowedTypesOrMembers != null)
{ policyBuilder.ApplyModTypeConfig(modTypeDetails.AllowedTypesOrMembers);
} var policy = policyBuilder.Build(); // Create sandboxed loader
var loader = new CasAssemblyLoader(policy, isCollectible: true);
modLoaders[modId] = loader; // for later unload // Load every DLL under this policy
foreach (var dllPath in Directory.GetFiles(scriptsDir, "*.dll", SearchOption.AllDirectories))
{ var assembly = loader.LoadFromAssemblyPath(dllPath); // DLL is now sandboxed — scripts can run but only within whitelist
} What Happens at Runtime? Mod script inherits SyncScript / AsyncScript and runs Update()
Every time it tries to call a method/field/property not whitelisted → SecurityException
Example: entity.Transform.Position += new Vector3(1,0,0); → allowed
File.WriteAllText("cheat.txt", "godmode"); → blocked, exception thrown DisableMod Cleans Up
FrontFacing.DisableMod calls loader.Unload() on the CasAssemblyLoader → removes the sandboxed assembly from memory (collectible context). Mod makers
Your scripts can use anything in the mod type's whitelist.
If you need more access (e.g. audio playback, custom UI), ask the game dev to expand the mod type's AllowedTypesOrMembers. Users
Mods run in a sandbox — they can't delete your files, steal data, or crash the system unless the game developer explicitly allowed it in the mod type. ... I should make a default list of classes, shouldn't I.]]></description><link>3-extras/2-mod-handling-(cascore-policy).html</link><guid isPermaLink="false">3 - Extras/2 - Mod Handling (CasCore Policy).md</guid><pubDate>Wed, 04 Feb 2026 04:37:00 GMT</pubDate></item><item><title><![CDATA[3 - Loading and Using A Mod]]></title><description><![CDATA[This is the most intensive part of the mod system; the final line between the developer and the player. We can assume that at runtime, the mod should be at[Game Install Folder]/Mods/
For this to work, we do several things!All you need to do is validate the mod!await ModValidation.ValidateMod(modRoot, AppContext.BaseDirectory, false);
The system will automatically handle security on pretty much all fronts!Atop this, you can change the warn only bool to true (which will warn a user if a mod is invalid but run it anyways) or false (which will not allow a mod to run if it's invalid)!Moreover, if the developer included DLLs in the class which exist on the system already, we use the system version instead of the version shipped with the mod!Want to check against specific DLLs? You can place them in "Engine/Security/DLLChecks" Everything in this folder will be checked against, ensuring they don't exist within the mod!
For enabling/disabling mods, enabling a mod creates a signed file we use to dictate that the mod we are looking at is enabled!You can check FrontFacing.cs for a few functions, but in general;This should be continuous; I made a function you can view in the <a data-href="1 - PrototypeUserKey" href="3-extras/1-prototypeuserkey.html" class="internal-link" target="_self" rel="noopener nofollow">1 - PrototypeUserKey</a> page (although it would not work properly for some reason so I removed it)This will be the key we use for managing the enabled/disabled mods list.byte[] userKey = userGuid.ToByteArray();
var chosenModPath = ModManager.ListMods();
await FrontFacing.EnableMod(modPath, "ModName", userKey);
Note 'IsModEnabled', 'LoadModScripts' and 'DisableMod'//Is the mod enabled by the user?
if (FrontFacing.IsModEnabled(modPath, userKey))
{ cm = FrontFacing.ModContentRegistry.GetOrCreate(modId, modPath); Console.WriteLine($"Stride ContentManager ready for enabled mod at: {modPath}"); //Always load the assemblies first FrontFacing.LoadModScripts(modRoot, modId); try { var prefab = await cm.LoadAsync&lt;Prefab&gt;("testPrefab"); var texture = await cm.LoadAsync&lt;Texture&gt;("testTexture"); Console.WriteLine($"Asset Loading Worked!"); //Now remove the assets on Stride3D and come back here to unload FrontFacing.DisableMod(modRoot, modId); } catch (Exception ex) { Console.WriteLine($"Failed to load asset: {ex.Message}"); }
} When using DisableMod, you must Destroy all instantiated entities from this mod's prefabs
Clear any cached script instances, event subscriptions, etc.
REMOVE ALL MOD ENTITIES
]]></description><link>2-how-to-use/3-loading-and-using-a-mod.html</link><guid isPermaLink="false">2 - How To Use/3 - Loading and Using A Mod.md</guid><pubDate>Wed, 04 Feb 2026 04:29:39 GMT</pubDate></item><item><title><![CDATA[2 - Creating A Mod]]></title><description><![CDATA[Now, let's create a mod!We can turn ANYTHING into a mod as long as it's a folder. The importance of this and what it entails will come later.For now, how exactly do we create a mod?Optimally, this should be the Stride3D folder containing things like images, models, JSONs, DLLs and .prefab files.For loading items with scripts in them, you should use the .prefab file!Also, there should be a Prefabs, Assets and Scripts folder.This is extremely simple! Simply useawait ModCreator.ConvertFolderToMod( inputFolder: tempInput, modName: "Test Mod", thisModType: "testmod", //What ModType is this? description: "None Provided.", authorName: "None Provided.", authorWebsite: "None Provided.", modImagePath: new List&lt;string&gt;() //REMEMBER RELATIVE );
ModDetails doesn't include sandbox config — that's in ModTypeDetails for the type.You could choose to leave these as is or sign these to validate the mods! I didn't place this in by default as there are so many ways to do this, but I highly a system for this if you don't want mods being tampered with.I highly suggest trusting that the user installs the mod from a trusted place, then creating a hash of all of the files for checking async whenever you want to use it. You could also use KeeperOfTomes to observe whenever the files are changed and if any changes match an updated hash. Failure to add this could mean someone could add a bad script which would load alongside legit mod data.Mods/
└── YourModName/ ├── Assets/ ← all non-code files from input ├── Prefabs/ ← prefabs copied from input ├── Scripts/ ← DLL copied here (optional) ├── mod.json ← manifest file describing the mod (id, DLL, prefabs) └── &lt;modId&gt;.moddetails ← additional metadata: name, type, author, hash, created date This supports asset only based mods! (Mainly just the default Stride system beautified); it auto tags this!Let's say you have a program which is built around modding and you want to create a ton of default mods for your app. However, you don't want to go through the trouble of creating 100 different projects for this.This is the power of a Hangar Bay; it allows you to store multiple things within the same bay to be executed. With this system, you can create subfolders in your project and turn it into a mod! As an example, you might haveXRUIOS/
└── DefaultApps/ ├── Calendar ├── Timer ├── DesktopMirror ├── Stopwatch ├── Settings └── Window Manager All you need to do is ensure each has a Prefabs, Assets and Scripts folder.Now, for each you can use await ModCreator.ConvertFolderToMod( inputFolder: XRUIOS/DefaultApps/Stopwatch, modName: "Stopwatch"
);
and you will get an exported mod for each!Scripts in DLLs will load under CasCore sandbox based on the ModType's AllowedTypesOrMembers whitelist.]]></description><link>2-how-to-use/2-creating-a-mod.html</link><guid isPermaLink="false">2 - How To Use/2 - Creating A Mod.md</guid><pubDate>Wed, 04 Feb 2026 04:28:22 GMT</pubDate></item><item><title><![CDATA[1 - Creating A ModType]]></title><description><![CDATA[When handling the ModSystem, there are generally 9 steps to go from a few notes on paper to a finalized systems users on end devices can use. This is a general flow of how that would work, piece by piece.Note: You can have multiple modtypes in the same game!You are going to need two things moving forward;
A Bank Name (For "Secrets") and
A Data Phrase (Or a Phrase only this program knows; think UUID)
This can be anything you can think of; however, the Data Phrase should not be something easy to break. I highly recommend using a nice and long password, then saving it to a password manager.var BankName = "test".ToSecureData();
var DataPhrase = "test".ToSecureData();
We want these as SecureData, which protects the data from tampering; you will know this is true because trying to view the variables in editor will have them appear as<img alt="Pasted image 20260203181933.png" src="assets/pasted-image-20260203181933.png" target="_self">Note that this is not equal to "test", but if we run a simpleConsole.WriteLine(DataPhrase.ConvertToString());
We can see our value<br><img alt="Pasted image 20260203182159.png" src="assets/pasted-image-20260203182159.png" target="_self">This is an advantage of Pariah Cybersecurity (Which is cross compatible).You can ignore the warning!await Initializer.InitializeSecureData(BankName, DataPhrase);
This simple block handles creating a Secure Bank we will use for our mods. It will be created in your documents folder, under the subfolder "Stride3D Secret Banks". You can however insert a third variable (A path) to override this and build the directory wherever you want.NEVER, EVER share your Bank with ANYONE; this will allow them unlimited access to editing the ModType which places you, mod developers and users at risk of very, very dangerous exploits.Now, we will create two things; a ModMetadata (which contains information on the mod) and a ModRules (which contains the actual rules for Mods).
//Create a new ModMetaData var metadata = new ModMetadata( modName: "Test Mod Type", modDescription: "Smoke test mod type", modWebsite: null, authorName: "Test", authorBio: null, authorWebsite: null, modImagePath: null, authorImagePath: null, modVersion: "1.0.0"
); //Create a new ModRules var rules = new ModRules( mustInclude: new[] { "" }, mustExclude: new[] { "" }, mustExcludeDLL: new[] { "evil.dll" }, defaultCalls: new[] { "Initialize" }, publicDefaultCalls: new[] { "GetInfo" }
); Keep in mind ModMetadata is not the public facing ModType info and is used more for recordkeeping.This is the stuff the user sees! Or rather should
var modTypeDetails = new ModTypeDetails( Name: "Test Mod Type", Description: "Smoke test mod type", Extension: "testmod", Website: null, AuthorName: "Test", AuthorWebsite: null, TypeImagePath: new List&lt;string&gt;(), Hash: new List&lt;ulong&gt;(), AllowedTypesOrMembers: ModTypeDetails.RecommendedGameplayDefaults // Whitelist for sandbox (customize as needed)
); The images should be placed relative to the ModType (Within the same directory)! The media is auto exported for the most part!TypeImagePath are mainly images, but you can technically do something like making description a markdown file and having a gallery UI only accept image filetypes. The Markdown file will still get auto hashed later!*Finally, we create the ModType!var newModType = await ModTypeHandler.CreateModType( modExtension: "testmod", modVersion: "1.0.0", metadata: metadata, rules: rules, bankName: BankName, dataPhrase: DataPhrase, modTypeDetails: modTypeDetails
);
Note: Check "ModTypeHandler for deletion and updating functionsThis will create several files; The ModType file (which contains the general bulk of information) The Public Key (which we give to the player and mod developers, make sure we don't trust a copy of it but ship the public key directly) The signature key (Same as above, however this is used to ensure nobody tampered with the ModType file) and The metadata info (which contains the information displayed on the UI) The output will overall look likeModStyles/ ├─ MyType.ModStyle ├─ MyType.ModStyleMetaData ├─ Images/ │ ├─ icon.png │ └─ banner.png ]]></description><link>2-how-to-use/1-creating-a-modtype.html</link><guid isPermaLink="false">2 - How To Use/1 - Creating A ModType.md</guid><pubDate>Wed, 04 Feb 2026 04:27:12 GMT</pubDate><enclosure url="." length="0" type="false"/><content:encoded>&lt;figure&gt;&lt;img src=&quot;.&quot;&gt;&lt;/figure&gt;</content:encoded></item><item><title><![CDATA[1 - PrototypeUserKey]]></title><description><![CDATA[This is a thing I wanted to include in the library, but it would not work! If you can figure this out, I would love to put it in the DLL!
using Pariah_Cybersecurity;
using System;
using System.IO;
using System.Threading.Tasks;
using Walker.Crypto;
using WISecureData;
using static Pariah_Cybersecurity.DataHandler;
using static Pariah_Cybersecurity.DataHandler.SecretManager; namespace HangarBay
{ public static class PlayerSecrets { public static async Task&lt;SecureData&gt; GetKey(string bankFolder, SecureData password) { // Base folder: VS debug/bin folder string baseFolder = AppContext.BaseDirectory; string bankDir = Path.Combine(baseFolder, bankFolder); Directory.CreateDirectory(bankDir); // make sure folder exists var bankName = "PlayerBank".ToSecureData(); var masterKey = "PlayerMasterKey".ToSecureData(); // Ensure bank exists if (!await DataHandler.SecretManager.CheckIfBankExists(bankDir, bankName.ConvertToString())) { await DataHandler.SecretManager.CreateBank(bankDir, bankName.ConvertToString(), null, masterKey.ConvertToString()); } SecureData secret = default; try { // Get the secret if it exists secret = await DataHandler.SecretManager.GetPublicSecret(bankDir, bankName.ConvertToString(), "Secrets", masterKey.ConvertToString()); // Decrypt it using the password you provide var aesText = SimpleAESEncryption.AESEncryptedText.FromString(secret.ConvertToString()); var decryptedBytes = await AsyncAESEncryption.DecryptBytesAsync(aesText, password); secret = await BinaryConverter.NCByteArrayToObjectAsync&lt;SecureData&gt;(decryptedBytes); return secret; } catch { // Secret missing? Create a new one var newSecret = Guid.NewGuid().ToString().ToSecureData(); // Encrypt it with password var secretBytes = await BinaryConverter.NCObjectToByteArrayAsync(newSecret); var encryptedBytes = await AsyncAESEncryption.EncryptBytesAsync(secretBytes, password); var encryptedString = encryptedBytes.ToString().ToSecureData(); // Create PublicKeyFileInit properly var privKeyData = new DataHandler.SecretManager.PublicKeyFileInit( "Secrets", null, encryptedString ); // Add secret to bank await DataHandler.SecretManager.AddPublicSecret(bankDir, bankName.ConvertToString(), privKeyData, masterKey.ConvertToString()); return newSecret; } } }
} ]]></description><link>3-extras/1-prototypeuserkey.html</link><guid isPermaLink="false">3 - Extras/1 - PrototypeUserKey.md</guid><pubDate>Wed, 04 Feb 2026 03:28:57 GMT</pubDate></item><item><title><![CDATA[Pasted image 20260203182159]]></title><description><![CDATA[<img src="assets/pasted-image-20260203182159.png" target="_self">]]></description><link>assets/pasted-image-20260203182159.html</link><guid isPermaLink="false">Assets/Pasted image 20260203182159.png</guid><pubDate>Tue, 03 Feb 2026 23:21:59 GMT</pubDate><enclosure url="." length="0" type="false"/><content:encoded>&lt;figure&gt;&lt;img src=&quot;.&quot;&gt;&lt;/figure&gt;</content:encoded></item><item><title><![CDATA[Pasted image 20260203181933]]></title><description><![CDATA[<img src="assets/pasted-image-20260203181933.png" target="_self">]]></description><link>assets/pasted-image-20260203181933.html</link><guid isPermaLink="false">Assets/Pasted image 20260203181933.png</guid><pubDate>Tue, 03 Feb 2026 23:19:33 GMT</pubDate><enclosure url="." length="0" type="false"/><content:encoded>&lt;figure&gt;&lt;img src=&quot;.&quot;&gt;&lt;/figure&gt;</content:encoded></item><item><title><![CDATA[WalkerDev]]></title><description><![CDATA[<img src="assets/walkerdev.png" target="_self">]]></description><link>assets/walkerdev.html</link><guid isPermaLink="false">Assets/WalkerDev.png</guid><pubDate>Mon, 02 Feb 2026 19:54:05 GMT</pubDate><enclosure url="assets/walkerdev.png" length="0" type="image/png"/><content:encoded>&lt;figure&gt;&lt;img src=&quot;assets/walkerdev.png&quot;&gt;&lt;/figure&gt;</content:encoded></item><item><title><![CDATA[Kennaness]]></title><description><![CDATA[<img src="assets/kennaness.png" target="_self">]]></description><link>assets/kennaness.html</link><guid isPermaLink="false">Assets/Kennaness.png</guid><pubDate>Mon, 02 Feb 2026 19:54:05 GMT</pubDate><enclosure url="assets/kennaness.png" length="0" type="image/png"/><content:encoded>&lt;figure&gt;&lt;img src=&quot;assets/kennaness.png&quot;&gt;&lt;/figure&gt;</content:encoded></item><item><title><![CDATA[HangarBay]]></title><description><![CDATA[<img src="assets/hangarbay.png" target="_self">]]></description><link>assets/hangarbay.html</link><guid isPermaLink="false">Assets/HangarBay.png</guid><pubDate>Mon, 02 Feb 2026 19:53:49 GMT</pubDate><enclosure url="assets/hangarbay.png" length="0" type="image/png"/><content:encoded>&lt;figure&gt;&lt;img src=&quot;assets/hangarbay.png&quot;&gt;&lt;/figure&gt;</content:encoded></item></channel></rss>