diff --git a/locales/en-US.yml b/locales/en-US.yml
index 67d7dd8a87..26cf271a5a 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -1664,6 +1664,7 @@ _role:
     gtlAvailable: "Can view the global timeline"
     ltlAvailable: "Can view the local timeline"
     canPublicNote: "Can send public notes"
+    canInitiateConversation: "Can mention, reply or quote"
     canCreateContent: "Can create contents"
     canUpdateContent: "Can edit contents"
     canDeleteContent: "Can delete contents"
diff --git a/locales/index.d.ts b/locales/index.d.ts
index ff6b22aaad..bb7a00edea 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -6558,6 +6558,10 @@ export interface Locale extends ILocale {
              * パブリック投稿の許可
              */
             "canPublicNote": string;
+            /**
+             * メンション、リプライ、引用の許可
+             */
+            "canInitiateConversation": string;
             /**
              * コンテンツの作成
              */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 2766d0a913..31a4567ff9 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1697,6 +1697,7 @@ _role:
     gtlAvailable: "グローバルタイムラインの閲覧"
     ltlAvailable: "ローカルタイムラインの閲覧"
     canPublicNote: "パブリック投稿の許可"
+    canInitiateConversation: "メンション、リプライ、引用の許可"
     canCreateContent: "コンテンツの作成"
     canUpdateContent: "コンテンツの編集"
     canDeleteContent: "コンテンツの削除"
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index b775344666..6e442451b9 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -259,13 +259,14 @@ export class NoteCreateService implements OnApplicationShutdown {
 		if (data.channel != null) data.localOnly = true;
 
 		const meta = await this.metaService.fetch();
+		const policies = await this.roleService.getUserPolicies(user.id);
 
 		if (data.visibility === 'public' && data.channel == null) {
 			const sensitiveWords = meta.sensitiveWords;
 			if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) {
 				data.visibility = 'home';
 				this.logger.warn('Visibility changed to home because sensitive words are included', { user: user.id, note: data });
-			} else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
+			} else if (policies.canPublicNote === false) {
 				data.visibility = 'home';
 			}
 		}
@@ -379,6 +380,18 @@ export class NoteCreateService implements OnApplicationShutdown {
 			}
 		}
 
+		if (policies.canInitiateConversation === false) {
+			if (
+				mentionedUsers.some(u => u.id !== user.id)
+				|| (data.reply && data.reply.replyUserId !== user.id)
+				|| (data.visibility === 'specified' && data.visibleUsers?.some(u => u.id !== user.id))
+				|| (this.isQuote(data) && data.renote.userId !== user.id)
+			) {
+				this.logger.error('Request rejected because user has no permission to initiate conversation', { user: user.id, note: data });
+				throw new IdentifiableError('332dd91b-6a00-430a-ac39-620cf60ad34b', 'Notes including mentions, replies, or renotes are not allowed.');
+			}
+		}
+
 		tags = tags.filter(tag => Array.from(tag).length <= 128).splice(0, 32);
 
 		if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) {
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 6c3582fbd8..d8797040f8 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -36,6 +36,7 @@ export type RolePolicies = {
 	gtlAvailable: boolean;
 	ltlAvailable: boolean;
 	canPublicNote: boolean;
+	canInitiateConversation: boolean;
 	canCreateContent: boolean;
 	canUpdateContent: boolean;
 	canDeleteContent: boolean;
@@ -69,6 +70,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
 	gtlAvailable: true,
 	ltlAvailable: true,
 	canPublicNote: true,
+	canInitiateConversation: true,
 	canCreateContent: true,
 	canUpdateContent: true,
 	canDeleteContent: true,
@@ -338,6 +340,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
 			gtlAvailable: calc('gtlAvailable', vs => vs.some(v => v === true)),
 			ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)),
 			canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)),
+			canInitiateConversation: calc('canInitiateConversation', vs => vs.some(v => v === true)),
 			canCreateContent: calc('canCreateContent', vs => vs.some(v => v === true)),
 			canUpdateContent: calc('canUpdateContent', vs => vs.some(v => v === true)),
 			canDeleteContent: calc('canDeleteContent', vs => vs.some(v => v === true)),
diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index e081f3b7a9..ed83676a2e 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -393,7 +393,7 @@ export class UserEntityService implements OnModuleInit {
 				bannerBlurhash: user.bannerBlurhash,
 				isLocked: user.isLocked,
 				isSilenced: !policies?.canPublicNote,
-				isLimited: !(policies?.canCreateContent && policies.canUpdateContent && policies.canDeleteContent),
+				isLimited: !(policies?.canCreateContent && policies.canUpdateContent && policies.canDeleteContent && policies.canInitiateConversation),
 				isSuspended: user.isSuspended,
 				description: profile!.description,
 				location: profile!.location,
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 02797476b4..b9cc7a555d 100644
--- a/packages/backend/src/server/api/endpoints/admin/show-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts
@@ -212,7 +212,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			const policies = await this.roleService.getUserPolicies(user.id);
 			const isModerator = await this.roleService.isModerator(user);
-			const isLimited = !(policies.canCreateContent && policies.canUpdateContent && policies.canDeleteContent);
+			const isLimited = !(policies.canCreateContent && policies.canUpdateContent && policies.canDeleteContent && policies.canInitiateConversation);
 			const isSilenced = !policies.canPublicNote;
 
 			const _me = await this.usersRepository.findOneByOrFail({ id: me.id });
diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts
index 5d517ad9c4..02674ef88c 100644
--- a/packages/frontend/src/const.ts
+++ b/packages/frontend/src/const.ts
@@ -75,6 +75,7 @@ export const ROLE_POLICIES = [
 	'gtlAvailable',
 	'ltlAvailable',
 	'canPublicNote',
+	'canInitiateConversation',
 	'canCreateContent',
 	'canUpdateContent',
 	'canDeleteContent',
@@ -131,7 +132,7 @@ export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = {
 	position: ['x=', 'y='],
 	fg: ['color='],
 	bg: ['color='],
-  border: ['width=', 'style=', 'color=', 'radius=', 'noclip'],
+	border: ['width=', 'style=', 'color=', 'radius=', 'noclip'],
 	font: ['serif', 'monospace', 'cursive', 'fantasy', 'emoji', 'math'],
 	blur: [],
 	rainbow: ['speed=', 'delay='],
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index b4052fe335..8ee1958fcb 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -160,6 +160,26 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 			</MkFolder>
 
+			<MkFolder v-if="matchQuery([i18n.ts._role._options.canInitiateConversation, 'canInitiateConversation'])">
+				<template #label>{{ i18n.ts._role._options.canInitiateConversation }}</template>
+				<template #suffix>
+					<span v-if="role.policies.canInitiateConversation.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
+					<span v-else>{{ role.policies.canInitiateConversation.value ? i18n.ts.yes : i18n.ts.no }}</span>
+					<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canInitiateConversation)"></i></span>
+				</template>
+				<div class="_gaps">
+					<MkSwitch v-model="role.policies.canInitiateConversation.useDefault" :readonly="readonly">
+						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
+					</MkSwitch>
+					<MkSwitch v-model="role.policies.canInitiateConversation.value" :disabled="role.policies.canInitiateConversation.useDefault" :readonly="readonly">
+						<template #label>{{ i18n.ts.enable }}</template>
+					</MkSwitch>
+					<MkRange v-model="role.policies.canInitiateConversation.priority" :min="0" :max="2" :step="1" easing :textConverter="(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>
+			</MkFolder>
+
 			<MkFolder v-if="matchQuery([i18n.ts._role._options.canCreateContent, 'canCreateContent'])">
 				<template #label>{{ i18n.ts._role._options.canCreateContent }}</template>
 				<template #suffix>
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 8386b82e98..86637660c7 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -48,6 +48,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 							</MkSwitch>
 						</MkFolder>
 
+						<MkFolder v-if="matchQuery([i18n.ts._role._options.canInitiateConversation, 'canInitiateConversation'])">
+							<template #label>{{ i18n.ts._role._options.canInitiateConversation }}</template>
+							<template #suffix>{{ policies.canInitiateConversation ? i18n.ts.yes : i18n.ts.no }}</template>
+							<MkSwitch v-model="policies.canInitiateConversation">
+								<template #label>{{ i18n.ts.enable }}</template>
+							</MkSwitch>
+						</MkFolder>
+
 						<MkFolder v-if="matchQuery([i18n.ts._role._options.canCreateContent, 'canCreateContent'])">
 							<template #label>{{ i18n.ts._role._options.canCreateContent }}</template>
 							<template #suffix>{{ policies.canCreateContent ? i18n.ts.yes : i18n.ts.no }}</template>