diff --git a/migration/1576269851876-TalkFederationId.ts b/migration/1576269851876-TalkFederationId.ts
new file mode 100644
index 0000000000..57e86c8b03
--- /dev/null
+++ b/migration/1576269851876-TalkFederationId.ts
@@ -0,0 +1,14 @@
+import {MigrationInterface, QueryRunner} from "typeorm";
+
+export class TalkFederationId1576269851876 implements MigrationInterface {
+    name = 'TalkFederationId1576269851876'
+
+    public async up(queryRunner: QueryRunner): Promise<any> {
+        await queryRunner.query(`ALTER TABLE "messaging_message" ADD "uri" character varying(512)`, undefined);
+    }
+
+    public async down(queryRunner: QueryRunner): Promise<any> {
+        await queryRunner.query(`ALTER TABLE "messaging_message" DROP COLUMN "uri"`, undefined);
+    }
+
+}
diff --git a/src/models/entities/messaging-message.ts b/src/models/entities/messaging-message.ts
index c18897a37d..ac0764674c 100644
--- a/src/models/entities/messaging-message.ts
+++ b/src/models/entities/messaging-message.ts
@@ -64,6 +64,11 @@ export class MessagingMessage {
 	})
 	public isRead: boolean;
 
+	@Column('varchar', {
+		length: 512, nullable: true,
+	})
+	public uri: string | null;
+
 	@Column({
 		...id(),
 		array: true, default: '{}'
diff --git a/src/remote/activitypub/kernel/index.ts b/src/remote/activitypub/kernel/index.ts
index c8298dc797..615edff88c 100644
--- a/src/remote/activitypub/kernel/index.ts
+++ b/src/remote/activitypub/kernel/index.ts
@@ -1,8 +1,9 @@
-import { IObject, isCreate, isDelete, isUpdate, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isCollection  } from '../type';
+import { IObject, isCreate, isDelete, isUpdate, isRead, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isCollection } from '../type';
 import { IRemoteUser } from '../../../models/entities/user';
 import create from './create';
 import performDeleteActivity from './delete';
 import performUpdateActivity from './update';
+import { performReadActivity } from './read';
 import follow from './follow';
 import undo from './undo';
 import like from './like';
@@ -41,6 +42,8 @@ async function performOneActivity(actor: IRemoteUser, activity: IObject): Promis
 		await performDeleteActivity(actor, activity);
 	} else if (isUpdate(activity)) {
 		await performUpdateActivity(actor, activity);
+	} else if (isRead(activity)) {
+		await performReadActivity(actor, activity);
 	} else if (isFollow(activity)) {
 		await follow(actor, activity);
 	} else if (isAccept(activity)) {
diff --git a/src/remote/activitypub/kernel/read.ts b/src/remote/activitypub/kernel/read.ts
new file mode 100644
index 0000000000..e4049fa7ef
--- /dev/null
+++ b/src/remote/activitypub/kernel/read.ts
@@ -0,0 +1,27 @@
+import { IRemoteUser } from '../../../models/entities/user';
+import { IRead, getApId } from '../type';
+import { isSelfHost, extractDbHost } from '../../../misc/convert-host';
+import { MessagingMessages } from '../../../models';
+import { readUserMessagingMessage } from '../../../server/api/common/read-messaging-message';
+
+export const performReadActivity = async (actor: IRemoteUser, activity: IRead): Promise<string> => {
+	const id = await getApId(activity.object);
+
+	if (!isSelfHost(extractDbHost(id))) {
+		return `skip: Read to foreign host (${id})`;
+	}
+
+	const messageId = id.split('/').pop();
+
+	const message = await MessagingMessages.findOne(messageId);
+	if (message == null) {
+		return `skip: message not found`;
+	}
+
+	if (actor.id != message.recipientId) {
+		return `skip: actor is not a message recipient`;
+	}
+
+	await readUserMessagingMessage(message.recipientId!, message.userId, [message.id]);
+	return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`;
+};
diff --git a/src/remote/activitypub/models/note.ts b/src/remote/activitypub/models/note.ts
index 17c3721bdb..7ce0b6a11f 100644
--- a/src/remote/activitypub/models/note.ts
+++ b/src/remote/activitypub/models/note.ts
@@ -226,7 +226,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
 
 	if (note._misskey_talk && visibility === 'specified') {
 		for (const recipient of visibleUsers) {
-			await createMessage(actor, recipient, undefined, text || undefined, (files && files.length > 0) ? files[0] : null);
+			await createMessage(actor, recipient, undefined, text || undefined, (files && files.length > 0) ? files[0] : null, object.id);
 			return null;
 		}
 	}
diff --git a/src/remote/activitypub/renderer/ordered-collection.ts b/src/remote/activitypub/renderer/ordered-collection.ts
index 5461005983..68870a0ecd 100644
--- a/src/remote/activitypub/renderer/ordered-collection.ts
+++ b/src/remote/activitypub/renderer/ordered-collection.ts
@@ -6,7 +6,7 @@
  * @param last URL of last page (optional)
  * @param orderedItems attached objects (optional)
  */
-export default function(id: string, totalItems: any, first?: string, last?: string, orderedItems?: object) {
+export default function(id: string | null, totalItems: any, first?: string, last?: string, orderedItems?: object) {
 	const page: any = {
 		id,
 		type: 'OrderedCollection',
diff --git a/src/remote/activitypub/renderer/read.ts b/src/remote/activitypub/renderer/read.ts
new file mode 100644
index 0000000000..c53b47859f
--- /dev/null
+++ b/src/remote/activitypub/renderer/read.ts
@@ -0,0 +1,9 @@
+import config from '../../../config';
+import { ILocalUser } from '../../../models/entities/user';
+import { MessagingMessage } from '../../../models/entities/messaging-message';
+
+export const renderReadActivity = (user: ILocalUser, message: MessagingMessage) => ({
+	type: 'Read',
+	actor: `${config.url}/users/${user.id}`,
+	object: message.uri
+});
diff --git a/src/remote/activitypub/type.ts b/src/remote/activitypub/type.ts
index 5670df243d..ad3f9638a7 100644
--- a/src/remote/activitypub/type.ts
+++ b/src/remote/activitypub/type.ts
@@ -140,6 +140,10 @@ export interface IUpdate extends IActivity {
 	type: 'Update';
 }
 
+export interface IRead extends IActivity {
+	type: 'Read';
+}
+
 export interface IUndo extends IActivity {
 	type: 'Undo';
 }
@@ -180,6 +184,7 @@ export interface IBlock extends IActivity {
 export const isCreate = (object: IObject): object is ICreate => object.type === 'Create';
 export const isDelete = (object: IObject): object is IDelete => object.type === 'Delete';
 export const isUpdate = (object: IObject): object is IUpdate => object.type === 'Update';
+export const isRead = (object: IObject): object is IRead => object.type === 'Read';
 export const isUndo = (object: IObject): object is IUndo => object.type === 'Undo';
 export const isFollow = (object: IObject): object is IFollow => object.type === 'Follow';
 export const isAccept = (object: IObject): object is IAccept => object.type === 'Accept';
diff --git a/src/server/api/common/read-messaging-message.ts b/src/server/api/common/read-messaging-message.ts
index d18d8cd9d2..90510bb393 100644
--- a/src/server/api/common/read-messaging-message.ts
+++ b/src/server/api/common/read-messaging-message.ts
@@ -1,12 +1,17 @@
 import { publishMainStream, publishGroupMessagingStream } from '../../../services/stream';
 import { publishMessagingStream } from '../../../services/stream';
 import { publishMessagingIndexStream } from '../../../services/stream';
-import { User } from '../../../models/entities/user';
+import { User, ILocalUser, IRemoteUser } from '../../../models/entities/user';
 import { MessagingMessage } from '../../../models/entities/messaging-message';
 import { MessagingMessages, UserGroupJoinings, Users } from '../../../models';
 import { In } from 'typeorm';
 import { IdentifiableError } from '../../../misc/identifiable-error';
 import { UserGroup } from '../../../models/entities/user-group';
+import { toArray } from '../../../prelude/array';
+import { renderReadActivity } from '../../../remote/activitypub/renderer/read';
+import { renderActivity } from '../../../remote/activitypub/renderer';
+import { deliver } from '../../../queue';
+import orderedCollection from '../../../remote/activitypub/renderer/ordered-collection';
 
 /**
  * Mark messages as read
@@ -101,3 +106,17 @@ export async function readGroupMessagingMessage(
 		publishMainStream(userId, 'readAllMessagingMessages');
 	}
 }
+
+export async function deliverReadActivity(user: ILocalUser, recipient: IRemoteUser, messages: MessagingMessage | MessagingMessage[]) {
+	messages = toArray(messages).filter(x => x.uri);
+	const contents = messages.map(x => renderReadActivity(user, x));
+
+	if (contents.length > 1) {
+		const collection = orderedCollection(null, contents.length, undefined, undefined, contents);
+		deliver(user, renderActivity(collection), recipient.inbox);
+	} else {
+		for (const content of contents) {
+			deliver(user, renderActivity(content), recipient.inbox);
+		}
+	}
+}
diff --git a/src/server/api/endpoints/messaging/messages.ts b/src/server/api/endpoints/messaging/messages.ts
index b0b3e20d02..ea01086a8b 100644
--- a/src/server/api/endpoints/messaging/messages.ts
+++ b/src/server/api/endpoints/messaging/messages.ts
@@ -3,10 +3,10 @@ import { ID } from '../../../../misc/cafy-id';
 import define from '../../define';
 import { ApiError } from '../../error';
 import { getUser } from '../../common/getters';
-import { MessagingMessages, UserGroups, UserGroupJoinings } from '../../../../models';
+import { MessagingMessages, UserGroups, UserGroupJoinings, Users } from '../../../../models';
 import { makePaginationQuery } from '../../common/make-pagination-query';
 import { Brackets } from 'typeorm';
-import { readUserMessagingMessage, readGroupMessagingMessage } from '../../common/read-messaging-message';
+import { readUserMessagingMessage, readGroupMessagingMessage, deliverReadActivity } from '../../common/read-messaging-message';
 
 export const meta = {
 	desc: {
@@ -114,6 +114,11 @@ export default define(meta, async (ps, user) => {
 		// Mark all as read
 		if (ps.markAsRead) {
 			readUserMessagingMessage(user.id, recipient.id, messages.filter(m => m.recipientId === user.id).map(x => x.id));
+
+			// リモートユーザーとのメッセージだったら既読配信
+			if (Users.isLocalUser(user) && Users.isRemoteUser(recipient)) {
+				deliverReadActivity(user, recipient, messages);
+			}
 		}
 
 		return await Promise.all(messages.map(message => MessagingMessages.pack(message, user, {
diff --git a/src/server/api/stream/channels/messaging.ts b/src/server/api/stream/channels/messaging.ts
index 1e5e94c1c8..8456871e6a 100644
--- a/src/server/api/stream/channels/messaging.ts
+++ b/src/server/api/stream/channels/messaging.ts
@@ -1,7 +1,8 @@
 import autobind from 'autobind-decorator';
-import { readUserMessagingMessage, readGroupMessagingMessage } from '../../common/read-messaging-message';
+import { readUserMessagingMessage, readGroupMessagingMessage, deliverReadActivity } from '../../common/read-messaging-message';
 import Channel from '../channel';
-import { UserGroupJoinings } from '../../../../models';
+import { UserGroupJoinings, Users, MessagingMessages } from '../../../../models';
+import { User, ILocalUser, IRemoteUser } from '../../../../models/entities/user';
 
 export default class extends Channel {
 	public readonly chName = 'messaging';
@@ -9,11 +10,13 @@ export default class extends Channel {
 	public static requireCredential = true;
 
 	private otherpartyId: string | null;
+	private otherparty?: User;
 	private groupId: string | null;
 
 	@autobind
 	public async init(params: any) {
 		this.otherpartyId = params.otherparty as string;
+		this.otherparty = await Users.findOne({ id: this.otherpartyId });
 		this.groupId = params.group as string;
 
 		// Check joining
@@ -44,6 +47,13 @@ export default class extends Channel {
 			case 'read':
 				if (this.otherpartyId) {
 					readUserMessagingMessage(this.user!.id, this.otherpartyId, [body.id]);
+
+					// リモートユーザーからのメッセージだったら既読配信
+					if (Users.isLocalUser(this.user!) && Users.isRemoteUser(this.otherparty!)) {
+						MessagingMessages.findOne(body.id).then(message => {
+							if (message) deliverReadActivity(this.user as ILocalUser, this.otherparty as IRemoteUser, message);
+						});
+					}
 				} else if (this.groupId) {
 					readGroupMessagingMessage(this.user!.id, this.groupId, [body.id]);
 				}
diff --git a/src/services/messages/create.ts b/src/services/messages/create.ts
index 278070aa86..8646ce37fc 100644
--- a/src/services/messages/create.ts
+++ b/src/services/messages/create.ts
@@ -13,7 +13,7 @@ import renderCreate from '../../remote/activitypub/renderer/create';
 import { renderActivity } from '../../remote/activitypub/renderer';
 import { deliver } from '../../queue';
 
-export async function createMessage(user: User, recipientUser: User | undefined, recipientGroup: UserGroup | undefined, text: string | undefined, file: DriveFile | null) {
+export async function createMessage(user: User, recipientUser: User | undefined, recipientGroup: UserGroup | undefined, text: string | undefined, file: DriveFile | null, uri?: string) {
 	const message = await MessagingMessages.save({
 		id: genId(),
 		createdAt: new Date(),
@@ -23,7 +23,8 @@ export async function createMessage(user: User, recipientUser: User | undefined,
 		text: text ? text.trim() : null,
 		userId: user.id,
 		isRead: false,
-		reads: [] as any[]
+		reads: [] as any[],
+		uri
 	} as MessagingMessage);
 
 	const messageObj = await MessagingMessages.pack(message);