diff --git a/Common/Phantom.Common.Data.Web/Users/AuthenticatedUserInfo.cs b/Common/Phantom.Common.Data.Web/Users/AuthenticatedUserInfo.cs
index 7db483b..9becdf6 100644
--- a/Common/Phantom.Common.Data.Web/Users/AuthenticatedUserInfo.cs
+++ b/Common/Phantom.Common.Data.Web/Users/AuthenticatedUserInfo.cs
@@ -1,4 +1,5 @@
-using MemoryPack;
+using System.Collections.Immutable;
+using MemoryPack;
 
 namespace Phantom.Common.Data.Web.Users;
 
@@ -6,5 +7,18 @@ namespace Phantom.Common.Data.Web.Users;
 public sealed partial record AuthenticatedUserInfo(
 	[property: MemoryPackOrder(0)] Guid Guid,
 	[property: MemoryPackOrder(1)] string Name,
-	[property: MemoryPackOrder(2)] PermissionSet Permissions
-);
+	[property: MemoryPackOrder(2)] PermissionSet Permissions,
+	[property: MemoryPackOrder(3)] ImmutableHashSet<Guid> ManagedAgentGuids
+) {
+	public bool CheckPermission(Permission permission) {
+		return Permissions.Check(permission);
+	}
+
+	public bool HasAccessToAgent(Guid agentGuid) {
+		return ManagedAgentGuids.Contains(agentGuid) || Permissions.Check(Permission.ManageAllAgents);
+	}
+
+	public ImmutableHashSet<Guid> FilterAccessibleAgentGuids(ImmutableHashSet<Guid> agentGuids) {
+		return Permissions.Check(Permission.ManageAllAgents) ? agentGuids : agentGuids.Intersect(ManagedAgentGuids);
+	}
+}
diff --git a/Common/Phantom.Common.Data.Web/Users/Permission.cs b/Common/Phantom.Common.Data.Web/Users/Permission.cs
index 6220c89..5a96efe 100644
--- a/Common/Phantom.Common.Data.Web/Users/Permission.cs
+++ b/Common/Phantom.Common.Data.Web/Users/Permission.cs
@@ -14,6 +14,9 @@ public sealed record Permission(string Id, Permission? Parent) {
 		return Register(id, this);
 	}
 
+	public const string ManageAllAgentsPolicy = "Agents.ManageAll";
+	public static readonly Permission ManageAllAgents = Register(ManageAllAgentsPolicy);
+	
 	public const string ViewInstancesPolicy = "Instances.View";
 	public static readonly Permission ViewInstances = Register(ViewInstancesPolicy);
 	
diff --git a/Controller/Phantom.Controller.Database.Postgres/Migrations/20240407211636_UserAgentAccess.Designer.cs b/Controller/Phantom.Controller.Database.Postgres/Migrations/20240407211636_UserAgentAccess.Designer.cs
new file mode 100644
index 0000000..4907623
--- /dev/null
+++ b/Controller/Phantom.Controller.Database.Postgres/Migrations/20240407211636_UserAgentAccess.Designer.cs
@@ -0,0 +1,353 @@
+// <auto-generated />
+using System;
+using System.Text.Json;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Phantom.Controller.Database;
+
+#nullable disable
+
+namespace Phantom.Controller.Database.Postgres.Migrations
+{
+    [DbContext(typeof(ApplicationDbContext))]
+    [Migration("20240407211636_UserAgentAccess")]
+    partial class UserAgentAccess
+    {
+        /// <inheritdoc />
+        protected override void BuildTargetModel(ModelBuilder modelBuilder)
+        {
+#pragma warning disable 612, 618
+            modelBuilder
+                .HasAnnotation("ProductVersion", "8.0.0")
+                .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+            NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.AgentEntity", b =>
+                {
+                    b.Property<Guid>("AgentGuid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uuid");
+
+                    b.Property<string>("BuildVersion")
+                        .IsRequired()
+                        .HasColumnType("text");
+
+                    b.Property<int>("MaxInstances")
+                        .HasColumnType("integer");
+
+                    b.Property<ushort>("MaxMemory")
+                        .HasColumnType("integer");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("text");
+
+                    b.Property<int>("ProtocolVersion")
+                        .HasColumnType("integer");
+
+                    b.HasKey("AgentGuid");
+
+                    b.ToTable("Agents", "agents");
+                });
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditLogEntity", b =>
+                {
+                    b.Property<long>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("bigint");
+
+                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
+
+                    b.Property<JsonDocument>("Data")
+                        .HasColumnType("jsonb");
+
+                    b.Property<string>("EventType")
+                        .IsRequired()
+                        .HasColumnType("text");
+
+                    b.Property<string>("SubjectId")
+                        .IsRequired()
+                        .HasColumnType("text");
+
+                    b.Property<string>("SubjectType")
+                        .IsRequired()
+                        .HasColumnType("text");
+
+                    b.Property<Guid?>("UserGuid")
+                        .HasColumnType("uuid");
+
+                    b.Property<DateTime>("UtcTime")
+                        .HasColumnType("timestamp with time zone");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("UserGuid");
+
+                    b.ToTable("AuditLog", "system");
+                });
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.EventLogEntity", b =>
+                {
+                    b.Property<Guid>("EventGuid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uuid");
+
+                    b.Property<Guid?>("AgentGuid")
+                        .HasColumnType("uuid");
+
+                    b.Property<JsonDocument>("Data")
+                        .HasColumnType("jsonb");
+
+                    b.Property<string>("EventType")
+                        .IsRequired()
+                        .HasColumnType("text");
+
+                    b.Property<string>("SubjectId")
+                        .IsRequired()
+                        .HasColumnType("text");
+
+                    b.Property<string>("SubjectType")
+                        .IsRequired()
+                        .HasColumnType("text");
+
+                    b.Property<DateTime>("UtcTime")
+                        .HasColumnType("timestamp with time zone");
+
+                    b.HasKey("EventGuid");
+
+                    b.ToTable("EventLog", "system");
+                });
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.InstanceEntity", b =>
+                {
+                    b.Property<Guid>("InstanceGuid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uuid");
+
+                    b.Property<Guid>("AgentGuid")
+                        .HasColumnType("uuid");
+
+                    b.Property<string>("InstanceName")
+                        .IsRequired()
+                        .HasColumnType("text");
+
+                    b.Property<Guid>("JavaRuntimeGuid")
+                        .HasColumnType("uuid");
+
+                    b.Property<string>("JvmArguments")
+                        .IsRequired()
+                        .HasColumnType("text");
+
+                    b.Property<bool>("LaunchAutomatically")
+                        .HasColumnType("boolean");
+
+                    b.Property<ushort>("MemoryAllocation")
+                        .HasColumnType("integer");
+
+                    b.Property<string>("MinecraftServerKind")
+                        .IsRequired()
+                        .HasColumnType("text");
+
+                    b.Property<string>("MinecraftVersion")
+                        .IsRequired()
+                        .HasColumnType("text");
+
+                    b.Property<int>("RconPort")
+                        .HasColumnType("integer");
+
+                    b.Property<int>("ServerPort")
+                        .HasColumnType("integer");
+
+                    b.HasKey("InstanceGuid");
+
+                    b.ToTable("Instances", "agents");
+                });
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.PermissionEntity", b =>
+                {
+                    b.Property<string>("Id")
+                        .HasColumnType("text");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Permissions", "identity");
+                });
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.RoleEntity", b =>
+                {
+                    b.Property<Guid>("RoleGuid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uuid");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("text");
+
+                    b.HasKey("RoleGuid");
+
+                    b.ToTable("Roles", "identity");
+                });
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.RolePermissionEntity", b =>
+                {
+                    b.Property<Guid>("RoleGuid")
+                        .HasColumnType("uuid");
+
+                    b.Property<string>("PermissionId")
+                        .HasColumnType("text");
+
+                    b.HasKey("RoleGuid", "PermissionId");
+
+                    b.HasIndex("PermissionId");
+
+                    b.ToTable("RolePermissions", "identity");
+                });
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.UserAgentAccessEntity", b =>
+                {
+                    b.Property<Guid>("UserGuid")
+                        .HasColumnType("uuid");
+
+                    b.Property<Guid>("AgentGuid")
+                        .HasColumnType("uuid");
+
+                    b.HasKey("UserGuid", "AgentGuid");
+
+                    b.HasIndex("AgentGuid");
+
+                    b.ToTable("UserAgentAccess", "identity");
+                });
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.UserEntity", b =>
+                {
+                    b.Property<Guid>("UserGuid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uuid");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("text");
+
+                    b.Property<string>("PasswordHash")
+                        .IsRequired()
+                        .HasColumnType("text");
+
+                    b.HasKey("UserGuid");
+
+                    b.HasIndex("Name")
+                        .IsUnique();
+
+                    b.ToTable("Users", "identity");
+                });
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
+                {
+                    b.Property<Guid>("UserGuid")
+                        .HasColumnType("uuid");
+
+                    b.Property<string>("PermissionId")
+                        .HasColumnType("text");
+
+                    b.HasKey("UserGuid", "PermissionId");
+
+                    b.HasIndex("PermissionId");
+
+                    b.ToTable("UserPermissions", "identity");
+                });
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.UserRoleEntity", b =>
+                {
+                    b.Property<Guid>("UserGuid")
+                        .HasColumnType("uuid");
+
+                    b.Property<Guid>("RoleGuid")
+                        .HasColumnType("uuid");
+
+                    b.HasKey("UserGuid", "RoleGuid");
+
+                    b.HasIndex("RoleGuid");
+
+                    b.ToTable("UserRoles", "identity");
+                });
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditLogEntity", b =>
+                {
+                    b.HasOne("Phantom.Controller.Database.Entities.UserEntity", "User")
+                        .WithMany()
+                        .HasForeignKey("UserGuid")
+                        .OnDelete(DeleteBehavior.SetNull);
+
+                    b.Navigation("User");
+                });
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.RolePermissionEntity", b =>
+                {
+                    b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
+                        .WithMany()
+                        .HasForeignKey("PermissionId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Phantom.Controller.Database.Entities.RoleEntity", null)
+                        .WithMany()
+                        .HasForeignKey("RoleGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.UserAgentAccessEntity", b =>
+                {
+                    b.HasOne("Phantom.Controller.Database.Entities.AgentEntity", null)
+                        .WithMany()
+                        .HasForeignKey("AgentGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Phantom.Controller.Database.Entities.UserEntity", null)
+                        .WithMany()
+                        .HasForeignKey("UserGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
+                {
+                    b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
+                        .WithMany()
+                        .HasForeignKey("PermissionId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Phantom.Controller.Database.Entities.UserEntity", null)
+                        .WithMany()
+                        .HasForeignKey("UserGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.UserRoleEntity", b =>
+                {
+                    b.HasOne("Phantom.Controller.Database.Entities.RoleEntity", "Role")
+                        .WithMany()
+                        .HasForeignKey("RoleGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Phantom.Controller.Database.Entities.UserEntity", "User")
+                        .WithMany()
+                        .HasForeignKey("UserGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Role");
+
+                    b.Navigation("User");
+                });
+#pragma warning restore 612, 618
+        }
+    }
+}
diff --git a/Controller/Phantom.Controller.Database.Postgres/Migrations/20240407211636_UserAgentAccess.cs b/Controller/Phantom.Controller.Database.Postgres/Migrations/20240407211636_UserAgentAccess.cs
new file mode 100644
index 0000000..21f4160
--- /dev/null
+++ b/Controller/Phantom.Controller.Database.Postgres/Migrations/20240407211636_UserAgentAccess.cs
@@ -0,0 +1,56 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Phantom.Controller.Database.Postgres.Migrations
+{
+    /// <inheritdoc />
+    public partial class UserAgentAccess : Migration
+    {
+        /// <inheritdoc />
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.CreateTable(
+                name: "UserAgentAccess",
+                schema: "identity",
+                columns: table => new
+                {
+                    UserGuid = table.Column<Guid>(type: "uuid", nullable: false),
+                    AgentGuid = table.Column<Guid>(type: "uuid", nullable: false)
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_UserAgentAccess", x => new { x.UserGuid, x.AgentGuid });
+                    table.ForeignKey(
+                        name: "FK_UserAgentAccess_Agents_AgentGuid",
+                        column: x => x.AgentGuid,
+                        principalSchema: "agents",
+                        principalTable: "Agents",
+                        principalColumn: "AgentGuid",
+                        onDelete: ReferentialAction.Cascade);
+                    table.ForeignKey(
+                        name: "FK_UserAgentAccess_Users_UserGuid",
+                        column: x => x.UserGuid,
+                        principalSchema: "identity",
+                        principalTable: "Users",
+                        principalColumn: "UserGuid",
+                        onDelete: ReferentialAction.Cascade);
+                });
+
+            migrationBuilder.CreateIndex(
+                name: "IX_UserAgentAccess_AgentGuid",
+                schema: "identity",
+                table: "UserAgentAccess",
+                column: "AgentGuid");
+        }
+
+        /// <inheritdoc />
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropTable(
+                name: "UserAgentAccess",
+                schema: "identity");
+        }
+    }
+}
diff --git a/Controller/Phantom.Controller.Database.Postgres/Migrations/ApplicationDbContextModelSnapshot.cs b/Controller/Phantom.Controller.Database.Postgres/Migrations/ApplicationDbContextModelSnapshot.cs
index 63bef7e..549d983 100644
--- a/Controller/Phantom.Controller.Database.Postgres/Migrations/ApplicationDbContextModelSnapshot.cs
+++ b/Controller/Phantom.Controller.Database.Postgres/Migrations/ApplicationDbContextModelSnapshot.cs
@@ -18,7 +18,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
         {
 #pragma warning disable 612, 618
             modelBuilder
-                .HasAnnotation("ProductVersion", "7.0.11")
+                .HasAnnotation("ProductVersion", "8.0.0")
                 .HasAnnotation("Relational:MaxIdentifierLength", 63);
 
             NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@@ -204,6 +204,21 @@ namespace Phantom.Controller.Database.Postgres.Migrations
                     b.ToTable("RolePermissions", "identity");
                 });
 
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.UserAgentAccessEntity", b =>
+                {
+                    b.Property<Guid>("UserGuid")
+                        .HasColumnType("uuid");
+
+                    b.Property<Guid>("AgentGuid")
+                        .HasColumnType("uuid");
+
+                    b.HasKey("UserGuid", "AgentGuid");
+
+                    b.HasIndex("AgentGuid");
+
+                    b.ToTable("UserAgentAccess", "identity");
+                });
+
             modelBuilder.Entity("Phantom.Controller.Database.Entities.UserEntity", b =>
                 {
                     b.Property<Guid>("UserGuid")
@@ -281,6 +296,21 @@ namespace Phantom.Controller.Database.Postgres.Migrations
                         .IsRequired();
                 });
 
+            modelBuilder.Entity("Phantom.Controller.Database.Entities.UserAgentAccessEntity", b =>
+                {
+                    b.HasOne("Phantom.Controller.Database.Entities.AgentEntity", null)
+                        .WithMany()
+                        .HasForeignKey("AgentGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("Phantom.Controller.Database.Entities.UserEntity", null)
+                        .WithMany()
+                        .HasForeignKey("UserGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
             modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
                 {
                     b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
diff --git a/Controller/Phantom.Controller.Database/ApplicationDbContext.cs b/Controller/Phantom.Controller.Database/ApplicationDbContext.cs
index f3c313a..9698200 100644
--- a/Controller/Phantom.Controller.Database/ApplicationDbContext.cs
+++ b/Controller/Phantom.Controller.Database/ApplicationDbContext.cs
@@ -20,7 +20,8 @@ public class ApplicationDbContext : DbContext {
 	public DbSet<UserRoleEntity> UserRoles { get; init; } = null!;
 	public DbSet<UserPermissionEntity> UserPermissions { get; init; } = null!;
 	public DbSet<RolePermissionEntity> RolePermissions { get; init; } = null!;
-	
+	public DbSet<UserAgentAccessEntity> UserAgentAccess { get; init; } = null!;
+
 	public DbSet<AgentEntity> Agents { get; init; } = null!;
 	public DbSet<InstanceEntity> Instances { get; init; } = null!;
 	public DbSet<AuditLogEntity> AuditLog { get; init; } = null!;
@@ -62,6 +63,12 @@ public class ApplicationDbContext : DbContext {
 			b.HasOne<RoleEntity>().WithMany().HasForeignKey(static e => e.RoleGuid).IsRequired().OnDelete(DeleteBehavior.Cascade);
 			b.HasOne<PermissionEntity>().WithMany().HasForeignKey(static e => e.PermissionId).IsRequired().OnDelete(DeleteBehavior.Cascade);
 		});
+		
+		builder.Entity<UserAgentAccessEntity>(static b => {
+			b.HasKey(static e => new { UserId = e.UserGuid, AgentId = e.AgentGuid });
+			b.HasOne<UserEntity>().WithMany().HasForeignKey(static e => e.UserGuid).IsRequired().OnDelete(DeleteBehavior.Cascade);
+			b.HasOne<AgentEntity>().WithMany().HasForeignKey(static e => e.AgentGuid).IsRequired().OnDelete(DeleteBehavior.Cascade);
+		});
 	}
 
 	protected override void ConfigureConventions(ModelConfigurationBuilder builder) {
diff --git a/Controller/Phantom.Controller.Database/Entities/UserAgentAccessEntity.cs b/Controller/Phantom.Controller.Database/Entities/UserAgentAccessEntity.cs
new file mode 100644
index 0000000..297efd2
--- /dev/null
+++ b/Controller/Phantom.Controller.Database/Entities/UserAgentAccessEntity.cs
@@ -0,0 +1,14 @@
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Phantom.Controller.Database.Entities;
+
+[Table("UserAgentAccess", Schema = "identity")]
+public sealed class UserAgentAccessEntity {
+	public Guid UserGuid { get; init; }
+	public Guid AgentGuid { get; init; }
+	
+	public UserAgentAccessEntity(Guid userGuid, Guid agentGuid) {
+		UserGuid = userGuid;
+		AgentGuid = agentGuid;
+	}
+}
diff --git a/Controller/Phantom.Controller.Database/Repositories/EventLogRepository.cs b/Controller/Phantom.Controller.Database/Repositories/EventLogRepository.cs
index b861511..7bd0280 100644
--- a/Controller/Phantom.Controller.Database/Repositories/EventLogRepository.cs
+++ b/Controller/Phantom.Controller.Database/Repositories/EventLogRepository.cs
@@ -17,11 +17,12 @@ public sealed class EventLogRepository {
 		db.Ctx.EventLog.Add(new EventLogEntity(eventGuid, utcTime, agentGuid, eventType, subjectId, extra));
 	}
 	
-	public Task<ImmutableArray<EventLogItem>> GetMostRecentItems(int count, CancellationToken cancellationToken) {
+	public Task<ImmutableArray<EventLogItem>> GetMostRecentItems(ImmutableHashSet<Guid> agentGuids, int count, CancellationToken cancellationToken) {
 		return db.Ctx
 		         .EventLog
 		         .AsQueryable()
 		         .OrderByDescending(static entity => entity.UtcTime)
+		         .Where(entity => entity.AgentGuid == null || agentGuids.Contains(entity.AgentGuid.Value))
 		         .Take(count)
 		         .AsAsyncEnumerable()
 		         .Select(static entity => new EventLogItem(entity.UtcTime, entity.AgentGuid, entity.EventType, entity.SubjectType, entity.SubjectId, entity.Data?.RootElement.ToString()))
diff --git a/Controller/Phantom.Controller.Database/Repositories/PermissionRepository.cs b/Controller/Phantom.Controller.Database/Repositories/PermissionRepository.cs
index 463f755..ffd8eff 100644
--- a/Controller/Phantom.Controller.Database/Repositories/PermissionRepository.cs
+++ b/Controller/Phantom.Controller.Database/Repositories/PermissionRepository.cs
@@ -1,4 +1,5 @@
-using Microsoft.EntityFrameworkCore;
+using System.Collections.Immutable;
+using Microsoft.EntityFrameworkCore;
 using Phantom.Common.Data.Web.Users;
 using Phantom.Controller.Database.Entities;
 using Phantom.Utils.Collections;
@@ -23,4 +24,12 @@ public sealed class PermissionRepository {
 
 		return new PermissionSet(await userPermissions.Union(rolePermissions).AsAsyncEnumerable().ToImmutableSetAsync());
 	}
+
+	public Task<ImmutableHashSet<Guid>> GetManagedAgentGuids(UserEntity user) {
+		return db.Ctx.UserAgentAccess
+		         .Where(ua => ua.UserGuid == user.UserGuid)
+		         .Select(static ua => ua.AgentGuid)
+		         .AsAsyncEnumerable()
+		         .ToImmutableSetAsync();
+	}
 }
diff --git a/Controller/Phantom.Controller.Services/Agents/AgentActor.cs b/Controller/Phantom.Controller.Services/Agents/AgentActor.cs
index 6dd6760..46c17fa 100644
--- a/Controller/Phantom.Controller.Services/Agents/AgentActor.cs
+++ b/Controller/Phantom.Controller.Services/Agents/AgentActor.cs
@@ -10,14 +10,12 @@ using Phantom.Common.Data.Replies;
 using Phantom.Common.Data.Web.Agent;
 using Phantom.Common.Data.Web.Instance;
 using Phantom.Common.Data.Web.Minecraft;
-using Phantom.Common.Data.Web.Users;
 using Phantom.Common.Messages.Agent;
 using Phantom.Common.Messages.Agent.ToAgent;
 using Phantom.Controller.Database;
 using Phantom.Controller.Database.Entities;
 using Phantom.Controller.Minecraft;
 using Phantom.Controller.Services.Instances;
-using Phantom.Controller.Services.Users.Sessions;
 using Phantom.Utils.Actor;
 using Phantom.Utils.Actor.Mailbox;
 using Phantom.Utils.Actor.Tasks;
@@ -34,7 +32,7 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
 	private static readonly TimeSpan DisconnectionRecheckInterval = TimeSpan.FromSeconds(5);
 	private static readonly TimeSpan DisconnectionThreshold = TimeSpan.FromSeconds(12);
 
-	public readonly record struct Init(Guid AgentGuid, AgentConfiguration AgentConfiguration, ControllerState ControllerState, MinecraftVersions MinecraftVersions, UserLoginManager UserLoginManager, IDbContextProvider DbProvider, CancellationToken CancellationToken);
+	public readonly record struct Init(Guid AgentGuid, AgentConfiguration AgentConfiguration, ControllerState ControllerState, MinecraftVersions MinecraftVersions, IDbContextProvider DbProvider, CancellationToken CancellationToken);
 	
 	public static Props<ICommand> Factory(Init init) {
 		return Props<ICommand>.Create(() => new AgentActor(init), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume, MailboxType = UnboundedJumpAheadMailbox.Name });
@@ -42,7 +40,6 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
 
 	private readonly ControllerState controllerState;
 	private readonly MinecraftVersions minecraftVersions;
-	private readonly UserLoginManager userLoginManager;
 	private readonly IDbContextProvider dbProvider;
 	private readonly CancellationToken cancellationToken;
 	
@@ -79,7 +76,6 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
 	private AgentActor(Init init) {
 		this.controllerState = init.ControllerState;
 		this.minecraftVersions = init.MinecraftVersions;
-		this.userLoginManager = init.UserLoginManager;
 		this.dbProvider = init.DbProvider;
 		this.cancellationToken = init.CancellationToken;
 		
@@ -98,11 +94,11 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
 		Receive<NotifyIsAliveCommand>(NotifyIsAlive);
 		Receive<UpdateStatsCommand>(UpdateStats);
 		Receive<UpdateJavaRuntimesCommand>(UpdateJavaRuntimes);
-		ReceiveAndReplyLater<CreateOrUpdateInstanceCommand, Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>>(CreateOrUpdateInstance);
+		ReceiveAndReplyLater<CreateOrUpdateInstanceCommand, Result<CreateOrUpdateInstanceResult, InstanceActionFailure>>(CreateOrUpdateInstance);
 		Receive<UpdateInstanceStatusCommand>(UpdateInstanceStatus);
-		ReceiveAndReplyLater<LaunchInstanceCommand, Result<LaunchInstanceResult, UserInstanceActionFailure>>(LaunchInstance);
-		ReceiveAndReplyLater<StopInstanceCommand, Result<StopInstanceResult, UserInstanceActionFailure>>(StopInstance);
-		ReceiveAndReplyLater<SendCommandToInstanceCommand, Result<SendCommandToInstanceResult, UserInstanceActionFailure>>(SendMinecraftCommand);
+		ReceiveAndReplyLater<LaunchInstanceCommand, Result<LaunchInstanceResult, InstanceActionFailure>>(LaunchInstance);
+		ReceiveAndReplyLater<StopInstanceCommand, Result<StopInstanceResult, InstanceActionFailure>>(StopInstance);
+		ReceiveAndReplyLater<SendCommandToInstanceCommand, Result<SendCommandToInstanceResult, InstanceActionFailure>>(SendMinecraftCommand);
 		Receive<ReceiveInstanceDataCommand>(ReceiveInstanceData);
 	}
 
@@ -150,21 +146,13 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
 		}
 	}
 
-	private async Task<Result<TReply, UserInstanceActionFailure>> RequestInstance<TCommand, TReply>(ImmutableArray<byte> authToken, Guid instanceGuid, Func<Guid, TCommand> commandFactoryFromLoggedInUserGuid) where TCommand : InstanceActor.ICommand, ICanReply<Result<TReply, InstanceActionFailure>> {
-		var loggedInUser = userLoginManager.GetLoggedInUser(authToken);
-		if (!loggedInUser.CheckPermission(Permission.ControlInstances)) {
-			return (UserInstanceActionFailure) UserActionFailure.NotAuthorized;
-		}
-		
-		var command = commandFactoryFromLoggedInUserGuid(loggedInUser.Guid!.Value);
-		
+	private async Task<Result<TReply, InstanceActionFailure>> RequestInstance<TCommand, TReply>(Guid instanceGuid, TCommand command) where TCommand : InstanceActor.ICommand, ICanReply<Result<TReply, InstanceActionFailure>> {
 		if (instanceActorByGuid.TryGetValue(instanceGuid, out var instance)) {
-			var result = await instance.Request(command, cancellationToken);
-			return result.MapError(static error => (UserInstanceActionFailure) error);
+			return await instance.Request(command, cancellationToken);
 		}
 		else {
 			Logger.Warning("Could not deliver command {CommandType} to instance {InstanceGuid}, instance not found.", command.GetType().Name, instanceGuid);
-			return (UserInstanceActionFailure) InstanceActionFailure.InstanceDoesNotExist;
+			return InstanceActionFailure.InstanceDoesNotExist;
 		}
 	}
 
@@ -195,15 +183,15 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
 	
 	public sealed record UpdateJavaRuntimesCommand(ImmutableArray<TaggedJavaRuntime> JavaRuntimes) : ICommand;
 	
-	public sealed record CreateOrUpdateInstanceCommand(ImmutableArray<byte> AuthToken, Guid InstanceGuid, InstanceConfiguration Configuration) : ICommand, ICanReply<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>>;
+	public sealed record CreateOrUpdateInstanceCommand(Guid LoggedInUserGuid, Guid InstanceGuid, InstanceConfiguration Configuration) : ICommand, ICanReply<Result<CreateOrUpdateInstanceResult, InstanceActionFailure>>;
 	
 	public sealed record UpdateInstanceStatusCommand(Guid InstanceGuid, IInstanceStatus Status) : ICommand;
 
-	public sealed record LaunchInstanceCommand(ImmutableArray<byte> AuthToken, Guid InstanceGuid) : ICommand, ICanReply<Result<LaunchInstanceResult, UserInstanceActionFailure>>;
+	public sealed record LaunchInstanceCommand(Guid LoggedInUserGuid, Guid InstanceGuid) : ICommand, ICanReply<Result<LaunchInstanceResult, InstanceActionFailure>>;
 	
-	public sealed record StopInstanceCommand(ImmutableArray<byte> AuthToken, Guid InstanceGuid, MinecraftStopStrategy StopStrategy) : ICommand, ICanReply<Result<StopInstanceResult, UserInstanceActionFailure>>;
+	public sealed record StopInstanceCommand(Guid LoggedInUserGuid, Guid InstanceGuid, MinecraftStopStrategy StopStrategy) : ICommand, ICanReply<Result<StopInstanceResult, InstanceActionFailure>>;
 	
-	public sealed record SendCommandToInstanceCommand(ImmutableArray<byte> AuthToken, Guid InstanceGuid, string Command) : ICommand, ICanReply<Result<SendCommandToInstanceResult, UserInstanceActionFailure>>;
+	public sealed record SendCommandToInstanceCommand(Guid LoggedInUserGuid, Guid InstanceGuid, string Command) : ICommand, ICanReply<Result<SendCommandToInstanceResult, InstanceActionFailure>>;
 	
 	public sealed record ReceiveInstanceDataCommand(Instance Instance) : ICommand, IJumpAhead;
 
@@ -292,30 +280,25 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
 		controllerState.UpdateAgentJavaRuntimes(agentGuid, javaRuntimes);
 	}
 	
-	private Task<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>> CreateOrUpdateInstance(CreateOrUpdateInstanceCommand command) {
-		var loggedInUser = userLoginManager.GetLoggedInUser(command.AuthToken);
-		if (!loggedInUser.CheckPermission(Permission.CreateInstances)) {
-			return Task.FromResult<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>>((UserInstanceActionFailure) UserActionFailure.NotAuthorized);
-		}
-		
+	private Task<Result<CreateOrUpdateInstanceResult, InstanceActionFailure>> CreateOrUpdateInstance(CreateOrUpdateInstanceCommand command) {
 		var instanceConfiguration = command.Configuration;
 
 		if (string.IsNullOrWhiteSpace(instanceConfiguration.InstanceName)) {
-			return Task.FromResult<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>>(CreateOrUpdateInstanceResult.InstanceNameMustNotBeEmpty);
+			return Task.FromResult<Result<CreateOrUpdateInstanceResult, InstanceActionFailure>>(CreateOrUpdateInstanceResult.InstanceNameMustNotBeEmpty);
 		}
 		
 		if (instanceConfiguration.MemoryAllocation <= RamAllocationUnits.Zero) {
-			return Task.FromResult<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>>(CreateOrUpdateInstanceResult.InstanceMemoryMustNotBeZero);
+			return Task.FromResult<Result<CreateOrUpdateInstanceResult, InstanceActionFailure>>(CreateOrUpdateInstanceResult.InstanceMemoryMustNotBeZero);
 		}
 		
 		return minecraftVersions.GetServerExecutableInfo(instanceConfiguration.MinecraftVersion, cancellationToken)
-		                        .ContinueOnActor(CreateOrUpdateInstance1, loggedInUser.Guid!.Value, command)
+		                        .ContinueOnActor(CreateOrUpdateInstance1, command)
 		                        .Unwrap();
 	}
 
-	private Task<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>> CreateOrUpdateInstance1(FileDownloadInfo? serverExecutableInfo, Guid loggedInUserGuid, CreateOrUpdateInstanceCommand command) {
+	private Task<Result<CreateOrUpdateInstanceResult, InstanceActionFailure>> CreateOrUpdateInstance1(FileDownloadInfo? serverExecutableInfo, CreateOrUpdateInstanceCommand command) {
 		if (serverExecutableInfo == null) {
-			return Task.FromResult<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>>(CreateOrUpdateInstanceResult.MinecraftVersionDownloadInfoNotFound);
+			return Task.FromResult<Result<CreateOrUpdateInstanceResult, InstanceActionFailure>>(CreateOrUpdateInstanceResult.MinecraftVersionDownloadInfoNotFound);
 		}
 		
 		var instanceConfiguration = command.Configuration;
@@ -325,13 +308,13 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
 			instanceActorRef = CreateNewInstance(Instance.Offline(command.InstanceGuid, instanceConfiguration));
 		}
 		
-		var configureInstanceCommand = new InstanceActor.ConfigureInstanceCommand(loggedInUserGuid, command.InstanceGuid, instanceConfiguration, new InstanceLaunchProperties(serverExecutableInfo), isCreatingInstance);
+		var configureInstanceCommand = new InstanceActor.ConfigureInstanceCommand(command.LoggedInUserGuid, command.InstanceGuid, instanceConfiguration, new InstanceLaunchProperties(serverExecutableInfo), isCreatingInstance);
 		
 		return instanceActorRef.Request(configureInstanceCommand, cancellationToken)
 		                       .ContinueOnActor(CreateOrUpdateInstance2, configureInstanceCommand);
 	}
 	
-	private Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure> CreateOrUpdateInstance2(Result<ConfigureInstanceResult, InstanceActionFailure> result, InstanceActor.ConfigureInstanceCommand command) {
+	private Result<CreateOrUpdateInstanceResult, InstanceActionFailure> CreateOrUpdateInstance2(Result<ConfigureInstanceResult, InstanceActionFailure> result, InstanceActor.ConfigureInstanceCommand command) {
 		var instanceGuid = command.InstanceGuid;
 		var instanceName = command.Configuration.InstanceName;
 		var isCreating = command.IsCreatingInstance;
@@ -359,16 +342,16 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
 		TellInstance(command.InstanceGuid, new InstanceActor.SetStatusCommand(command.Status));
 	}
 
-	private Task<Result<LaunchInstanceResult, UserInstanceActionFailure>> LaunchInstance(LaunchInstanceCommand command) {
-		return RequestInstance<InstanceActor.LaunchInstanceCommand, LaunchInstanceResult>(command.AuthToken, command.InstanceGuid, static loggedInUserGuid => new InstanceActor.LaunchInstanceCommand(loggedInUserGuid));
+	private Task<Result<LaunchInstanceResult, InstanceActionFailure>> LaunchInstance(LaunchInstanceCommand command) {
+		return RequestInstance<InstanceActor.LaunchInstanceCommand, LaunchInstanceResult>(command.InstanceGuid, new InstanceActor.LaunchInstanceCommand(command.LoggedInUserGuid));
 	}
 
-	private Task<Result<StopInstanceResult, UserInstanceActionFailure>> StopInstance(StopInstanceCommand command) {
-		return RequestInstance<InstanceActor.StopInstanceCommand, StopInstanceResult>(command.AuthToken, command.InstanceGuid, loggedInUserGuid => new InstanceActor.StopInstanceCommand(loggedInUserGuid, command.StopStrategy));
+	private Task<Result<StopInstanceResult, InstanceActionFailure>> StopInstance(StopInstanceCommand command) {
+		return RequestInstance<InstanceActor.StopInstanceCommand, StopInstanceResult>(command.InstanceGuid, new InstanceActor.StopInstanceCommand(command.LoggedInUserGuid, command.StopStrategy));
 	}
 
-	private Task<Result<SendCommandToInstanceResult, UserInstanceActionFailure>> SendMinecraftCommand(SendCommandToInstanceCommand command) {
-		return RequestInstance<InstanceActor.SendCommandToInstanceCommand, SendCommandToInstanceResult>(command.AuthToken, command.InstanceGuid, loggedInUserGuid => new InstanceActor.SendCommandToInstanceCommand(loggedInUserGuid, command.Command));
+	private Task<Result<SendCommandToInstanceResult, InstanceActionFailure>> SendMinecraftCommand(SendCommandToInstanceCommand command) {
+		return RequestInstance<InstanceActor.SendCommandToInstanceCommand, SendCommandToInstanceResult>(command.InstanceGuid, new InstanceActor.SendCommandToInstanceCommand(command.LoggedInUserGuid, command.Command));
 	}
 
 	private void ReceiveInstanceData(ReceiveInstanceDataCommand command) {
diff --git a/Controller/Phantom.Controller.Services/Agents/AgentManager.cs b/Controller/Phantom.Controller.Services/Agents/AgentManager.cs
index 168d32f..af21c8b 100644
--- a/Controller/Phantom.Controller.Services/Agents/AgentManager.cs
+++ b/Controller/Phantom.Controller.Services/Agents/AgentManager.cs
@@ -1,4 +1,5 @@
 using System.Collections.Concurrent;
+using System.Collections.Immutable;
 using Akka.Actor;
 using Phantom.Common.Data;
 using Phantom.Common.Data.Agent;
@@ -44,7 +45,7 @@ sealed class AgentManager {
 	}
 
 	private ActorRef<AgentActor.ICommand> CreateAgentActor(Guid agentGuid, AgentConfiguration agentConfiguration) {
-		var init = new AgentActor.Init(agentGuid, agentConfiguration, controllerState, minecraftVersions, userLoginManager, dbProvider, cancellationToken);
+		var init = new AgentActor.Init(agentGuid, agentConfiguration, controllerState, minecraftVersions, dbProvider, cancellationToken);
 		var name = "Agent:" + agentGuid;
 		return actorSystem.ActorOf(AgentActor.Factory(init), name);
 	}
@@ -87,7 +88,18 @@ sealed class AgentManager {
 		}
 	}
 
-	public async Task<Result<TReply, UserInstanceActionFailure>> DoInstanceAction<TCommand, TReply>(Guid agentGuid, TCommand command) where TCommand : class, AgentActor.ICommand, ICanReply<Result<TReply, UserInstanceActionFailure>> {
-		return agentsByGuid.TryGetValue(agentGuid, out var agent) ? await agent.Request(command, cancellationToken) : (UserInstanceActionFailure) InstanceActionFailure.AgentDoesNotExist;
+	public async Task<Result<TReply, UserInstanceActionFailure>> DoInstanceAction<TCommand, TReply>(Permission requiredPermission, ImmutableArray<byte> authToken, Guid agentGuid, Func<Guid, TCommand> commandFactoryFromLoggedInUserGuid) where TCommand : class, AgentActor.ICommand, ICanReply<Result<TReply, InstanceActionFailure>> {
+		var loggedInUser = userLoginManager.GetLoggedInUser(authToken);
+		if (!loggedInUser.HasAccessToAgent(agentGuid) || !loggedInUser.CheckPermission(requiredPermission)) {
+			return (UserInstanceActionFailure) UserActionFailure.NotAuthorized;
+		}
+		
+		if (!agentsByGuid.TryGetValue(agentGuid, out var agent)) {
+			return (UserInstanceActionFailure) InstanceActionFailure.AgentDoesNotExist;
+		}
+		
+		var command = commandFactoryFromLoggedInUserGuid(loggedInUser.Guid!.Value);
+		var result = await agent.Request(command, cancellationToken);
+		return result.MapError(static error => (UserInstanceActionFailure) error);
 	}
 }
diff --git a/Controller/Phantom.Controller.Services/ControllerServices.cs b/Controller/Phantom.Controller.Services/ControllerServices.cs
index 91dfbaa..7c62ef7 100644
--- a/Controller/Phantom.Controller.Services/ControllerServices.cs
+++ b/Controller/Phantom.Controller.Services/ControllerServices.cs
@@ -64,7 +64,7 @@ public sealed class ControllerServices : IDisposable {
 		this.InstanceLogManager = new InstanceLogManager();
 		
 		this.AuditLogManager = new AuditLogManager(dbProvider);
-		this.EventLogManager = new EventLogManager(ActorSystem, dbProvider, shutdownCancellationToken);
+		this.EventLogManager = new EventLogManager(ControllerState, ActorSystem, dbProvider, shutdownCancellationToken);
 		
 		this.AgentRegistrationHandler = new AgentRegistrationHandler(AgentManager, InstanceLogManager, EventLogManager);
 		this.WebRegistrationHandler = new WebRegistrationHandler(webAuthToken, ControllerState, InstanceLogManager, UserManager, RoleManager, UserRoleManager, UserLoginManager, AuditLogManager, AgentManager, MinecraftVersions, EventLogManager);
diff --git a/Controller/Phantom.Controller.Services/Events/EventLogManager.cs b/Controller/Phantom.Controller.Services/Events/EventLogManager.cs
index 2e129e9..73361da 100644
--- a/Controller/Phantom.Controller.Services/Events/EventLogManager.cs
+++ b/Controller/Phantom.Controller.Services/Events/EventLogManager.cs
@@ -11,11 +11,13 @@ using Phantom.Utils.Actor;
 namespace Phantom.Controller.Services.Events; 
 
 sealed partial class EventLogManager {
+	private readonly ControllerState controllerState;
 	private readonly ActorRef<EventLogDatabaseStorageActor.ICommand> databaseStorageActor;
 	private readonly IDbContextProvider dbProvider;
 	private readonly CancellationToken cancellationToken;
 
-	public EventLogManager(IActorRefFactory actorSystem, IDbContextProvider dbProvider, CancellationToken cancellationToken) {
+	public EventLogManager(ControllerState controllerState, IActorRefFactory actorSystem, IDbContextProvider dbProvider, CancellationToken cancellationToken) {
+		this.controllerState = controllerState;
 		this.databaseStorageActor = actorSystem.ActorOf(EventLogDatabaseStorageActor.Factory(new EventLogDatabaseStorageActor.Init(dbProvider, cancellationToken)), "EventLogDatabaseStorage");
 		this.dbProvider = dbProvider;
 		this.cancellationToken = cancellationToken;
@@ -30,7 +32,9 @@ sealed partial class EventLogManager {
 			return UserActionFailure.NotAuthorized;
 		}
 		
+		var accessibleAgentGuids = loggedInUser.FilterAccessibleAgentGuids(controllerState.AgentsByGuid.Keys.ToImmutableHashSet());
+		
 		await using var db = dbProvider.Lazy();
-		return await new EventLogRepository(db).GetMostRecentItems(count, cancellationToken);
+		return await new EventLogRepository(db).GetMostRecentItems(accessibleAgentGuids, count, cancellationToken);
 	}
 }
diff --git a/Controller/Phantom.Controller.Services/Rpc/WebMessageHandlerActor.cs b/Controller/Phantom.Controller.Services/Rpc/WebMessageHandlerActor.cs
index 29d2cd8..091671a 100644
--- a/Controller/Phantom.Controller.Services/Rpc/WebMessageHandlerActor.cs
+++ b/Controller/Phantom.Controller.Services/Rpc/WebMessageHandlerActor.cs
@@ -141,19 +141,39 @@ sealed class WebMessageHandlerActor : ReceiveActor<IMessageToController> {
 	}
 
 	private Task<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>> HandleCreateOrUpdateInstance(CreateOrUpdateInstanceMessage message) {
-		return agentManager.DoInstanceAction<AgentActor.CreateOrUpdateInstanceCommand, CreateOrUpdateInstanceResult>(message.Configuration.AgentGuid, new AgentActor.CreateOrUpdateInstanceCommand(message.AuthToken, message.InstanceGuid, message.Configuration));
+		return agentManager.DoInstanceAction<AgentActor.CreateOrUpdateInstanceCommand, CreateOrUpdateInstanceResult>(
+			Permission.CreateInstances,
+			message.AuthToken,
+			message.Configuration.AgentGuid,
+			loggedInUserGuid => new AgentActor.CreateOrUpdateInstanceCommand(loggedInUserGuid, message.InstanceGuid, message.Configuration)
+		);
 	}
 
 	private Task<Result<LaunchInstanceResult, UserInstanceActionFailure>> HandleLaunchInstance(LaunchInstanceMessage message) {
-		return agentManager.DoInstanceAction<AgentActor.LaunchInstanceCommand, LaunchInstanceResult>(message.AgentGuid, new AgentActor.LaunchInstanceCommand(message.AuthToken, message.InstanceGuid));
+		return agentManager.DoInstanceAction<AgentActor.LaunchInstanceCommand, LaunchInstanceResult>(
+			Permission.ControlInstances,
+			message.AuthToken,
+			message.AgentGuid,
+			loggedInUserGuid => new AgentActor.LaunchInstanceCommand(loggedInUserGuid, message.InstanceGuid)
+		);
 	}
 
 	private Task<Result<StopInstanceResult, UserInstanceActionFailure>> HandleStopInstance(StopInstanceMessage message) {
-		return agentManager.DoInstanceAction<AgentActor.StopInstanceCommand, StopInstanceResult>(message.AgentGuid, new AgentActor.StopInstanceCommand(message.AuthToken, message.InstanceGuid, message.StopStrategy));
+		return agentManager.DoInstanceAction<AgentActor.StopInstanceCommand, StopInstanceResult>(
+			Permission.ControlInstances,
+			message.AuthToken,
+			message.AgentGuid,
+			loggedInUserGuid => new AgentActor.StopInstanceCommand(loggedInUserGuid, message.InstanceGuid, message.StopStrategy)
+		);
 	}
 
 	private Task<Result<SendCommandToInstanceResult, UserInstanceActionFailure>> HandleSendCommandToInstance(SendCommandToInstanceMessage message) {
-		return agentManager.DoInstanceAction<AgentActor.SendCommandToInstanceCommand, SendCommandToInstanceResult>(message.AgentGuid, new AgentActor.SendCommandToInstanceCommand(message.AuthToken, message.InstanceGuid, message.Command));
+		return agentManager.DoInstanceAction<AgentActor.SendCommandToInstanceCommand, SendCommandToInstanceResult>(
+			Permission.ControlInstances,
+			message.AuthToken,
+			message.AgentGuid,
+			loggedInUserGuid => new AgentActor.SendCommandToInstanceCommand(loggedInUserGuid, message.InstanceGuid, message.Command)
+		);
 	}
 
 	private Task<ImmutableArray<MinecraftVersion>> HandleGetMinecraftVersions(GetMinecraftVersionsMessage message) {
diff --git a/Controller/Phantom.Controller.Services/Users/Sessions/AuthenticatedUserCache.cs b/Controller/Phantom.Controller.Services/Users/Sessions/AuthenticatedUserCache.cs
index 7a77c4c..c850cf0 100644
--- a/Controller/Phantom.Controller.Services/Users/Sessions/AuthenticatedUserCache.cs
+++ b/Controller/Phantom.Controller.Services/Users/Sessions/AuthenticatedUserCache.cs
@@ -14,9 +14,12 @@ sealed class AuthenticatedUserCache {
 	}
 
 	public async Task<AuthenticatedUserInfo?> Update(UserEntity user, ILazyDbContext db) {
+		var permissionRepository = new PermissionRepository(db);
+		var userPermissions = await permissionRepository.GetAllUserPermissions(user);
+		var userManagedAgentGuids = await permissionRepository.GetManagedAgentGuids(user);
+		
 		var userGuid = user.UserGuid;
-		var userPermissions = await new PermissionRepository(db).GetAllUserPermissions(user);
-		var userInfo = new AuthenticatedUserInfo(userGuid, user.Name, userPermissions);
+		var userInfo = new AuthenticatedUserInfo(userGuid, user.Name, userPermissions, userManagedAgentGuids);
 		return authenticatedUsersByGuid[userGuid] = userInfo;
 	}
 	
diff --git a/Controller/Phantom.Controller.Services/Users/Sessions/LoggedInUser.cs b/Controller/Phantom.Controller.Services/Users/Sessions/LoggedInUser.cs
index 583c03b..043ac70 100644
--- a/Controller/Phantom.Controller.Services/Users/Sessions/LoggedInUser.cs
+++ b/Controller/Phantom.Controller.Services/Users/Sessions/LoggedInUser.cs
@@ -1,4 +1,5 @@
-using Phantom.Common.Data.Web.Users;
+using System.Collections.Immutable;
+using Phantom.Common.Data.Web.Users;
 
 namespace Phantom.Controller.Services.Users.Sessions;
 
@@ -6,6 +7,14 @@ readonly record struct LoggedInUser(AuthenticatedUserInfo? AuthenticatedUserInfo
 	public Guid? Guid => AuthenticatedUserInfo?.Guid;
 	
 	public bool CheckPermission(Permission permission) {
-		return AuthenticatedUserInfo != null && AuthenticatedUserInfo.Permissions.Check(permission);
+		return AuthenticatedUserInfo is {} info && info.CheckPermission(permission);
+	}
+
+	public bool HasAccessToAgent(Guid agentGuid) {
+		return AuthenticatedUserInfo is {} info && info.HasAccessToAgent(agentGuid);
+	}
+
+	public ImmutableHashSet<Guid> FilterAccessibleAgentGuids(ImmutableHashSet<Guid> agentGuids) {
+		return AuthenticatedUserInfo is {} info ? info.FilterAccessibleAgentGuids(agentGuids) : ImmutableHashSet<Guid>.Empty;
 	}
 }
diff --git a/Web/Phantom.Web.Services/Agents/AgentManager.cs b/Web/Phantom.Web.Services/Agents/AgentManager.cs
index 4ab2a79..e203405 100644
--- a/Web/Phantom.Web.Services/Agents/AgentManager.cs
+++ b/Web/Phantom.Web.Services/Agents/AgentManager.cs
@@ -2,6 +2,7 @@
 using Phantom.Common.Data.Web.Agent;
 using Phantom.Utils.Events;
 using Phantom.Utils.Logging;
+using Phantom.Web.Services.Authentication;
 
 namespace Phantom.Web.Services.Agents; 
 
@@ -18,7 +19,13 @@ public sealed class AgentManager {
 		return agents.Value;
 	}
 	
-	public ImmutableDictionary<Guid, Agent> ToDictionaryByGuid() {
-		return agents.Value.ToImmutableDictionary(static agent => agent.AgentGuid);
+	public ImmutableDictionary<Guid, Agent> ToDictionaryByGuid(AuthenticatedUser? authenticatedUser) {
+		if (authenticatedUser == null) {
+			return ImmutableDictionary<Guid, Agent>.Empty;
+		}
+		
+		return agents.Value
+		             .Where(agent => authenticatedUser.Info.HasAccessToAgent(agent.AgentGuid))
+		             .ToImmutableDictionary(static agent => agent.AgentGuid);
 	}
 }
diff --git a/Web/Phantom.Web.Services/Authentication/AuthenticatedUser.cs b/Web/Phantom.Web.Services/Authentication/AuthenticatedUser.cs
index 8f024b6..7554775 100644
--- a/Web/Phantom.Web.Services/Authentication/AuthenticatedUser.cs
+++ b/Web/Phantom.Web.Services/Authentication/AuthenticatedUser.cs
@@ -3,8 +3,4 @@ using Phantom.Common.Data.Web.Users;
 
 namespace Phantom.Web.Services.Authentication;
 
-public sealed record AuthenticatedUser(AuthenticatedUserInfo Info, ImmutableArray<byte> Token) {
-	public bool CheckPermission(Permission permission) {
-		return Info.Permissions.Check(permission);
-	}
-}
+public sealed record AuthenticatedUser(AuthenticatedUserInfo Info, ImmutableArray<byte> Token);
diff --git a/Web/Phantom.Web.Services/Events/EventLogManager.cs b/Web/Phantom.Web.Services/Events/EventLogManager.cs
index 0bb5f55..79eb8aa 100644
--- a/Web/Phantom.Web.Services/Events/EventLogManager.cs
+++ b/Web/Phantom.Web.Services/Events/EventLogManager.cs
@@ -16,7 +16,7 @@ public sealed class EventLogManager {
 	}
 
 	public async Task<Result<ImmutableArray<EventLogItem>, UserActionFailure>> GetMostRecentItems(AuthenticatedUser? authenticatedUser, int count, CancellationToken cancellationToken) {
-		if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.ViewEvents)) {
+		if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.ViewEvents)) {
 			var message = new GetEventLogMessage(authenticatedUser.Token, count);
 			return await controllerConnection.Send<GetEventLogMessage, Result<ImmutableArray<EventLogItem>, UserActionFailure>>(message, cancellationToken);
 		}
diff --git a/Web/Phantom.Web.Services/Instances/InstanceManager.cs b/Web/Phantom.Web.Services/Instances/InstanceManager.cs
index 98f1633..5f1ddfc 100644
--- a/Web/Phantom.Web.Services/Instances/InstanceManager.cs
+++ b/Web/Phantom.Web.Services/Instances/InstanceManager.cs
@@ -33,12 +33,17 @@ public sealed class InstanceManager {
 		return instances.Value;
 	}
 
-	public Instance? GetByGuid(Guid instanceGuid) {
-		return instances.Value.GetValueOrDefault(instanceGuid);
+	public Instance? GetByGuid(AuthenticatedUser? authenticatedUser, Guid instanceGuid) {
+		if (authenticatedUser == null) {
+			return null;
+		}
+		
+		var instance = instances.Value.GetValueOrDefault(instanceGuid);
+		return instance != null && authenticatedUser.Info.HasAccessToAgent(instance.Configuration.AgentGuid) ? instance : null;
 	}
 
 	public async Task<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>> CreateOrUpdateInstance(AuthenticatedUser? authenticatedUser, Guid instanceGuid, InstanceConfiguration configuration, CancellationToken cancellationToken) {
-		if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.CreateInstances)) {
+		if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.CreateInstances)) {
 			var message = new CreateOrUpdateInstanceMessage(authenticatedUser.Token, instanceGuid, configuration);
 			return await controllerConnection.Send<CreateOrUpdateInstanceMessage, Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>>(message, cancellationToken);
 		}
@@ -48,7 +53,7 @@ public sealed class InstanceManager {
 	}
 
 	public async Task<Result<LaunchInstanceResult, UserInstanceActionFailure>> LaunchInstance(AuthenticatedUser? authenticatedUser, Guid agentGuid, Guid instanceGuid, CancellationToken cancellationToken) {
-		if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.ControlInstances)) {
+		if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.ControlInstances)) {
 			var message = new LaunchInstanceMessage(authenticatedUser.Token, agentGuid, instanceGuid);
 			return await controllerConnection.Send<LaunchInstanceMessage, Result<LaunchInstanceResult, UserInstanceActionFailure>>(message, cancellationToken);
 		}
@@ -58,7 +63,7 @@ public sealed class InstanceManager {
 	}
 
 	public async Task<Result<StopInstanceResult, UserInstanceActionFailure>> StopInstance(AuthenticatedUser? authenticatedUser, Guid agentGuid, Guid instanceGuid, MinecraftStopStrategy stopStrategy, CancellationToken cancellationToken) {
-		if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.ControlInstances)) {
+		if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.ControlInstances)) {
 			var message = new StopInstanceMessage(authenticatedUser.Token, agentGuid, instanceGuid, stopStrategy);
 			return await controllerConnection.Send<StopInstanceMessage, Result<StopInstanceResult, UserInstanceActionFailure>>(message, cancellationToken);
 		}
@@ -68,7 +73,7 @@ public sealed class InstanceManager {
 	}
 
 	public async Task<Result<SendCommandToInstanceResult, UserInstanceActionFailure>> SendCommandToInstance(AuthenticatedUser? authenticatedUser, Guid agentGuid, Guid instanceGuid, string command, CancellationToken cancellationToken) {
-		if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.ControlInstances)) {
+		if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.ControlInstances)) {
 			var message = new SendCommandToInstanceMessage(authenticatedUser.Token, agentGuid, instanceGuid, command);
 			return await controllerConnection.Send<SendCommandToInstanceMessage, Result<SendCommandToInstanceResult, UserInstanceActionFailure>>(message, cancellationToken);
 		}
diff --git a/Web/Phantom.Web.Services/Users/AuditLogManager.cs b/Web/Phantom.Web.Services/Users/AuditLogManager.cs
index ff20c4c..9f89b25 100644
--- a/Web/Phantom.Web.Services/Users/AuditLogManager.cs
+++ b/Web/Phantom.Web.Services/Users/AuditLogManager.cs
@@ -16,7 +16,7 @@ public sealed class AuditLogManager {
 	}
 
 	public async Task<Result<ImmutableArray<AuditLogItem>, UserActionFailure>> GetMostRecentItems(AuthenticatedUser? authenticatedUser, int count, CancellationToken cancellationToken) {
-		if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.ViewAudit)) {
+		if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.ViewAudit)) {
 			var message = new GetAuditLogMessage(authenticatedUser.Token, count);
 			return await controllerConnection.Send<GetAuditLogMessage, Result<ImmutableArray<AuditLogItem>, UserActionFailure>>(message, cancellationToken);
 		}
diff --git a/Web/Phantom.Web.Services/Users/UserManager.cs b/Web/Phantom.Web.Services/Users/UserManager.cs
index 30edf57..bcb0672 100644
--- a/Web/Phantom.Web.Services/Users/UserManager.cs
+++ b/Web/Phantom.Web.Services/Users/UserManager.cs
@@ -19,7 +19,7 @@ public sealed class UserManager {
 	}
 
 	public async Task<Result<CreateUserResult, UserActionFailure>> Create(AuthenticatedUser? authenticatedUser, string username, string password, CancellationToken cancellationToken) {
-		if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.EditUsers)) {
+		if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.EditUsers)) {
 			return await controllerConnection.Send<CreateUserMessage, Result<CreateUserResult, UserActionFailure>>(new CreateUserMessage(authenticatedUser.Token, username, password), cancellationToken);
 		}
 		else {
@@ -28,7 +28,7 @@ public sealed class UserManager {
 	}
 	
 	public async Task<Result<DeleteUserResult, UserActionFailure>> DeleteByGuid(AuthenticatedUser? authenticatedUser, Guid userGuid, CancellationToken cancellationToken) {
-		if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.EditUsers)) {
+		if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.EditUsers)) {
 			return await controllerConnection.Send<DeleteUserMessage, Result<DeleteUserResult, UserActionFailure>>(new DeleteUserMessage(authenticatedUser.Token, userGuid), cancellationToken);
 		}
 		else {
diff --git a/Web/Phantom.Web.Services/Users/UserRoleManager.cs b/Web/Phantom.Web.Services/Users/UserRoleManager.cs
index 2fd20b1..6e86c04 100644
--- a/Web/Phantom.Web.Services/Users/UserRoleManager.cs
+++ b/Web/Phantom.Web.Services/Users/UserRoleManager.cs
@@ -23,7 +23,7 @@ public sealed class UserRoleManager {
 	}
 
 	public async Task<Result<ChangeUserRolesResult, UserActionFailure>> ChangeUserRoles(AuthenticatedUser? authenticatedUser, Guid subjectUserGuid, ImmutableHashSet<Guid> addToRoleGuids, ImmutableHashSet<Guid> removeFromRoleGuids, CancellationToken cancellationToken) {
-		if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.EditUsers)) {
+		if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.EditUsers)) {
 			return await controllerConnection.Send<ChangeUserRolesMessage, Result<ChangeUserRolesResult, UserActionFailure>>(new ChangeUserRolesMessage(authenticatedUser.Token, subjectUserGuid, addToRoleGuids, removeFromRoleGuids), cancellationToken);
 		}
 		else {
diff --git a/Web/Phantom.Web/Pages/Agents.razor b/Web/Phantom.Web/Pages/Agents.razor
index 5c273f8..d7bf359 100644
--- a/Web/Phantom.Web/Pages/Agents.razor
+++ b/Web/Phantom.Web/Pages/Agents.razor
@@ -1,4 +1,5 @@
 @page "/agents"
+@using System.Collections.Immutable
 @using Phantom.Common.Data.Web.Agent
 @using Phantom.Utils.Collections
 @using Phantom.Web.Services.Agents
@@ -46,33 +47,45 @@
         <Cell class="fw-semibold text-center text-success">Online</Cell>
         <Cell class="text-end">-</Cell>
         break;
+
       case AgentIsOffline:
         <Cell class="fw-semibold text-center">Offline</Cell>
         <Cell class="text-end">N/A</Cell>
         break;
+
       case AgentIsDisconnected status:
         <Cell class="fw-semibold text-center">Offline</Cell>
         <Cell class="text-end">
           <TimeWithOffset Time="status.LastPingTime" />
         </Cell>
         break;
+
       default:
         <Cell class="fw-semibold text-center">N/A</Cell>
         break;
     }
   </ItemRow>
   <NoItemsRow>
-    No agents registered.
+    No agents found.
   </NoItemsRow>
 </Table>
 
 @code {
 
-  private readonly TableData<Agent, Guid> agentTable = new();
+  private TableData<Agent, Guid>? agentTable;
+
+  protected override async Task OnInitializedAsync() {
+    var authenticatedUser = await GetAuthenticatedUser();
+    if (authenticatedUser == null) {
+      return;
+    }
 
-  protected override void OnInitialized() {
     AgentManager.AgentsChanged.Subscribe(this, agents => {
-      var sortedAgents = agents.Sort(static (a1, a2) => a1.Configuration.AgentName.CompareTo(a2.Configuration.AgentName));
+      var sortedAgents = agents.Where(agent => authenticatedUser.Info.HasAccessToAgent(agent.AgentGuid))
+                               .OrderBy(static agent => agent.Configuration.AgentName)
+                               .ToImmutableArray();
+
+      agentTable ??= new TableData<Agent, Guid>();
       agentTable.UpdateFrom(sortedAgents, static agent => agent.AgentGuid, static agent => agent, static (agent, _) => agent);
       InvokeAsync(StateHasChanged);
     });
diff --git a/Web/Phantom.Web/Pages/InstanceDetail.razor b/Web/Phantom.Web/Pages/InstanceDetail.razor
index d075087..54b51f9 100644
--- a/Web/Phantom.Web/Pages/InstanceDetail.razor
+++ b/Web/Phantom.Web/Pages/InstanceDetail.razor
@@ -7,9 +7,15 @@
 @using Phantom.Common.Data.Instance
 @using Phantom.Web.Services.Instances
 @using Phantom.Web.Services.Authorization
-@inherits Phantom.Web.Components.PhantomComponent
+@inherits PhantomComponent
 @inject InstanceManager InstanceManager
 
+@if (isLoading) {
+  <h1>Instance</h1>
+  <p>Loading...</p>
+  return;
+}
+
 @if (Instance == null) {
   <h1>Instance Not Found</h1>
   <p>Return to <a href="instances">all instances</a>.</p>
@@ -54,19 +60,25 @@
   [Parameter]
   public Guid InstanceGuid { get; init; }
 
+  private Instance? Instance { get; set; }
+  private bool isLoading = true;
+
   private string? lastError = null;
   private bool isLaunchingInstance = false;
 
-  private Instance? Instance { get; set; }
+  protected override async Task OnInitializedAsync() {
+    Instance = InstanceManager.GetByGuid(await GetAuthenticatedUser(), InstanceGuid);
+    isLoading = false;
 
-  protected override void OnInitialized() {
-    InstanceManager.InstancesChanged.Subscribe(this, instances => {
-      var newInstance = instances.TryGetValue(InstanceGuid, out var instance) ? instance : null;
-      if (newInstance != Instance) {
-        Instance = newInstance;
-        InvokeAsync(StateHasChanged);
-      }
-    });
+    if (Instance != null) {
+      InstanceManager.InstancesChanged.Subscribe(this, instances => {
+        var newInstance = instances.TryGetValue(InstanceGuid, out var instance) ? instance : null;
+        if (newInstance != Instance) {
+          Instance = newInstance;
+          InvokeAsync(StateHasChanged);
+        }
+      });
+    }
   }
 
   private async Task LaunchInstance() {
diff --git a/Web/Phantom.Web/Pages/InstanceEdit.razor b/Web/Phantom.Web/Pages/InstanceEdit.razor
index 20e95b6..f67f8e9 100644
--- a/Web/Phantom.Web/Pages/InstanceEdit.razor
+++ b/Web/Phantom.Web/Pages/InstanceEdit.razor
@@ -6,14 +6,20 @@
 @inherits PhantomComponent
 @inject InstanceManager InstanceManager
 
+@if (isLoading) {
+  <h1>Edit Instance</h1>
+  <p>Loading...</p>
+  return;
+}
+
 @if (Instance == null) {
   <h1>Instance Not Found</h1>
   <p>Return to <a href="instances">all instances</a>.</p>
+  return;
 }
-else {
-  <h1>Edit Instance: @Instance.Configuration.InstanceName</h1>
-  <InstanceAddOrEditForm EditedInstance="Instance" />
-}
+
+<h1>Edit Instance: @Instance.Configuration.InstanceName</h1>
+<InstanceAddOrEditForm EditedInstance="Instance" />
 
 @code {
 
@@ -21,9 +27,11 @@ else {
   public Guid InstanceGuid { get; init; }
 
   private Instance? Instance { get; set; }
+  private bool isLoading = true;
 
-  protected override void OnInitialized() {
-    Instance = InstanceManager.GetByGuid(InstanceGuid);
+  protected override async Task OnInitializedAsync() {
+    Instance = InstanceManager.GetByGuid(await GetAuthenticatedUser(), InstanceGuid);
+    isLoading = false;
   }
 
 }
diff --git a/Web/Phantom.Web/Pages/Instances.razor b/Web/Phantom.Web/Pages/Instances.razor
index cdb2a5a..0e4bdbb 100644
--- a/Web/Phantom.Web/Pages/Instances.razor
+++ b/Web/Phantom.Web/Pages/Instances.razor
@@ -69,9 +69,18 @@
       this.agentNamesByGuid = agents.ToImmutableDictionary(static agent => agent.AgentGuid, static agent => agent.Configuration.AgentName);
       InvokeAsync(StateHasChanged);
     });
+  }
+
+  protected override async Task OnInitializedAsync() {
+    var authenticatedUser = await GetAuthenticatedUser();
+    if (authenticatedUser == null) {
+      instances = ImmutableArray<Instance>.Empty;
+      return;
+    }
 
     InstanceManager.InstancesChanged.Subscribe(this, instances => {
       this.instances = instances.Values
+                                .Where(instance => authenticatedUser.Info.HasAccessToAgent(instance.Configuration.AgentGuid))
                                 .OrderBy(instance => agentNamesByGuid.TryGetValue(instance.Configuration.AgentGuid, out var agentName) ? agentName : string.Empty)
                                 .ThenBy(static instance => instance.Configuration.InstanceName)
                                 .ToImmutableArray();
diff --git a/Web/Phantom.Web/Shared/InstanceAddOrEditForm.razor b/Web/Phantom.Web/Shared/InstanceAddOrEditForm.razor
index 97a676e..5d970d3 100644
--- a/Web/Phantom.Web/Shared/InstanceAddOrEditForm.razor
+++ b/Web/Phantom.Web/Shared/InstanceAddOrEditForm.razor
@@ -278,10 +278,11 @@
   }
 
   protected override async Task OnInitializedAsync() {
+    var authenticatedUser = await GetAuthenticatedUser();
     var agentJavaRuntimesTask = ControllerConnection.Send<GetAgentJavaRuntimesMessage, ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>>(new GetAgentJavaRuntimesMessage(), TimeSpan.FromSeconds(30));
     var minecraftVersionsTask = ControllerConnection.Send<GetMinecraftVersionsMessage, ImmutableArray<MinecraftVersion>>(new GetMinecraftVersionsMessage(), TimeSpan.FromSeconds(30));
 
-    allAgentsByGuid = AgentManager.ToDictionaryByGuid();
+    allAgentsByGuid = AgentManager.ToDictionaryByGuid(authenticatedUser);
     allAgentJavaRuntimes = await agentJavaRuntimesTask;
     allMinecraftVersions = await minecraftVersionsTask;