From 81f11d8f860803cf01fbb2cfd106bd3344db98f2 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 15 Jan 2023 20:52:53 +0900
Subject: [PATCH] refactor: rename role.options -> role.policies

---
 locales/ja-JP.yml                             |   1 +
 .../migration/1673783015567-Policies.js       |  13 ++
 packages/backend/src/core/DriveService.ts     |   4 +-
 .../backend/src/core/NoteCreateService.ts     |   2 +-
 .../backend/src/core/NotePiningService.ts     |   2 +-
 packages/backend/src/core/RoleService.ts      |  26 ++--
 packages/backend/src/core/UserListService.ts  |   2 +-
 .../src/core/entities/RoleEntityService.ts    |  10 +-
 .../src/core/entities/UserEntityService.ts    |   4 +-
 packages/backend/src/models/entities/Meta.ts  |   2 +-
 packages/backend/src/models/entities/Role.ts  |   2 +-
 .../src/server/NodeinfoServerService.ts       |   8 +-
 .../backend/src/server/api/ApiCallService.ts  |   8 +-
 .../backend/src/server/api/EndpointsModule.ts |   8 +-
 packages/backend/src/server/api/endpoints.ts  |   6 +-
 .../endpoints/admin/emoji/add-aliases-bulk.ts |   2 +-
 .../server/api/endpoints/admin/emoji/add.ts   |   2 +-
 .../server/api/endpoints/admin/emoji/copy.ts  |   2 +-
 .../api/endpoints/admin/emoji/delete-bulk.ts  |   2 +-
 .../api/endpoints/admin/emoji/delete.ts       |   2 +-
 .../api/endpoints/admin/emoji/import-zip.ts   |   2 +-
 .../api/endpoints/admin/emoji/list-remote.ts  |   2 +-
 .../server/api/endpoints/admin/emoji/list.ts  |   2 +-
 .../admin/emoji/remove-aliases-bulk.ts        |   2 +-
 .../endpoints/admin/emoji/set-aliases-bulk.ts |   2 +-
 .../admin/emoji/set-category-bulk.ts          |   2 +-
 .../api/endpoints/admin/emoji/update.ts       |   2 +-
 .../src/server/api/endpoints/admin/meta.ts    |   4 +-
 .../api/endpoints/admin/roles/create.ts       |   6 +-
 ...override.ts => update-default-policies.ts} |   8 +-
 .../api/endpoints/admin/roles/update.ts       |   6 +-
 .../server/api/endpoints/admin/show-user.ts   |   3 +-
 .../server/api/endpoints/antennas/create.ts   |   2 +-
 .../server/api/endpoints/clips/add-note.ts    |   2 +-
 .../src/server/api/endpoints/clips/create.ts  |   2 +-
 .../backend/src/server/api/endpoints/drive.ts |   4 +-
 .../src/server/api/endpoints/i/update.ts      |   2 +-
 .../server/api/endpoints/i/webhooks/create.ts |   2 +-
 .../src/server/api/endpoints/invite.ts        |   2 +-
 .../backend/src/server/api/endpoints/meta.ts  |   4 +-
 .../api/endpoints/notes/global-timeline.ts    |   4 +-
 .../api/endpoints/notes/hybrid-timeline.ts    |   4 +-
 .../api/endpoints/notes/local-timeline.ts     |   4 +-
 .../api/endpoints/users/lists/create.ts       |   2 +-
 .../api/stream/channels/global-timeline.ts    |   4 +-
 .../api/stream/channels/hybrid-timeline.ts    |   4 +-
 .../api/stream/channels/local-timeline.ts     |   4 +-
 .../backend/src/server/api/stream/types.ts    |   2 +-
 .../frontend/src/pages/admin/roles.editor.vue | 142 +++++++++---------
 packages/frontend/src/pages/admin/roles.vue   | 119 +++++++--------
 packages/frontend/src/pages/timeline.vue      |   4 +-
 packages/frontend/src/pages/user-info.vue     |  20 ++-
 packages/frontend/src/ui/_common_/common.ts   |   4 +-
 53 files changed, 254 insertions(+), 232 deletions(-)
 create mode 100644 packages/backend/migration/1673783015567-Policies.js
 rename packages/backend/src/server/api/endpoints/admin/roles/{update-default-role-override.ts => update-default-policies.ts} (85%)

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 79856447a0..786d41ca41 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -952,6 +952,7 @@ _role:
   isPublic: "ロールを公開"
   descriptionOfIsPublic: "ロールにアサインされたユーザーを誰でも見ることができます。また、ユーザーのプロフィールでこのロールが表示されます。"
   options: "オプション"
+  policies: "ポリシー"
   baseRole: "ベースロール"
   useBaseValue: "ベースロールの値を使用"
   chooseRoleToAssign: "アサインするロールを選択"
diff --git a/packages/backend/migration/1673783015567-Policies.js b/packages/backend/migration/1673783015567-Policies.js
new file mode 100644
index 0000000000..8b36921d41
--- /dev/null
+++ b/packages/backend/migration/1673783015567-Policies.js
@@ -0,0 +1,13 @@
+export class Policies1673783015567 {
+    name = 'Policies1673783015567'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "role" RENAME COLUMN "options" TO "policies"`);
+				await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "defaultRoleOverride" TO "policies"`);
+    }
+
+    async down(queryRunner) {
+				await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "policies" TO "defaultRoleOverride"`);
+        await queryRunner.query(`ALTER TABLE "role" RENAME COLUMN "policies" TO "options"`);
+    }
+}
diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts
index 9002c96a65..598a457e83 100644
--- a/packages/backend/src/core/DriveService.ts
+++ b/packages/backend/src/core/DriveService.ts
@@ -479,8 +479,8 @@ export class DriveService {
 		if (user && !isLink) {
 			const usage = await this.driveFileEntityService.calcDriveUsageOf(user);
 
-			const role = await this.roleService.getUserRoleOptions(user.id);
-			const driveCapacity = 1024 * 1024 * role.driveCapacityMb;
+			const policies = await this.roleService.getUserPolicies(user.id);
+			const driveCapacity = 1024 * 1024 * policies.driveCapacityMb;
 			this.registerLogger.debug('drive capacity override applied');
 			this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`);
 
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 112b84fdf9..3dc44a25fe 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -226,7 +226,7 @@ export class NoteCreateService {
 		if (data.channel != null) data.localOnly = true;
 
 		if (data.visibility === 'public' && data.channel == null) {
-			if ((await this.roleService.getUserRoleOptions(user.id)).canPublicNote === false) {
+			if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
 				data.visibility = 'home';
 			}
 		}
diff --git a/packages/backend/src/core/NotePiningService.ts b/packages/backend/src/core/NotePiningService.ts
index bc038e17a7..bb6def1edb 100644
--- a/packages/backend/src/core/NotePiningService.ts
+++ b/packages/backend/src/core/NotePiningService.ts
@@ -57,7 +57,7 @@ export class NotePiningService {
 
 		const pinings = await this.userNotePiningsRepository.findBy({ userId: user.id });
 
-		if (pinings.length >= (await this.roleService.getUserRoleOptions(user.id)).pinLimit) {
+		if (pinings.length >= (await this.roleService.getUserPolicies(user.id)).pinLimit) {
 			throw new IdentifiableError('15a018eb-58e5-4da1-93be-330fcc5e4e1a', 'You can not pin notes any more.');
 		}
 
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 42b477d9ed..abad058303 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -13,7 +13,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { StreamMessages } from '@/server/api/stream/types.js';
 import type { OnApplicationShutdown } from '@nestjs/common';
 
-export type RoleOptions = {
+export type RolePolicies = {
 	gtlAvailable: boolean;
 	ltlAvailable: boolean;
 	canPublicNote: boolean;
@@ -31,7 +31,7 @@ export type RoleOptions = {
 	rateLimitFactor: number;
 };
 
-export const DEFAULT_ROLE: RoleOptions = {
+export const DEFAULT_POLICIES: RolePolicies = {
 	gtlAvailable: true,
 	ltlAvailable: true,
 	canPublicNote: true,
@@ -195,26 +195,26 @@ export class RoleService implements OnApplicationShutdown {
 	}
 
 	@bindThis
-	public async getUserRoleOptions(userId: User['id'] | null): Promise<RoleOptions> {
+	public async getUserPolicies(userId: User['id'] | null): Promise<RolePolicies> {
 		const meta = await this.metaService.fetch();
-		const baseRoleOptions = { ...DEFAULT_ROLE, ...meta.defaultRoleOverride };
+		const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies };
 
-		if (userId == null) return baseRoleOptions;
+		if (userId == null) return basePolicies;
 
 		const roles = await this.getUserRoles(userId);
 
-		function calc<T extends keyof RoleOptions>(name: T, aggregate: (values: RoleOptions[T][]) => RoleOptions[T]) {
-			if (roles.length === 0) return baseRoleOptions[name];
+		function calc<T extends keyof RolePolicies>(name: T, aggregate: (values: RolePolicies[T][]) => RolePolicies[T]) {
+			if (roles.length === 0) return basePolicies[name];
 
-			const options = roles.map(role => role.options[name] ?? { priority: 0, useDefault: true });
+			const policies = roles.map(role => role.policies[name] ?? { priority: 0, useDefault: true });
 
-			const p2 = options.filter(option => option.priority === 2);
-			if (p2.length > 0) return aggregate(p2.map(option => option.useDefault ? baseRoleOptions[name] : option.value));
+			const p2 = policies.filter(policy => policy.priority === 2);
+			if (p2.length > 0) return aggregate(p2.map(policy => policy.useDefault ? basePolicies[name] : policy.value));
 
-			const p1 = options.filter(option => option.priority === 1);
-			if (p1.length > 0) return aggregate(p2.map(option => option.useDefault ? baseRoleOptions[name] : option.value));
+			const p1 = policies.filter(policy => policy.priority === 1);
+			if (p1.length > 0) return aggregate(p2.map(policy => policy.useDefault ? basePolicies[name] : policy.value));
 
-			return aggregate(options.map(option => option.useDefault ? baseRoleOptions[name] : option.value));
+			return aggregate(policies.map(policy => policy.useDefault ? basePolicies[name] : policy.value));
 		}
 
 		return {
diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts
index 18c9787fa8..fc48738307 100644
--- a/packages/backend/src/core/UserListService.ts
+++ b/packages/backend/src/core/UserListService.ts
@@ -35,7 +35,7 @@ export class UserListService {
 		const currentCount = await this.userListJoiningsRepository.countBy({
 			userListId: list.id,
 		});
-		if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).userEachUserListsLimit) {
+		if (currentCount > (await this.roleService.getUserPolicies(me.id)).userEachUserListsLimit) {
 			throw new Error('Too many users');
 		}
 
diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts
index 6a14775653..52f3374468 100644
--- a/packages/backend/src/core/entities/RoleEntityService.ts
+++ b/packages/backend/src/core/entities/RoleEntityService.ts
@@ -6,7 +6,7 @@ import type { Packed } from '@/misc/schema.js';
 import type { User } from '@/models/entities/User.js';
 import type { Role } from '@/models/entities/Role.js';
 import { bindThis } from '@/decorators.js';
-import { DEFAULT_ROLE } from '@/core/RoleService.js';
+import { DEFAULT_POLICIES } from '@/core/RoleService.js';
 import { UserEntityService } from './UserEntityService.js';
 
 @Injectable()
@@ -40,9 +40,9 @@ export class RoleEntityService {
 			roleId: role.id,
 		});
 
-		const roleOptions = { ...role.options };
-		for (const [k, v] of Object.entries(DEFAULT_ROLE)) {
-			if (roleOptions[k] == null) roleOptions[k] = {
+		const policies = { ...role.policies };
+		for (const [k, v] of Object.entries(DEFAULT_POLICIES)) {
+			if (policies[k] == null) policies[k] = {
 				useDefault: true,
 				priority: 0,
 				value: v,
@@ -62,7 +62,7 @@ export class RoleEntityService {
 			isAdministrator: role.isAdministrator,
 			isModerator: role.isModerator,
 			canEditMembersByModerator: role.canEditMembersByModerator,
-			options: roleOptions,
+			policies: policies,
 			usersCount: assigns.length,
 			...(opts.detail ? {
 				users: this.userEntityService.packMany(assigns.map(x => x.userId), me),
diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index 6b754150cf..bf6f6f4553 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -423,7 +423,7 @@ export class UserEntityService implements OnModuleInit {
 				bannerUrl: user.banner ? this.driveFileEntityService.getPublicUrl(user.banner, false) : null,
 				bannerBlurhash: user.banner?.blurhash ?? null,
 				isLocked: user.isLocked,
-				isSilenced: this.roleService.getUserRoleOptions(user.id).then(r => !r.canPublicNote),
+				isSilenced: this.roleService.getUserPolicies(user.id).then(r => !r.canPublicNote),
 				isSuspended: user.isSuspended ?? falsy,
 				description: profile!.description,
 				location: profile!.location,
@@ -496,7 +496,7 @@ export class UserEntityService implements OnModuleInit {
 			} : {}),
 
 			...(opts.includeSecrets ? {
-				role: this.roleService.getUserRoleOptions(user.id),
+				policies: this.roleService.getUserPolicies(user.id),
 				email: profile!.email,
 				emailVerified: profile!.emailVerified,
 				securityKeysList: profile!.twoFactorEnabled
diff --git a/packages/backend/src/models/entities/Meta.ts b/packages/backend/src/models/entities/Meta.ts
index e724ba9a49..5d222a6da1 100644
--- a/packages/backend/src/models/entities/Meta.ts
+++ b/packages/backend/src/models/entities/Meta.ts
@@ -458,5 +458,5 @@ export class Meta {
 	@Column('jsonb', {
 		default: { },
 	})
-	public defaultRoleOverride: Record<string, any>;
+	public policies: Record<string, any>;
 }
diff --git a/packages/backend/src/models/entities/Role.ts b/packages/backend/src/models/entities/Role.ts
index d8d203493b..abd5f864a2 100644
--- a/packages/backend/src/models/entities/Role.ts
+++ b/packages/backend/src/models/entities/Role.ts
@@ -136,7 +136,7 @@ export class Role {
 	@Column('jsonb', {
 		default: { },
 	})
-	public options: Record<string, {
+	public policies: Record<string, {
 		useDefault: boolean;
 		priority: number;
 		value: any;
diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts
index 19380d13a4..024ddfe632 100644
--- a/packages/backend/src/server/NodeinfoServerService.ts
+++ b/packages/backend/src/server/NodeinfoServerService.ts
@@ -10,7 +10,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { bindThis } from '@/decorators.js';
 import NotesChart from '@/core/chart/charts/notes.js';
 import UsersChart from '@/core/chart/charts/users.js';
-import { DEFAULT_ROLE } from '@/core/RoleService.js';
+import { DEFAULT_POLICIES } from '@/core/RoleService.js';
 import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
 
 const nodeinfo2_1path = '/nodeinfo/2.1';
@@ -74,7 +74,7 @@ export class NodeinfoServerService {
 
 			const proxyAccount = meta.proxyAccountId ? await this.userEntityService.pack(meta.proxyAccountId).catch(() => null) : null;
 
-			const baseRoleOptions = { ...DEFAULT_ROLE, ...meta.defaultRoleOverride };
+			const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies };
 
 			return {
 				software: {
@@ -105,8 +105,8 @@ export class NodeinfoServerService {
 					repositoryUrl: meta.repositoryUrl,
 					feedbackUrl: meta.feedbackUrl,
 					disableRegistration: meta.disableRegistration,
-					disableLocalTimeline: !baseRoleOptions.ltlAvailable,
-					disableGlobalTimeline: !baseRoleOptions.gtlAvailable,
+					disableLocalTimeline: !basePolicies.ltlAvailable,
+					disableGlobalTimeline: !basePolicies.gtlAvailable,
 					emailRequiredForSignup: meta.emailRequiredForSignup,
 					enableHcaptcha: meta.enableHcaptcha,
 					enableRecaptcha: meta.enableRecaptcha,
diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts
index dcc9342a82..395a1c468a 100644
--- a/packages/backend/src/server/api/ApiCallService.ts
+++ b/packages/backend/src/server/api/ApiCallService.ts
@@ -225,7 +225,7 @@ export class ApiCallService implements OnApplicationShutdown {
 			}
 
 			// TODO: 毎リクエスト計算するのもあれだしキャッシュしたい
-			const factor = user ? (await this.roleService.getUserRoleOptions(user.id)).rateLimitFactor : 1;
+			const factor = user ? (await this.roleService.getUserPolicies(user.id)).rateLimitFactor : 1;
 
 			// Rate limit
 			await this.rateLimiterService.limit(limit as IEndpointMeta['limit'] & { key: NonNullable<string> }, limitActor, factor).catch(err => {
@@ -274,9 +274,9 @@ export class ApiCallService implements OnApplicationShutdown {
 			}
 		}
 
-		if (ep.meta.requireRoleOption != null && !user!.isRoot) {
-			const myRole = await this.roleService.getUserRoleOptions(user!.id);
-			if (!myRole[ep.meta.requireRoleOption]) {
+		if (ep.meta.requireRolePolicy != null && !user!.isRoot) {
+			const policies = await this.roleService.getUserPolicies(user!.id);
+			if (!policies[ep.meta.requireRolePolicy]) {
 				throw new ApiError({
 					message: 'You are not assigned to a required role.',
 					code: 'ROLE_PERMISSION_DENIED',
diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts
index aa88a9dd13..14927da7d6 100644
--- a/packages/backend/src/server/api/EndpointsModule.ts
+++ b/packages/backend/src/server/api/EndpointsModule.ts
@@ -65,7 +65,7 @@ import * as ep___admin_roles_show from './endpoints/admin/roles/show.js';
 import * as ep___admin_roles_update from './endpoints/admin/roles/update.js';
 import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js';
 import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js';
-import * as ep___admin_roles_updateDefaultRoleOverride from './endpoints/admin/roles/update-default-role-override.js';
+import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js';
 import * as ep___announcements from './endpoints/announcements.js';
 import * as ep___antennas_create from './endpoints/antennas/create.js';
 import * as ep___antennas_delete from './endpoints/antennas/delete.js';
@@ -399,7 +399,7 @@ const $admin_roles_show: Provider = { provide: 'ep:admin/roles/show', useClass:
 const $admin_roles_update: Provider = { provide: 'ep:admin/roles/update', useClass: ep___admin_roles_update.default };
 const $admin_roles_assign: Provider = { provide: 'ep:admin/roles/assign', useClass: ep___admin_roles_assign.default };
 const $admin_roles_unassign: Provider = { provide: 'ep:admin/roles/unassign', useClass: ep___admin_roles_unassign.default };
-const $admin_roles_updateDefaultRoleOverride: Provider = { provide: 'ep:admin/roles/update-default-role-override', useClass: ep___admin_roles_updateDefaultRoleOverride.default };
+const $admin_roles_updateDefaultPolicies: Provider = { provide: 'ep:admin/roles/update-default-policies', useClass: ep___admin_roles_updateDefaultPolicies.default };
 const $announcements: Provider = { provide: 'ep:announcements', useClass: ep___announcements.default };
 const $antennas_create: Provider = { provide: 'ep:antennas/create', useClass: ep___antennas_create.default };
 const $antennas_delete: Provider = { provide: 'ep:antennas/delete', useClass: ep___antennas_delete.default };
@@ -737,7 +737,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
 		$admin_roles_update,
 		$admin_roles_assign,
 		$admin_roles_unassign,
-		$admin_roles_updateDefaultRoleOverride,
+		$admin_roles_updateDefaultPolicies,
 		$announcements,
 		$antennas_create,
 		$antennas_delete,
@@ -1069,7 +1069,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
 		$admin_roles_update,
 		$admin_roles_assign,
 		$admin_roles_unassign,
-		$admin_roles_updateDefaultRoleOverride,
+		$admin_roles_updateDefaultPolicies,
 		$announcements,
 		$antennas_create,
 		$antennas_delete,
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index f50a3b5dd2..54c4206ea4 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -64,7 +64,7 @@ import * as ep___admin_roles_show from './endpoints/admin/roles/show.js';
 import * as ep___admin_roles_update from './endpoints/admin/roles/update.js';
 import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js';
 import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js';
-import * as ep___admin_roles_updateDefaultRoleOverride from './endpoints/admin/roles/update-default-role-override.js';
+import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js';
 import * as ep___announcements from './endpoints/announcements.js';
 import * as ep___antennas_create from './endpoints/antennas/create.js';
 import * as ep___antennas_delete from './endpoints/antennas/delete.js';
@@ -396,7 +396,7 @@ const eps = [
 	['admin/roles/update', ep___admin_roles_update],
 	['admin/roles/assign', ep___admin_roles_assign],
 	['admin/roles/unassign', ep___admin_roles_unassign],
-	['admin/roles/update-default-role-override', ep___admin_roles_updateDefaultRoleOverride],
+	['admin/roles/update-default-policies', ep___admin_roles_updateDefaultPolicies],
 	['announcements', ep___announcements],
 	['antennas/create', ep___antennas_create],
 	['antennas/delete', ep___antennas_delete],
@@ -695,7 +695,7 @@ export interface IEndpointMeta {
 	 */
 	readonly requireAdmin?: boolean;
 
-	readonly requireRoleOption?: string;
+	readonly requireRolePolicy?: string;
 
 	/**
 	 * エンドポイントのリミテーションに関するやつ
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts
index d114fd3d55..9b6c774f0c 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts
@@ -8,7 +8,7 @@ export const meta = {
 	tags: ['admin'],
 
 	requireCredential: true,
-	requireRoleOption: 'canManageCustomEmojis',
+	requireRolePolicy: 'canManageCustomEmojis',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
index 52ccb74447..abca1d169d 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
@@ -14,7 +14,7 @@ export const meta = {
 	tags: ['admin'],
 
 	requireCredential: true,
-	requireRoleOption: 'canManageCustomEmojis',
+	requireRolePolicy: 'canManageCustomEmojis',
 
 	errors: {
 		noSuchFile: {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
index 4d1fdd989d..b4fc7fd6f5 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
@@ -14,7 +14,7 @@ export const meta = {
 	tags: ['admin'],
 
 	requireCredential: true,
-	requireRoleOption: 'canManageCustomEmojis',
+	requireRolePolicy: 'canManageCustomEmojis',
 
 	errors: {
 		noSuchEmoji: {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts
index 27aa4fb1b1..ae45105b28 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts
@@ -9,7 +9,7 @@ export const meta = {
 	tags: ['admin'],
 
 	requireCredential: true,
-	requireRoleOption: 'canManageCustomEmojis',
+	requireRolePolicy: 'canManageCustomEmojis',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
index 2531246569..e237d87d34 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
@@ -10,7 +10,7 @@ export const meta = {
 	tags: ['admin'],
 
 	requireCredential: true,
-	requireRoleOption: 'canManageCustomEmojis',
+	requireRolePolicy: 'canManageCustomEmojis',
 
 	errors: {
 		noSuchEmoji: {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts
index 15f468c180..b4a07324bb 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts
@@ -5,7 +5,7 @@ import { QueueService } from '@/core/QueueService.js';
 export const meta = {
 	secure: true,
 	requireCredential: true,
-	requireRoleOption: 'canManageCustomEmojis',
+	requireRolePolicy: 'canManageCustomEmojis',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts
index 131c9ef223..d9ce97194a 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts
@@ -11,7 +11,7 @@ export const meta = {
 	tags: ['admin'],
 
 	requireCredential: true,
-	requireRoleOption: 'canManageCustomEmojis',
+	requireRolePolicy: 'canManageCustomEmojis',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
index ef2bc936c3..1a6096f36f 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
@@ -11,7 +11,7 @@ export const meta = {
 	tags: ['admin'],
 
 	requireCredential: true,
-	requireRoleOption: 'canManageCustomEmojis',
+	requireRolePolicy: 'canManageCustomEmojis',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts
index a70cd8d787..5fc9e024bf 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts
@@ -8,7 +8,7 @@ export const meta = {
 	tags: ['admin'],
 
 	requireCredential: true,
-	requireRoleOption: 'canManageCustomEmojis',
+	requireRolePolicy: 'canManageCustomEmojis',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts
index b33e5662bb..8b5ba8fbf4 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts
@@ -8,7 +8,7 @@ export const meta = {
 	tags: ['admin'],
 
 	requireCredential: true,
-	requireRoleOption: 'canManageCustomEmojis',
+	requireRolePolicy: 'canManageCustomEmojis',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts
index 05834bc572..827b5ace7a 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts
@@ -8,7 +8,7 @@ export const meta = {
 	tags: ['admin'],
 
 	requireCredential: true,
-	requireRoleOption: 'canManageCustomEmojis',
+	requireRolePolicy: 'canManageCustomEmojis',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
index 19645cb515..fb0ef12878 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
@@ -9,7 +9,7 @@ export const meta = {
 	tags: ['admin'],
 
 	requireCredential: true,
-	requireRoleOption: 'canManageCustomEmojis',
+	requireRolePolicy: 'canManageCustomEmojis',
 
 	errors: {
 		noSuchEmoji: {
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index fd08a5f847..b393827054 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -4,7 +4,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
 import { MetaService } from '@/core/MetaService.js';
 import type { Config } from '@/config.js';
 import { DI } from '@/di-symbols.js';
-import { DEFAULT_ROLE } from '@/core/RoleService.js';
+import { DEFAULT_POLICIES } from '@/core/RoleService.js';
 
 export const meta = {
 	tags: ['meta'],
@@ -440,7 +440,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				deeplIsPro: instance.deeplIsPro,
 				enableIpLogging: instance.enableIpLogging,
 				enableActiveEmailValidation: instance.enableActiveEmailValidation,
-				baseRole: { ...DEFAULT_ROLE, ...instance.defaultRoleOverride },
+				policies: { ...DEFAULT_POLICIES, ...instance.policies },
 			};
 		});
 	}
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/create.ts b/packages/backend/src/server/api/endpoints/admin/roles/create.ts
index a9216a6386..f136c6d624 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/create.ts
@@ -25,7 +25,7 @@ export const paramDef = {
 		isModerator: { type: 'boolean' },
 		isAdministrator: { type: 'boolean' },
 		canEditMembersByModerator: { type: 'boolean' },
-		options: {
+		policies: {
 			type: 'object',
 		},
 	},
@@ -39,7 +39,7 @@ export const paramDef = {
 		'isModerator',
 		'isAdministrator',
 		'canEditMembersByModerator',
-		'options',
+		'policies',
 	],
 } as const;
 
@@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				isAdministrator: ps.isAdministrator,
 				isModerator: ps.isModerator,
 				canEditMembersByModerator: ps.canEditMembersByModerator,
-				options: ps.options,
+				policies: ps.policies,
 			}).then(x => this.rolesRepository.findOneByOrFail(x.identifiers[0]));
 	
 			this.globalEventService.publishInternalEvent('roleCreated', created);
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts
similarity index 85%
rename from packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts
rename to packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts
index 35da04efd2..6006816bcb 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts
@@ -16,12 +16,12 @@ export const meta = {
 export const paramDef = {
 	type: 'object',
 	properties: {
-		options: {
+		policies: {
 			type: 'object',
 		},
 	},
 	required: [
-		'options',
+		'policies',
 	],
 } as const;
 
@@ -34,9 +34,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 	) {
 		super(meta, paramDef, async (ps) => {
 			await this.metaService.update({
-				defaultRoleOverride: ps.options,
+				policies: ps.policies,
 			});
-			this.globalEventService.publishInternalEvent('defaultRoleOverrideUpdated', ps.options);
+			this.globalEventService.publishInternalEvent('policiesUpdated', ps.policies);
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
index 4ca5124eda..fc4c3d8f11 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
@@ -33,7 +33,7 @@ export const paramDef = {
 		isModerator: { type: 'boolean' },
 		isAdministrator: { type: 'boolean' },
 		canEditMembersByModerator: { type: 'boolean' },
-		options: {
+		policies: {
 			type: 'object',
 		},
 	},
@@ -48,7 +48,7 @@ export const paramDef = {
 		'isModerator',
 		'isAdministrator',
 		'canEditMembersByModerator',
-		'options',
+		'policies',
 	],
 } as const;
 
@@ -79,7 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				isModerator: ps.isModerator,
 				isAdministrator: ps.isAdministrator,
 				canEditMembersByModerator: ps.canEditMembersByModerator,
-				options: ps.options,
+				policies: ps.policies,
 			});
 			const updated = await this.rolesRepository.findOneByOrFail({ id: ps.roleId });
 			this.globalEventService.publishInternalEvent('roleUpdated', updated);
diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts
index 3f4ec299af..94603cc91a 100644
--- a/packages/backend/src/server/api/endpoints/admin/show-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts
@@ -52,7 +52,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			}
 
 			const isModerator = await this.roleService.isModerator(user);
-			const isSilenced = !(await this.roleService.getUserRoleOptions(user.id)).canPublicNote;
+			const isSilenced = !(await this.roleService.getUserPolicies(user.id)).canPublicNote;
 
 			const _me = await this.usersRepository.findOneByOrFail({ id: me.id });
 			if (!await this.roleService.isAdministrator(_me) && await this.roleService.isAdministrator(user)) {
@@ -94,6 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				lastActiveDate: user.lastActiveDate,
 				moderationNote: profile.moderationNote,
 				signins,
+				policies: await this.roleService.getUserPolicies(user.id),
 				roles: await this.roleEntityService.packMany(roles, me, { detail: false }),
 			};
 		});
diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts
index 08625250c8..a1553b6a80 100644
--- a/packages/backend/src/server/api/endpoints/antennas/create.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/create.ts
@@ -92,7 +92,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			const currentAntennasCount = await this.antennasRepository.countBy({
 				userId: me.id,
 			});
-			if (currentAntennasCount > (await this.roleService.getUserRoleOptions(me.id)).antennaLimit) {
+			if (currentAntennasCount > (await this.roleService.getUserPolicies(me.id)).antennaLimit) {
 				throw new ApiError(meta.errors.tooManyAntennas);
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts
index 3cf096c242..f3f9c3477f 100644
--- a/packages/backend/src/server/api/endpoints/clips/add-note.ts
+++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts
@@ -97,7 +97,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			const currentCount = await this.clipNotesRepository.countBy({
 				clipId: clip.id,
 			});
-			if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).noteEachClipsLimit) {
+			if (currentCount > (await this.roleService.getUserPolicies(me.id)).noteEachClipsLimit) {
 				throw new ApiError(meta.errors.tooManyClipNotes);
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts
index abc0288c89..c095de702c 100644
--- a/packages/backend/src/server/api/endpoints/clips/create.ts
+++ b/packages/backend/src/server/api/endpoints/clips/create.ts
@@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			const currentCount = await this.clipsRepository.countBy({
 				userId: me.id,
 			});
-			if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).clipLimit) {
+			if (currentCount > (await this.roleService.getUserPolicies(me.id)).clipLimit) {
 				throw new ApiError(meta.errors.tooManyClips);
 			}
 	
diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts
index 2a06792dcf..e5bbfecbcf 100644
--- a/packages/backend/src/server/api/endpoints/drive.ts
+++ b/packages/backend/src/server/api/endpoints/drive.ts
@@ -47,10 +47,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			// Calculate drive usage
 			const usage = await this.driveFileEntityService.calcDriveUsageOf(me.id);
 
-			const myRole = await this.roleService.getUserRoleOptions(me.id);
+			const policies = await this.roleService.getUserPolicies(me.id);
 
 			return {
-				capacity: 1024 * 1024 * myRole.driveCapacityMb,
+				capacity: 1024 * 1024 * policies.driveCapacityMb,
 				usage: usage,
 			};
 		});
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index fe09eca674..b1eaab3908 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -173,7 +173,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			if (ps.mutedWords !== undefined) {
 				// TODO: ちゃんと数える
 				const length = JSON.stringify(ps.mutedWords).length;
-				if (length > (await this.roleService.getUserRoleOptions(user.id)).wordMuteLimit) {
+				if (length > (await this.roleService.getUserPolicies(user.id)).wordMuteLimit) {
 					throw new ApiError(meta.errors.tooManyMutedWords);
 				}
 
diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts
index 3d89b77a7b..51fcce6cf0 100644
--- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts
+++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts
@@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			const currentWebhooksCount = await this.webhooksRepository.countBy({
 				userId: me.id,
 			});
-			if (currentWebhooksCount > (await this.roleService.getUserRoleOptions(me.id)).webhookLimit) {
+			if (currentWebhooksCount > (await this.roleService.getUserPolicies(me.id)).webhookLimit) {
 				throw new ApiError(meta.errors.tooManyWebhooks);
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/invite.ts b/packages/backend/src/server/api/endpoints/invite.ts
index 9b03cf4bb6..5d2c479e79 100644
--- a/packages/backend/src/server/api/endpoints/invite.ts
+++ b/packages/backend/src/server/api/endpoints/invite.ts
@@ -9,7 +9,7 @@ export const meta = {
 	tags: ['meta'],
 
 	requireCredential: true,
-	requireRoleOption: 'canInvite',
+	requireRolePolicy: 'canInvite',
 
 	res: {
 		type: 'object',
diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts
index f46a32dfe7..89fa503173 100644
--- a/packages/backend/src/server/api/endpoints/meta.ts
+++ b/packages/backend/src/server/api/endpoints/meta.ts
@@ -7,7 +7,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { MetaService } from '@/core/MetaService.js';
 import type { Config } from '@/config.js';
 import { DI } from '@/di-symbols.js';
-import { DEFAULT_ROLE } from '@/core/RoleService.js';
+import { DEFAULT_POLICIES } from '@/core/RoleService.js';
 
 export const meta = {
 	tags: ['meta'],
@@ -334,7 +334,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 
 				translatorAvailable: instance.deeplAuthKey != null,
 
-				baseRole: { ...DEFAULT_ROLE, ...instance.defaultRoleOverride },
+				policies: { ...DEFAULT_POLICIES, ...instance.policies },
 
 				...(ps.detail ? {
 					pinnedPages: instance.pinnedPages,
diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
index 081563493d..5d0cdc3fca 100644
--- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
@@ -62,8 +62,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 		private activeUsersChart: ActiveUsersChart,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			const role = await this.roleService.getUserRoleOptions(me ? me.id : null);
-			if (!role.gtlAvailable) {
+			const policies = await this.roleService.getUserPolicies(me ? me.id : null);
+			if (!policies.gtlAvailable) {
 				throw new ApiError(meta.errors.gtlDisabled);
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
index b2c504448e..2819abb125 100644
--- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -71,8 +71,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 		private activeUsersChart: ActiveUsersChart,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			const role = await this.roleService.getUserRoleOptions(me.id);
-			if (!role.ltlAvailable) {
+			const policies = await this.roleService.getUserPolicies(me.id);
+			if (!policies.ltlAvailable) {
 				throw new ApiError(meta.errors.stlDisabled);
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
index 6361edc310..f396f7e584 100644
--- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
@@ -67,8 +67,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 		private activeUsersChart: ActiveUsersChart,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			const role = await this.roleService.getUserRoleOptions(me ? me.id : null);
-			if (!role.ltlAvailable) {
+			const policies = await this.roleService.getUserPolicies(me ? me.id : null);
+			if (!policies.ltlAvailable) {
 				throw new ApiError(meta.errors.ltlDisabled);
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts
index 22e5e3ce78..a840c1a04e 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/create.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts
@@ -55,7 +55,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			const currentCount = await this.userListsRepository.countBy({
 				userId: me.id,
 			});
-			if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).userListLimit) {
+			if (currentCount > (await this.roleService.getUserPolicies(me.id)).userListLimit) {
 				throw new ApiError(meta.errors.tooManyUserLists);
 			}
 	
diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts
index 185c813869..43d8907fc9 100644
--- a/packages/backend/src/server/api/stream/channels/global-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts
@@ -29,8 +29,8 @@ class GlobalTimelineChannel extends Channel {
 
 	@bindThis
 	public async init(params: any) {
-		const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null);
-		if (!role.gtlAvailable) return;
+		const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
+		if (!policies.gtlAvailable) return;
 
 		// Subscribe events
 		this.subscriber.on('notesStream', this.onNote);
diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
index a0f75f202c..340f677815 100644
--- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
@@ -30,8 +30,8 @@ class HybridTimelineChannel extends Channel {
 
 	@bindThis
 	public async init(params: any): Promise<void> {
-		const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null);
-		if (!role.ltlAvailable) return;
+		const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
+		if (!policies.ltlAvailable) return;
 
 		// Subscribe events
 		this.subscriber.on('notesStream', this.onNote);
diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts
index 7d76f42fe7..ea29e30d63 100644
--- a/packages/backend/src/server/api/stream/channels/local-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts
@@ -28,8 +28,8 @@ class LocalTimelineChannel extends Channel {
 
 	@bindThis
 	public async init(params: any) {
-		const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null);
-		if (!role.ltlAvailable) return;
+		const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
+		if (!policies.ltlAvailable) return;
 
 		// Subscribe events
 		this.subscriber.on('notesStream', this.onNote);
diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts
index 03837baefb..a442529bb3 100644
--- a/packages/backend/src/server/api/stream/types.ts
+++ b/packages/backend/src/server/api/stream/types.ts
@@ -30,7 +30,7 @@ export interface InternalStreamTypes {
 	remoteUserUpdated: Serialized<{ id: User['id']; }>;
 	follow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>;
 	unfollow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>;
-	defaultRoleOverrideUpdated: Serialized<Role['options']>;
+	policiesUpdated: Serialized<Role['options']>;
 	roleCreated: Serialized<Role>;
 	roleDeleted: Serialized<Role>;
 	roleUpdated: Serialized<Role>;
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index 0f67cec0b2..d0be0a8c39 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -36,20 +36,20 @@
 	</MkFolder>
 
 	<FormSlot>
-		<template #label>{{ i18n.ts._role.options }}</template>
+		<template #label>{{ i18n.ts._role.policies }}</template>
 		<div class="_gaps_s">
 			<MkFolder>
 				<template #label>{{ i18n.ts._role._options.rateLimitFactor }}</template>
-				<template #suffix>{{ options.rateLimitFactor.useDefault ? i18n.ts._role.useBaseValue : `${Math.floor(options.rateLimitFactor.value * 100)}%` }} <i :class="getPriorityIcon(options.rateLimitFactor)"></i></template>
+				<template #suffix>{{ policies.rateLimitFactor.useDefault ? i18n.ts._role.useBaseValue : `${Math.floor(policies.rateLimitFactor.value * 100)}%` }} <i :class="getPriorityIcon(policies.rateLimitFactor)"></i></template>
 				<div class="_gaps">
-					<MkSwitch v-model="options.rateLimitFactor.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.rateLimitFactor.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 					</MkSwitch>
-					<MkRange :model-value="options.rateLimitFactor.value * 100" :min="30" :max="300" :step="10" :text-converter="(v) => `${v}%`" @update:model-value="v => options.rateLimitFactor.value = (v / 100)">
+					<MkRange :model-value="policies.rateLimitFactor.value * 100" :min="30" :max="300" :step="10" :text-converter="(v) => `${v}%`" @update:model-value="v => policies.rateLimitFactor.value = (v / 100)">
 						<template #label>{{ i18n.ts._role._options.rateLimitFactor }}</template>
 						<template #caption>{{ i18n.ts._role._options.descriptionOfRateLimitFactor }}</template>
 					</MkRange>
-					<MkRange v-model="options.rateLimitFactor.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+					<MkRange v-model="policies.rateLimitFactor.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
 						<template #label>{{ i18n.ts._role.priority }}</template>
 					</MkRange>
 				</div>
@@ -57,15 +57,15 @@
 
 			<MkFolder>
 				<template #label>{{ i18n.ts._role._options.gtlAvailable }}</template>
-				<template #suffix>{{ options.gtlAvailable.useDefault ? i18n.ts._role.useBaseValue : (options.gtlAvailable.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(options.gtlAvailable)"></i></template>
+				<template #suffix>{{ policies.gtlAvailable.useDefault ? i18n.ts._role.useBaseValue : (policies.gtlAvailable.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(policies.gtlAvailable)"></i></template>
 				<div class="_gaps">
-					<MkSwitch v-model="options.gtlAvailable.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.gtlAvailable.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 					</MkSwitch>
-					<MkSwitch v-model="options.gtlAvailable.value" :disabled="options.gtlAvailable.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.gtlAvailable.value" :disabled="policies.gtlAvailable.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts.enable }}</template>
 					</MkSwitch>
-					<MkRange v-model="options.gtlAvailable.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+					<MkRange v-model="policies.gtlAvailable.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
 						<template #label>{{ i18n.ts._role.priority }}</template>
 					</MkRange>
 				</div>
@@ -73,15 +73,15 @@
 
 			<MkFolder>
 				<template #label>{{ i18n.ts._role._options.ltlAvailable }}</template>
-				<template #suffix>{{ options.ltlAvailable.useDefault ? i18n.ts._role.useBaseValue : (options.ltlAvailable.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(options.ltlAvailable)"></i></template>
+				<template #suffix>{{ policies.ltlAvailable.useDefault ? i18n.ts._role.useBaseValue : (policies.ltlAvailable.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(policies.ltlAvailable)"></i></template>
 				<div class="_gaps">
-					<MkSwitch v-model="options.ltlAvailable.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.ltlAvailable.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 					</MkSwitch>
-					<MkSwitch v-model="options.ltlAvailable.value" :disabled="options.ltlAvailable.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.ltlAvailable.value" :disabled="policies.ltlAvailable.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts.enable }}</template>
 					</MkSwitch>
-					<MkRange v-model="options.ltlAvailable.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+					<MkRange v-model="policies.ltlAvailable.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
 						<template #label>{{ i18n.ts._role.priority }}</template>
 					</MkRange>
 				</div>
@@ -89,15 +89,15 @@
 
 			<MkFolder>
 				<template #label>{{ i18n.ts._role._options.canPublicNote }}</template>
-				<template #suffix>{{ options.canPublicNote.useDefault ? i18n.ts._role.useBaseValue : (options.canPublicNote.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(options.canPublicNote)"></i></template>
+				<template #suffix>{{ policies.canPublicNote.useDefault ? i18n.ts._role.useBaseValue : (policies.canPublicNote.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(policies.canPublicNote)"></i></template>
 				<div class="_gaps">
-					<MkSwitch v-model="options.canPublicNote.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.canPublicNote.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 					</MkSwitch>
-					<MkSwitch v-model="options.canPublicNote.value" :disabled="options.canPublicNote.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.canPublicNote.value" :disabled="policies.canPublicNote.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts.enable }}</template>
 					</MkSwitch>
-					<MkRange v-model="options.canPublicNote.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+					<MkRange v-model="policies.canPublicNote.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
 						<template #label>{{ i18n.ts._role.priority }}</template>
 					</MkRange>
 				</div>
@@ -105,15 +105,15 @@
 
 			<MkFolder>
 				<template #label>{{ i18n.ts._role._options.canInvite }}</template>
-				<template #suffix>{{ options.canInvite.useDefault ? i18n.ts._role.useBaseValue : (options.canInvite.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(options.canInvite)"></i></template>
+				<template #suffix>{{ policies.canInvite.useDefault ? i18n.ts._role.useBaseValue : (policies.canInvite.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(policies.canInvite)"></i></template>
 				<div class="_gaps">
-					<MkSwitch v-model="options.canInvite.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.canInvite.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 					</MkSwitch>
-					<MkSwitch v-model="options.canInvite.value" :disabled="options.canInvite.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.canInvite.value" :disabled="policies.canInvite.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts.enable }}</template>
 					</MkSwitch>
-					<MkRange v-model="options.canInvite.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+					<MkRange v-model="policies.canInvite.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
 						<template #label>{{ i18n.ts._role.priority }}</template>
 					</MkRange>
 				</div>
@@ -121,15 +121,15 @@
 
 			<MkFolder>
 				<template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template>
-				<template #suffix>{{ options.canManageCustomEmojis.useDefault ? i18n.ts._role.useBaseValue : (options.canManageCustomEmojis.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(options.canManageCustomEmojis)"></i></template>
+				<template #suffix>{{ policies.canManageCustomEmojis.useDefault ? i18n.ts._role.useBaseValue : (policies.canManageCustomEmojis.value ? i18n.ts.yes : i18n.ts.no) }} <i :class="getPriorityIcon(policies.canManageCustomEmojis)"></i></template>
 				<div class="_gaps">
-					<MkSwitch v-model="options.canManageCustomEmojis.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.canManageCustomEmojis.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 					</MkSwitch>
-					<MkSwitch v-model="options.canManageCustomEmojis.value" :disabled="options.canManageCustomEmojis.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.canManageCustomEmojis.value" :disabled="policies.canManageCustomEmojis.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts.enable }}</template>
 					</MkSwitch>
-					<MkRange v-model="options.canManageCustomEmojis.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+					<MkRange v-model="policies.canManageCustomEmojis.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
 						<template #label>{{ i18n.ts._role.priority }}</template>
 					</MkRange>
 				</div>
@@ -137,15 +137,15 @@
 
 			<MkFolder>
 				<template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
-				<template #suffix>{{ options.driveCapacityMb.useDefault ? i18n.ts._role.useBaseValue : (options.driveCapacityMb.value + 'MB') }} <i :class="getPriorityIcon(options.driveCapacityMb)"></i></template>
+				<template #suffix>{{ policies.driveCapacityMb.useDefault ? i18n.ts._role.useBaseValue : (policies.driveCapacityMb.value + 'MB') }} <i :class="getPriorityIcon(policies.driveCapacityMb)"></i></template>
 				<div class="_gaps">
-					<MkSwitch v-model="options.driveCapacityMb.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.driveCapacityMb.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 					</MkSwitch>
-					<MkInput v-model="options.driveCapacityMb.value" :disabled="options.driveCapacityMb.useDefault" type="number" :readonly="readonly">
+					<MkInput v-model="policies.driveCapacityMb.value" :disabled="policies.driveCapacityMb.useDefault" type="number" :readonly="readonly">
 						<template #suffix>MB</template>
 					</MkInput>
-					<MkRange v-model="options.driveCapacityMb.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+					<MkRange v-model="policies.driveCapacityMb.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
 						<template #label>{{ i18n.ts._role.priority }}</template>
 					</MkRange>
 				</div>
@@ -153,14 +153,14 @@
 
 			<MkFolder>
 				<template #label>{{ i18n.ts._role._options.pinMax }}</template>
-				<template #suffix>{{ options.pinLimit.useDefault ? i18n.ts._role.useBaseValue : (options.pinLimit.value) }} <i :class="getPriorityIcon(options.pinLimit)"></i></template>
+				<template #suffix>{{ policies.pinLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.pinLimit.value) }} <i :class="getPriorityIcon(policies.pinLimit)"></i></template>
 				<div class="_gaps">
-					<MkSwitch v-model="options.pinLimit.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.pinLimit.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 					</MkSwitch>
-					<MkInput v-model="options.pinLimit.value" :disabled="options.pinLimit.useDefault" type="number" :readonly="readonly">
+					<MkInput v-model="policies.pinLimit.value" :disabled="policies.pinLimit.useDefault" type="number" :readonly="readonly">
 					</MkInput>
-					<MkRange v-model="options.pinLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+					<MkRange v-model="policies.pinLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
 						<template #label>{{ i18n.ts._role.priority }}</template>
 					</MkRange>
 				</div>
@@ -168,14 +168,14 @@
 
 			<MkFolder>
 				<template #label>{{ i18n.ts._role._options.antennaMax }}</template>
-				<template #suffix>{{ options.antennaLimit.useDefault ? i18n.ts._role.useBaseValue : (options.antennaLimit.value) }} <i :class="getPriorityIcon(options.antennaLimit)"></i></template>
+				<template #suffix>{{ policies.antennaLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.antennaLimit.value) }} <i :class="getPriorityIcon(policies.antennaLimit)"></i></template>
 				<div class="_gaps">
-					<MkSwitch v-model="options.antennaLimit.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.antennaLimit.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 					</MkSwitch>
-					<MkInput v-model="options.antennaLimit.value" :disabled="options.antennaLimit.useDefault" type="number" :readonly="readonly">
+					<MkInput v-model="policies.antennaLimit.value" :disabled="policies.antennaLimit.useDefault" type="number" :readonly="readonly">
 					</MkInput>
-					<MkRange v-model="options.antennaLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+					<MkRange v-model="policies.antennaLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
 						<template #label>{{ i18n.ts._role.priority }}</template>
 					</MkRange>
 				</div>
@@ -183,15 +183,15 @@
 
 			<MkFolder>
 				<template #label>{{ i18n.ts._role._options.wordMuteMax }}</template>
-				<template #suffix>{{ options.wordMuteLimit.useDefault ? i18n.ts._role.useBaseValue : (options.wordMuteLimit.value) }} <i :class="getPriorityIcon(options.wordMuteLimit)"></i></template>
+				<template #suffix>{{ policies.wordMuteLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.wordMuteLimit.value) }} <i :class="getPriorityIcon(policies.wordMuteLimit)"></i></template>
 				<div class="_gaps">
-					<MkSwitch v-model="options.wordMuteLimit.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.wordMuteLimit.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 					</MkSwitch>
-					<MkInput v-model="options.wordMuteLimit.value" :disabled="options.wordMuteLimit.useDefault" type="number" :readonly="readonly">
+					<MkInput v-model="policies.wordMuteLimit.value" :disabled="policies.wordMuteLimit.useDefault" type="number" :readonly="readonly">
 						<template #suffix>chars</template>
 					</MkInput>
-					<MkRange v-model="options.wordMuteLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+					<MkRange v-model="policies.wordMuteLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
 						<template #label>{{ i18n.ts._role.priority }}</template>
 					</MkRange>
 				</div>
@@ -199,14 +199,14 @@
 
 			<MkFolder>
 				<template #label>{{ i18n.ts._role._options.webhookMax }}</template>
-				<template #suffix>{{ options.webhookLimit.useDefault ? i18n.ts._role.useBaseValue : (options.webhookLimit.value) }} <i :class="getPriorityIcon(options.webhookLimit)"></i></template>
+				<template #suffix>{{ policies.webhookLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.webhookLimit.value) }} <i :class="getPriorityIcon(policies.webhookLimit)"></i></template>
 				<div class="_gaps">
-					<MkSwitch v-model="options.webhookLimit.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.webhookLimit.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 					</MkSwitch>
-					<MkInput v-model="options.webhookLimit.value" :disabled="options.webhookLimit.useDefault" type="number" :readonly="readonly">
+					<MkInput v-model="policies.webhookLimit.value" :disabled="policies.webhookLimit.useDefault" type="number" :readonly="readonly">
 					</MkInput>
-					<MkRange v-model="options.webhookLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+					<MkRange v-model="policies.webhookLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
 						<template #label>{{ i18n.ts._role.priority }}</template>
 					</MkRange>
 				</div>
@@ -214,14 +214,14 @@
 
 			<MkFolder>
 				<template #label>{{ i18n.ts._role._options.clipMax }}</template>
-				<template #suffix>{{ options.clipLimit.useDefault ? i18n.ts._role.useBaseValue : (options.clipLimit.value) }} <i :class="getPriorityIcon(options.clipLimit)"></i></template>
+				<template #suffix>{{ policies.clipLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.clipLimit.value) }} <i :class="getPriorityIcon(policies.clipLimit)"></i></template>
 				<div class="_gaps">
-					<MkSwitch v-model="options.clipLimit.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.clipLimit.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 					</MkSwitch>
-					<MkInput v-model="options.clipLimit.value" :disabled="options.clipLimit.useDefault" type="number" :readonly="readonly">
+					<MkInput v-model="policies.clipLimit.value" :disabled="policies.clipLimit.useDefault" type="number" :readonly="readonly">
 					</MkInput>
-					<MkRange v-model="options.clipLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+					<MkRange v-model="policies.clipLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
 						<template #label>{{ i18n.ts._role.priority }}</template>
 					</MkRange>
 				</div>
@@ -229,14 +229,14 @@
 
 			<MkFolder>
 				<template #label>{{ i18n.ts._role._options.noteEachClipsMax }}</template>
-				<template #suffix>{{ options.noteEachClipsLimit.useDefault ? i18n.ts._role.useBaseValue : (options.noteEachClipsLimit.value) }} <i :class="getPriorityIcon(options.noteEachClipsLimit)"></i></template>
+				<template #suffix>{{ policies.noteEachClipsLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.noteEachClipsLimit.value) }} <i :class="getPriorityIcon(policies.noteEachClipsLimit)"></i></template>
 				<div class="_gaps">
-					<MkSwitch v-model="options.noteEachClipsLimit.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.noteEachClipsLimit.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 					</MkSwitch>
-					<MkInput v-model="options.noteEachClipsLimit.value" :disabled="options.noteEachClipsLimit.useDefault" type="number" :readonly="readonly">
+					<MkInput v-model="policies.noteEachClipsLimit.value" :disabled="policies.noteEachClipsLimit.useDefault" type="number" :readonly="readonly">
 					</MkInput>
-					<MkRange v-model="options.noteEachClipsLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+					<MkRange v-model="policies.noteEachClipsLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
 						<template #label>{{ i18n.ts._role.priority }}</template>
 					</MkRange>
 				</div>
@@ -244,14 +244,14 @@
 
 			<MkFolder>
 				<template #label>{{ i18n.ts._role._options.userListMax }}</template>
-				<template #suffix>{{ options.userListLimit.useDefault ? i18n.ts._role.useBaseValue : (options.userListLimit.value) }} <i :class="getPriorityIcon(options.userListLimit)"></i></template>
+				<template #suffix>{{ policies.userListLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.userListLimit.value) }} <i :class="getPriorityIcon(policies.userListLimit)"></i></template>
 				<div class="_gaps">
-					<MkSwitch v-model="options.userListLimit.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.userListLimit.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 					</MkSwitch>
-					<MkInput v-model="options.userListLimit.value" :disabled="options.userListLimit.useDefault" type="number" :readonly="readonly">
+					<MkInput v-model="policies.userListLimit.value" :disabled="policies.userListLimit.useDefault" type="number" :readonly="readonly">
 					</MkInput>
-					<MkRange v-model="options.userListLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+					<MkRange v-model="policies.userListLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
 						<template #label>{{ i18n.ts._role.priority }}</template>
 					</MkRange>
 				</div>
@@ -259,14 +259,14 @@
 
 			<MkFolder>
 				<template #label>{{ i18n.ts._role._options.userEachUserListsMax }}</template>
-				<template #suffix>{{ options.userEachUserListsLimit.useDefault ? i18n.ts._role.useBaseValue : (options.userEachUserListsLimit.value) }} <i :class="getPriorityIcon(options.userEachUserListsLimit)"></i></template>
+				<template #suffix>{{ policies.userEachUserListsLimit.useDefault ? i18n.ts._role.useBaseValue : (policies.userEachUserListsLimit.value) }} <i :class="getPriorityIcon(policies.userEachUserListsLimit)"></i></template>
 				<div class="_gaps">
-					<MkSwitch v-model="options.userEachUserListsLimit.useDefault" :readonly="readonly">
+					<MkSwitch v-model="policies.userEachUserListsLimit.useDefault" :readonly="readonly">
 						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
 					</MkSwitch>
-					<MkInput v-model="options.userEachUserListsLimit.value" :disabled="options.userEachUserListsLimit.useDefault" type="number" :readonly="readonly">
+					<MkInput v-model="policies.userEachUserListsLimit.value" :disabled="policies.userEachUserListsLimit.useDefault" type="number" :readonly="readonly">
 					</MkInput>
-					<MkRange v-model="options.userEachUserListsLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+					<MkRange v-model="policies.userEachUserListsLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
 						<template #label>{{ i18n.ts._role.priority }}</template>
 					</MkRange>
 				</div>
@@ -306,7 +306,7 @@ import * as os from '@/os';
 import { i18n } from '@/i18n';
 import { instance } from '@/instance';
 
-const ROLE_OPTIONS = [
+const ROLE_POLICIES = [
 	'gtlAvailable',
 	'ltlAvailable',
 	'canPublicNote',
@@ -345,13 +345,13 @@ let condFormula = $ref(role?.condFormula ?? { id: uuid(), type: 'isRemote' });
 let isPublic = $ref(role?.isPublic ?? false);
 let canEditMembersByModerator = $ref(role?.canEditMembersByModerator ?? false);
 
-const options = reactive<Record<typeof ROLE_OPTIONS[number], { useDefault: boolean; priority: number; value: any; }>>({});
-for (const ROLE_OPTION of ROLE_OPTIONS) {
-	const _options = role?.options ?? {};
-	options[ROLE_OPTION] = {
-		useDefault: _options[ROLE_OPTION]?.useDefault ?? true,
-		priority: _options[ROLE_OPTION]?.priority ?? 0,
-		value: _options[ROLE_OPTION]?.value ?? instance.baseRole[ROLE_OPTION],
+const policies = reactive<Record<typeof ROLE_POLICIES[number], { useDefault: boolean; priority: number; value: any; }>>({});
+for (const ROLE_POLICY of ROLE_POLICIES) {
+	const _policies = role?.policies ?? {};
+	policies[ROLE_POLICY] = {
+		useDefault: _policies[ROLE_POLICY]?.useDefault ?? true,
+		priority: _policies[ROLE_POLICY]?.priority ?? 0,
+		value: _policies[ROLE_POLICY]?.value ?? instance.policies[ROLE_POLICY],
 	};
 }
 
@@ -381,7 +381,7 @@ async function save() {
 			isModerator: rolePermission === 'moderator',
 			isPublic,
 			canEditMembersByModerator,
-			options,
+			policies,
 		});
 		emit('updated');
 	} else {
@@ -395,7 +395,7 @@ async function save() {
 			isModerator: rolePermission === 'moderator',
 			isPublic,
 			canEditMembersByModerator,
-			options,
+			policies,
 		});
 		emit('created', created);
 	}
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 1ceead8b6c..f074069df0 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -10,114 +10,114 @@
 					<div class="_gaps">
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.rateLimitFactor }}</template>
-							<template #suffix>{{ Math.floor(options_rateLimitFactor * 100) }}%</template>
-							<MkRange :model-value="options_rateLimitFactor * 100" :min="30" :max="300" :step="10" :text-converter="(v) => `${v}%`" @update:model-value="v => options_rateLimitFactor = (v / 100)">
+							<template #suffix>{{ Math.floor(policies.rateLimitFactor * 100) }}%</template>
+							<MkRange :model-value="policies.rateLimitFactor * 100" :min="30" :max="300" :step="10" :text-converter="(v) => `${v}%`" @update:model-value="v => policies.rateLimitFactor = (v / 100)">
 								<template #caption>{{ i18n.ts._role._options.descriptionOfRateLimitFactor }}</template>
 							</MkRange>
 						</MkFolder>
 
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.gtlAvailable }}</template>
-							<template #suffix>{{ options_gtlAvailable ? i18n.ts.yes : i18n.ts.no }}</template>
-							<MkSwitch v-model="options_gtlAvailable">
+							<template #suffix>{{ policies.gtlAvailable ? i18n.ts.yes : i18n.ts.no }}</template>
+							<MkSwitch v-model="policies.gtlAvailable">
 								<template #label>{{ i18n.ts.enable }}</template>
 							</MkSwitch>
 						</MkFolder>
 
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.ltlAvailable }}</template>
-							<template #suffix>{{ options_ltlAvailable ? i18n.ts.yes : i18n.ts.no }}</template>
-							<MkSwitch v-model="options_ltlAvailable">
+							<template #suffix>{{ policies.ltlAvailable ? i18n.ts.yes : i18n.ts.no }}</template>
+							<MkSwitch v-model="policies.ltlAvailable">
 								<template #label>{{ i18n.ts.enable }}</template>
 							</MkSwitch>
 						</MkFolder>
 
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.canPublicNote }}</template>
-							<template #suffix>{{ options_canPublicNote ? i18n.ts.yes : i18n.ts.no }}</template>
-							<MkSwitch v-model="options_canPublicNote">
+							<template #suffix>{{ policies.canPublicNote ? i18n.ts.yes : i18n.ts.no }}</template>
+							<MkSwitch v-model="policies.canPublicNote">
 								<template #label>{{ i18n.ts.enable }}</template>
 							</MkSwitch>
 						</MkFolder>
 
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.canInvite }}</template>
-							<template #suffix>{{ options_canInvite ? i18n.ts.yes : i18n.ts.no }}</template>
-							<MkSwitch v-model="options_canInvite">
+							<template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template>
+							<MkSwitch v-model="policies.canInvite">
 								<template #label>{{ i18n.ts.enable }}</template>
 							</MkSwitch>
 						</MkFolder>
 
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template>
-							<template #suffix>{{ options_canManageCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template>
-							<MkSwitch v-model="options_canManageCustomEmojis">
+							<template #suffix>{{ policies.canManageCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template>
+							<MkSwitch v-model="policies.canManageCustomEmojis">
 								<template #label>{{ i18n.ts.enable }}</template>
 							</MkSwitch>
 						</MkFolder>
 
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
-							<template #suffix>{{ options_driveCapacityMb }}MB</template>
-							<MkInput v-model="options_driveCapacityMb" type="number">
+							<template #suffix>{{ policies.driveCapacityMb }}MB</template>
+							<MkInput v-model="policies.driveCapacityMb" type="number">
 								<template #suffix>MB</template>
 							</MkInput>
 						</MkFolder>
 
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.pinMax }}</template>
-							<template #suffix>{{ options_pinLimit }}</template>
-							<MkInput v-model="options_pinLimit" type="number">
+							<template #suffix>{{ policies.pinLimit }}</template>
+							<MkInput v-model="policies.pinLimit" type="number">
 							</MkInput>
 						</MkFolder>
 
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.antennaMax }}</template>
-							<template #suffix>{{ options_antennaLimit }}</template>
-							<MkInput v-model="options_antennaLimit" type="number">
+							<template #suffix>{{ policies.antennaLimit }}</template>
+							<MkInput v-model="policies.antennaLimit" type="number">
 							</MkInput>
 						</MkFolder>
 
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.wordMuteMax }}</template>
-							<template #suffix>{{ options_wordMuteLimit }}</template>
-							<MkInput v-model="options_wordMuteLimit" type="number">
+							<template #suffix>{{ policies.wordMuteLimit }}</template>
+							<MkInput v-model="policies.wordMuteLimit" type="number">
 								<template #suffix>chars</template>
 							</MkInput>
 						</MkFolder>
 
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.webhookMax }}</template>
-							<template #suffix>{{ options_webhookLimit }}</template>
-							<MkInput v-model="options_webhookLimit" type="number">
+							<template #suffix>{{ policies.webhookLimit }}</template>
+							<MkInput v-model="policies.webhookLimit" type="number">
 							</MkInput>
 						</MkFolder>
 
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.clipMax }}</template>
-							<template #suffix>{{ options_clipLimit }}</template>
-							<MkInput v-model="options_clipLimit" type="number">
+							<template #suffix>{{ policies.clipLimit }}</template>
+							<MkInput v-model="policies.clipLimit" type="number">
 							</MkInput>
 						</MkFolder>
 
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.noteEachClipsMax }}</template>
-							<template #suffix>{{ options_noteEachClipsLimit }}</template>
-							<MkInput v-model="options_noteEachClipsLimit" type="number">
+							<template #suffix>{{ policies.noteEachClipsLimit }}</template>
+							<MkInput v-model="policies.noteEachClipsLimit" type="number">
 							</MkInput>
 						</MkFolder>
 
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.userListMax }}</template>
-							<template #suffix>{{ options_userListLimit }}</template>
-							<MkInput v-model="options_userListLimit" type="number">
+							<template #suffix>{{ policies.userListLimit }}</template>
+							<MkInput v-model="policies.userListLimit" type="number">
 							</MkInput>
 						</MkFolder>
 
 						<MkFolder>
 							<template #label>{{ i18n.ts._role._options.userEachUserListsMax }}</template>
-							<template #suffix>{{ options_userEachUserListsLimit }}</template>
-							<MkInput v-model="options_userEachUserListsLimit" type="number">
+							<template #suffix>{{ policies.userEachUserListsLimit }}</template>
+							<MkInput v-model="policies.userEachUserListsLimit" type="number">
 							</MkInput>
 						</MkFolder>
 
@@ -134,7 +134,7 @@
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, reactive } from 'vue';
 import XHeader from './_header_.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
@@ -150,45 +150,36 @@ import { definePageMetadata } from '@/scripts/page-metadata';
 import { instance } from '@/instance';
 import { useRouter } from '@/router';
 
+const ROLE_POLICIES = [
+	'gtlAvailable',
+	'ltlAvailable',
+	'canPublicNote',
+	'canInvite',
+	'canManageCustomEmojis',
+	'driveCapacityMb',
+	'pinLimit',
+	'antennaLimit',
+	'wordMuteLimit',
+	'webhookLimit',
+	'clipLimit',
+	'noteEachClipsLimit',
+	'userListLimit',
+	'userEachUserListsLimit',
+	'rateLimitFactor',
+] as const;
+
 const router = useRouter();
 
 const roles = await os.api('admin/roles/list');
 
-let options_gtlAvailable = $ref(instance.baseRole.gtlAvailable);
-let options_ltlAvailable = $ref(instance.baseRole.ltlAvailable);
-let options_canPublicNote = $ref(instance.baseRole.canPublicNote);
-let options_canInvite = $ref(instance.baseRole.canInvite);
-let options_canManageCustomEmojis = $ref(instance.baseRole.canManageCustomEmojis);
-let options_driveCapacityMb = $ref(instance.baseRole.driveCapacityMb);
-let options_pinLimit = $ref(instance.baseRole.pinLimit);
-let options_antennaLimit = $ref(instance.baseRole.antennaLimit);
-let options_wordMuteLimit = $ref(instance.baseRole.wordMuteLimit);
-let options_webhookLimit = $ref(instance.baseRole.webhookLimit);
-let options_clipLimit = $ref(instance.baseRole.clipLimit);
-let options_noteEachClipsLimit = $ref(instance.baseRole.noteEachClipsLimit);
-let options_userListLimit = $ref(instance.baseRole.userListLimit);
-let options_userEachUserListsLimit = $ref(instance.baseRole.userEachUserListsLimit);
-let options_rateLimitFactor = $ref(instance.baseRole.rateLimitFactor);
+const policies = reactive<Record<typeof ROLE_POLICIES[number], any>>({});
+for (const ROLE_POLICY of ROLE_POLICIES) {
+	policies[ROLE_POLICY] = instance.policies[ROLE_POLICY];
+}
 
 async function updateBaseRole() {
-	await os.apiWithDialog('admin/roles/update-default-role-override', {
-		options: {
-			gtlAvailable: options_gtlAvailable,
-			ltlAvailable: options_ltlAvailable,
-			canPublicNote: options_canPublicNote,
-			canInvite: options_canInvite,
-			canManageCustomEmojis: options_canManageCustomEmojis,
-			driveCapacityMb: options_driveCapacityMb,
-			pinLimit: options_pinLimit,
-			antennaLimit: options_antennaLimit,
-			wordMuteLimit: options_wordMuteLimit,
-			webhookLimit: options_webhookLimit,
-			clipLimit: options_clipLimit,
-			noteEachClipsLimit: options_noteEachClipsLimit,
-			userListLimit: options_userListLimit,
-			userEachUserListsLimit: options_userEachUserListsLimit,
-			rateLimitFactor: options_rateLimitFactor,
-		},
+	await os.apiWithDialog('admin/roles/update-default-policies', {
+		policies,
 	});
 }
 
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index f5ee1be92c..59dc1114d1 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -35,8 +35,8 @@ import { definePageMetadata } from '@/scripts/page-metadata';
 
 const XTutorial = defineAsyncComponent(() => import('./timeline.tutorial.vue'));
 
-const isLocalTimelineAvailable = ($i == null && instance.baseRole.ltlAvailable) || ($i != null && $i.role.ltlAvailable);
-const isGlobalTimelineAvailable = ($i == null && instance.baseRole.gtlAvailable) || ($i != null && $i.role.gtlAvailable);
+const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable);
+const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable);
 const keymap = {
 	't': focus,
 };
diff --git a/packages/frontend/src/pages/user-info.vue b/packages/frontend/src/pages/user-info.vue
index 5bc98a56a7..d7e8102d60 100644
--- a/packages/frontend/src/pages/user-info.vue
+++ b/packages/frontend/src/pages/user-info.vue
@@ -86,16 +86,28 @@
 					</div>
 				</FormSection>
 			</div>
+
 			<div v-else-if="tab === 'moderation'" class="_gaps_m">
 				<MkSwitch v-model="suspended" @update:model-value="toggleSuspend">{{ i18n.ts.suspend }}</MkSwitch>
+
 				<div>
 					<MkButton v-if="user.host == null && iAmModerator" inline style="margin-right: 8px;" @click="resetPassword"><i class="ti ti-key"></i> {{ i18n.ts.resetPassword }}</MkButton>
 					<MkButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ i18n.ts.deleteAccount }}</MkButton>
 				</div>
+
+				<MkFolder>
+					<template #icon><i class="ti ti-license"></i></template>
+					<template #label>{{ i18n.ts._role.policies }}</template>
+					<div class="_gaps">
+						<div v-for="policy in Object.keys(info.policies)" :key="policy">
+							{{ policy }} ... {{ info.policies[policy] }}
+						</div>
+					</div>
+				</MkFolder>
+
 				<MkFolder>
 					<template #icon><i class="ti ti-badges"></i></template>
 					<template #label>{{ i18n.ts.roles }}</template>
-
 					<div class="_gaps">
 						<MkButton v-if="user.host == null && iAmModerator" primary rounded @click="assignRole"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton>
 
@@ -105,6 +117,7 @@
 						</div>
 					</div>
 				</MkFolder>
+
 				<MkFolder>
 					<template #icon><i class="ti ti-password"></i></template>
 					<template #label>IP</template>
@@ -117,16 +130,18 @@
 						</div>
 					</template>
 				</MkFolder>
+
 				<MkFolder>
 					<template #icon><i class="ti ti-cloud"></i></template>
 					<template #label>{{ i18n.ts.files }}</template>
-
 					<MkFileListForAdmin :pagination="filesPagination" view-mode="grid"/>
 				</MkFolder>
+
 				<MkTextarea v-model="moderationNote" manual-save>
 					<template #label>Moderation note</template>
 				</MkTextarea>
 			</div>
+
 			<div v-else-if="tab === 'chart'" class="_gaps_m">
 				<div class="cmhjzshm">
 					<div class="selects">
@@ -142,6 +157,7 @@
 					</div>
 				</div>
 			</div>
+
 			<div v-else-if="tab === 'raw'" class="_gaps_m">
 				<MkObjectView v-if="info && $i.isAdmin" tall :value="info">
 				</MkObjectView>
diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts
index cc152f9c1c..a90ec6172f 100644
--- a/packages/frontend/src/ui/_common_/common.ts
+++ b/packages/frontend/src/ui/_common_/common.ts
@@ -47,7 +47,7 @@ export function openInstanceMenu(ev: MouseEvent) {
 			to: '/clicker',
 			text: '🍪👈',
 			icon: 'ti ti-cookie',
-		}, ($i && ($i.isAdmin || $i.role.canInvite) && instance.disableRegistration) ? {
+		}, ($i && ($i.isAdmin || $i.policies.canInvite) && instance.disableRegistration) ? {
 			text: i18n.ts.invite,
 			icon: 'ti ti-user-plus',
 			action: () => {
@@ -63,7 +63,7 @@ export function openInstanceMenu(ev: MouseEvent) {
 					});
 				});
 			},
-		} : undefined, ($i && ($i.isAdmin || $i.role.canManageCustomEmojis)) ? {
+		} : undefined, ($i && ($i.isAdmin || $i.policies.canManageCustomEmojis)) ? {
 			type: 'link',
 			to: '/custom-emojis-manager',
 			text: i18n.ts.manageCustomEmojis,