Index: binary-improvements2/WebServer/src/WebAPI/APIs/Command.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/Command.cs	(revision 425)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/Command.cs	(revision 426)
@@ -3,4 +3,5 @@
 using JetBrains.Annotations;
 using Utf8Json;
+using Webserver.Permissions;
 
 namespace Webserver.WebAPI.APIs {
@@ -130,5 +131,5 @@
 		}
 
-		public override int DefaultPermissionLevel () => 2000;
+		public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
 	}
 }
Index: binary-improvements2/WebServer/src/WebAPI/APIs/Mods.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/Mods.cs	(revision 426)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/Mods.cs	(revision 426)
@@ -0,0 +1,91 @@
+using JetBrains.Annotations;
+using Utf8Json;
+using Webserver.Permissions;
+
+namespace Webserver.WebAPI.APIs {
+	[UsedImplicitly]
+	public class Mods : AbsRestApi {
+		private readonly byte[] loadedWebMods;
+
+		public Mods (Web _parent) {
+			JsonWriter writer = new JsonWriter ();
+			writer.WriteBeginArray ();
+
+			for (int i = 0; i < _parent.webMods.Count; i++) {
+				WebMod webMod = _parent.webMods [i];
+
+				if (i > 0) {
+					writer.WriteValueSeparator ();
+				}
+				
+				writer.WriteBeginObject ();
+
+				writeModJson (ref writer, webMod);
+
+				if (webMod.ReactBundle != null || webMod.CssPath != null) {
+					writer.WriteValueSeparator ();
+
+					writer.WritePropertyName ("web");
+					writer.WriteBeginObject ();
+					
+					string webModReactBundle = webMod.ReactBundle;
+					if (webModReactBundle != null) {
+						writer.WritePropertyName ("bundle");
+						writer.WriteString (webModReactBundle);
+					}
+
+					string webModCssFile = webMod.CssPath;
+					if (webModCssFile != null) {
+						if (webModReactBundle != null) {
+							writer.WriteValueSeparator ();
+						}
+
+						writer.WritePropertyName ("css");
+						writer.WriteString (webModCssFile);
+					}
+					
+					writer.WriteEndObject ();
+				}
+
+				writer.WriteEndObject ();
+			}
+
+			writer.WriteEndArray ();
+
+			loadedWebMods = writer.ToUtf8ByteArray ();
+		}
+
+		protected override void HandleRestGet (RequestContext _context) {
+			PrepareEnvelopedResult (out JsonWriter writer);
+			writer.WriteRaw (loadedWebMods);
+			SendEnvelopedResult (_context, ref writer);
+		}
+
+		public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
+
+		private void writeModJson (ref JsonWriter _writer, WebMod _webMod) {
+			_writer.WritePropertyName ("name");
+			_writer.WriteString (_webMod.ParentMod.Name);
+			
+			_writer.WriteValueSeparator ();
+			_writer.WritePropertyName ("displayName");
+			JsonCommons.WriteStringOrNull (ref _writer, _webMod.ParentMod.DisplayName);
+
+			_writer.WriteValueSeparator ();
+			_writer.WritePropertyName ("description");
+			JsonCommons.WriteStringOrNull (ref _writer, _webMod.ParentMod.Description);
+
+			_writer.WriteValueSeparator ();
+			_writer.WritePropertyName ("author");
+			JsonCommons.WriteStringOrNull (ref _writer, _webMod.ParentMod.Author);
+
+			_writer.WriteValueSeparator ();
+			_writer.WritePropertyName ("version");
+			JsonCommons.WriteStringOrNull (ref _writer, _webMod.ParentMod.VersionString);
+
+			_writer.WriteValueSeparator ();
+			_writer.WritePropertyName ("website");
+			JsonCommons.WriteStringOrNull (ref _writer, _webMod.ParentMod.Website);
+		}
+	}
+}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/Player.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/Player.cs	(revision 425)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/Player.cs	(revision 426)
@@ -256,5 +256,5 @@
 		}
 
-		public override int DefaultPermissionLevel () => 2000;
+		public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
 	}
 }
Index: binary-improvements2/WebServer/src/WebAPI/APIs/RegisterUser.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/RegisterUser.cs	(revision 425)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/RegisterUser.cs	(revision 426)
@@ -80,5 +80,6 @@
 			}
 			
-			// TODO: Check if username is already used!
+			// TODO: Check if username is already used by someone else!
+			// TODO: Remove existing username if player already had one!
 
 			AdminWebUsers.Instance.AddUser (username, password, regData.PlatformUserId, regData.CrossPlatformUserId);
@@ -95,5 +96,5 @@
 		}
 
-		public override int DefaultPermissionLevel () => 2000;
+		public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
 	}
 }
Index: binary-improvements2/WebServer/src/WebAPI/APIs/ServerStats.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/ServerStats.cs	(revision 425)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/ServerStats.cs	(revision 426)
@@ -2,4 +2,5 @@
 using Utf8Json;
 using Webserver.LiveData;
+using Webserver.Permissions;
 
 namespace Webserver.WebAPI.APIs {
@@ -47,5 +48,5 @@
 		}
 
-		public override int DefaultPermissionLevel () => 2000;
+		public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
 	}
 }
Index: binary-improvements2/WebServer/src/WebAPI/APIs/WebMods.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/WebMods.cs	(revision 425)
+++ 	(revision )
@@ -1,58 +1,0 @@
-using JetBrains.Annotations;
-using Utf8Json;
-
-namespace Webserver.WebAPI.APIs {
-	[UsedImplicitly]
-	public class WebMods : AbsRestApi {
-		private readonly byte[] loadedWebMods;
-
-		public WebMods (Web _parent) {
-			JsonWriter writer = new JsonWriter ();
-			writer.WriteBeginArray ();
-
-			for (int i = 0; i < _parent.webMods.Count; i++) {
-				WebMod webMod = _parent.webMods [i];
-				
-				if (i > 0) {
-					writer.WriteValueSeparator ();
-				}
-
-				writer.WriteBeginObject ();
-
-				writer.WriteString ("name");
-				writer.WriteNameSeparator ();
-				writer.WriteString (webMod.ParentMod.Name);
-
-				string webModReactBundle = webMod.ReactBundle;
-				if (webModReactBundle != null) {
-					writer.WriteValueSeparator ();
-					writer.WriteString ("bundle");
-					writer.WriteNameSeparator ();
-					writer.WriteString (webModReactBundle);
-				}
-
-				string webModCssFile = webMod.CssPath;
-				if (webModCssFile != null) {
-					writer.WriteValueSeparator ();
-					writer.WriteString ("css");
-					writer.WriteNameSeparator ();
-					writer.WriteString (webModCssFile);
-				}
-
-				writer.WriteEndObject ();
-			}
-
-			writer.WriteEndArray ();
-
-			loadedWebMods = writer.ToUtf8ByteArray ();
-		}
-
-		protected override void HandleRestGet (RequestContext _context) {
-			PrepareEnvelopedResult (out JsonWriter writer);
-			writer.WriteRaw (loadedWebMods);
-			SendEnvelopedResult (_context, ref writer);
-		}
-
-		public override int DefaultPermissionLevel () => 2000;
-	}
-}
Index: binary-improvements2/WebServer/src/WebAPI/APIs/WebUiUpdates.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/APIs/WebUiUpdates.cs	(revision 425)
+++ binary-improvements2/WebServer/src/WebAPI/APIs/WebUiUpdates.cs	(revision 426)
@@ -2,4 +2,5 @@
 using Utf8Json;
 using Webserver.LiveData;
+using Webserver.Permissions;
 
 namespace Webserver.WebAPI.APIs {
@@ -57,5 +58,5 @@
 		}
 
-		public override int DefaultPermissionLevel () => 2000;
+		public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
 	}
 }
Index: binary-improvements2/WebServer/src/WebAPI/AbsRestApi.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/AbsRestApi.cs	(revision 425)
+++ binary-improvements2/WebServer/src/WebAPI/AbsRestApi.cs	(revision 426)
@@ -10,6 +10,4 @@
 		private static readonly UnityEngine.Profiling.CustomSampler jsonDeserializeSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_Deserialize");
 
-		protected readonly string[] CachedPerMethodModuleNames = new string[(int)ERequestMethod.Count];
-
 		protected AbsRestApi (string _name = null) : this(null, _name) {
 		}
@@ -19,16 +17,6 @@
 
 		protected override void RegisterPermissions () {
-			base.RegisterPermissions ();
-			
-			for (int i = 0; i < (int)ERequestMethod.Count; i++) {
-				ERequestMethod method = (ERequestMethod)i;
-
-				if (method is not (ERequestMethod.GET or ERequestMethod.PUT or ERequestMethod.POST or ERequestMethod.DELETE)) {
-					continue;
-				}
-
-				CachedPerMethodModuleNames [i] = $"webapi.{Name}:{method.ToStringCached ()}";
-				AdminWebModules.Instance.AddKnownModule (CachedPerMethodModuleNames [i], DefaultMethodPermissionLevel (method));
-			}
+			AdminWebModules.Instance.AddKnownModule (new AdminWebModules.WebModule (CachedApiModuleName, DefaultPermissionLevel (),
+				DefaultMethodPermissionLevels (), true));
 		}
 
@@ -136,39 +124,31 @@
 
 		public override bool Authorized (RequestContext _context) {
-			return ActiveMethodPermissionLevel (_context.Method) >= _context.PermissionLevel;
+			AdminWebModules.WebModule module = AdminWebModules.Instance.GetModule (CachedApiModuleName);
+
+			if (module.LevelPerMethod != null) {
+				int perMethodLevel = module.LevelPerMethod [(int)_context.Method];
+				if (perMethodLevel == AdminWebModules.MethodLevelNotSupported) {
+					return false;
+				}
+
+				if (perMethodLevel != AdminWebModules.MethodLevelInheritGlobal) {
+					return perMethodLevel >= _context.PermissionLevel;
+				}
+			}
+
+			return module.LevelGlobal >= _context.PermissionLevel;
 		}
 
 		/// <summary>
-		/// Define default permission levels per HTTP method
+		/// Define default permission levels per HTTP method as an array of levels. See <see cref="ERequestMethod"/> for the order of entries.
 		/// </summary>
-		/// <param name="_method">HTTP method to return the default value for</param>
-		/// <returns>Default permission level for the given HTTP method. A value of int.MinValue means no per-method default, use per-API default</returns>
-		public virtual int DefaultMethodPermissionLevel (ERequestMethod _method) => int.MinValue;
-
-		public virtual int ActiveMethodPermissionLevel (ERequestMethod _method) {
-			string methodApiModuleName = CachedPerMethodModuleNames [(int)_method];
-
-			if (methodApiModuleName == null) {
-				return 0;
-			}
-
-			AdminWebModules.WebModule? overrideModule = AdminWebModules.Instance.GetModule (methodApiModuleName, false);
-			if (overrideModule.HasValue) {
-				return overrideModule.Value.PermissionLevel;
-			}
-
-			overrideModule = AdminWebModules.Instance.GetModule (CachedApiModuleName, false);
-			if (overrideModule.HasValue) {
-				return overrideModule.Value.PermissionLevel;
-			}
-
-			int defaultMethodPermissionLevel = DefaultMethodPermissionLevel (_method);
-			// ReSharper disable once ConvertIfStatementToReturnStatement
-			if (defaultMethodPermissionLevel != int.MinValue) {
-				return defaultMethodPermissionLevel;
-			}
-
-			return DefaultPermissionLevel ();
-		}
+		/// <returns>Default permission levels for supported methods. See <see cref="AdminWebModules.MethodLevelNotSupported"/> and <see cref="AdminWebModules.MethodLevelInheritGlobal"/>.</returns>
+		public virtual int[] DefaultMethodPermissionLevels () => new[] {
+			AdminWebModules.MethodLevelNotSupported,
+			AdminWebModules.MethodLevelInheritGlobal,
+			AdminWebModules.MethodLevelInheritGlobal,
+			AdminWebModules.MethodLevelInheritGlobal,
+			AdminWebModules.MethodLevelInheritGlobal
+		};
 
 #region Helpers
Index: binary-improvements2/WebServer/src/WebAPI/AbsWebAPI.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/AbsWebAPI.cs	(revision 425)
+++ binary-improvements2/WebServer/src/WebAPI/AbsWebAPI.cs	(revision 426)
@@ -19,5 +19,5 @@
 
 		protected virtual void RegisterPermissions () {
-			AdminWebModules.Instance.AddKnownModule ($"webapi.{Name}", DefaultPermissionLevel ());
+			AdminWebModules.Instance.AddKnownModule (new AdminWebModules.WebModule(CachedApiModuleName, DefaultPermissionLevel (), true));
 		}
 
@@ -25,5 +25,5 @@
 
 		public virtual bool Authorized (RequestContext _context) {
-			return AdminWebModules.Instance.ModuleAllowedWithLevel (CachedApiModuleName, _context.PermissionLevel);
+			return AdminWebModules.Instance.GetModule (CachedApiModuleName).LevelGlobal >= _context.PermissionLevel;
 		}
 
Index: binary-improvements2/WebServer/src/WebAPI/JsonCommons.cs
===================================================================
--- binary-improvements2/WebServer/src/WebAPI/JsonCommons.cs	(revision 425)
+++ binary-improvements2/WebServer/src/WebAPI/JsonCommons.cs	(revision 426)
@@ -60,4 +60,12 @@
 			_writer.WriteString (_dateTime.ToString ("o"));
 		}
+
+		public static void WriteStringOrNull (ref JsonWriter _writer, string _string) {
+			if (_string == null) {
+				_writer.WriteNull ();
+			} else {
+				_writer.WriteString (_string);
+			}
+		}
 	}
 }
