From 148c3736cecc501c5a80e47366e5a62da51a0414 Mon Sep 17 00:00:00 2001
From: naskya <m@naskya.net>
Date: Fri, 12 Apr 2024 17:13:57 +0900
Subject: [PATCH] refactor (backend): port convert-host to backend-rs

Co-authored-by: sup39 <dev@sup39.dev>
---
 Cargo.lock                                    |  2 +
 Cargo.toml                                    |  2 +
 packages/backend-rs/Cargo.toml                |  2 +
 packages/backend-rs/index.d.ts                |  5 ++
 packages/backend-rs/index.js                  |  7 +-
 packages/backend-rs/src/config/server.rs      |  7 +-
 packages/backend-rs/src/misc/convert_host.rs  | 67 +++++++++++++++++++
 packages/backend-rs/src/misc/mod.rs           |  1 +
 .../backend/src/misc/check-hit-antenna.ts     |  3 +-
 packages/backend/src/misc/convert-host.ts     | 46 -------------
 packages/backend/src/misc/populate-emojis.ts  | 14 ++--
 packages/backend/src/misc/reaction-lib.ts     |  4 +-
 .../src/models/repositories/drive-file.ts     |  2 +-
 .../queue/processors/db/export-blocking.ts    |  2 +-
 .../queue/processors/db/export-following.ts   |  2 +-
 .../src/queue/processors/db/export-mute.ts    |  2 +-
 .../queue/processors/db/export-user-lists.ts  |  2 +-
 .../queue/processors/db/import-blocking.ts    |  3 +-
 .../queue/processors/db/import-following.ts   |  3 +-
 .../src/queue/processors/db/import-muting.ts  |  3 +-
 .../queue/processors/db/import-user-lists.ts  |  3 +-
 .../backend/src/queue/processors/deliver.ts   |  2 +-
 .../backend/src/queue/processors/inbox.ts     |  8 +--
 .../src/remote/activitypub/check-fetch.ts     |  2 +-
 .../activitypub/kernel/announce/note.ts       |  4 +-
 .../remote/activitypub/kernel/create/note.ts  |  4 +-
 .../src/remote/activitypub/kernel/index.ts    |  4 +-
 .../src/remote/activitypub/kernel/read.ts     |  4 +-
 .../src/remote/activitypub/models/note.ts     | 16 ++---
 .../src/remote/activitypub/models/person.ts   |  5 +-
 .../src/remote/activitypub/models/question.ts |  2 +-
 .../src/remote/activitypub/resolver.ts        |  6 +-
 packages/backend/src/remote/resolve-user.ts   |  2 +-
 packages/backend/src/server/activitypub.ts    |  2 +-
 .../backend/src/server/api/common/signup.ts   |  5 +-
 .../api/endpoints/admin/emoji/list-remote.ts  |  2 +-
 .../refresh-remote-instance-metadata.ts       |  2 +-
 .../admin/federation/update-instance.ts       |  2 +-
 .../src/server/api/endpoints/ap/show.ts       |  4 +-
 .../api/endpoints/federation/show-instance.ts |  2 +-
 .../server/api/endpoints/users/followers.ts   |  4 +-
 .../server/api/endpoints/users/following.ts   |  4 +-
 .../backend/src/services/messages/create.ts   |  3 +-
 .../register-or-fetch-instance-doc.ts         |  2 +-
 44 files changed, 150 insertions(+), 123 deletions(-)
 create mode 100644 packages/backend-rs/src/misc/convert_host.rs
 delete mode 100644 packages/backend/src/misc/convert-host.ts

diff --git a/Cargo.lock b/Cargo.lock
index 97190cdc6e..93c475fa0b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -195,6 +195,7 @@ dependencies = [
  "cfg-if",
  "chrono",
  "cuid2",
+ "idna",
  "jsonschema",
  "macro_rs",
  "napi",
@@ -212,6 +213,7 @@ dependencies = [
  "serde_yaml",
  "thiserror",
  "tokio",
+ "url",
  "urlencoding",
 ]
 
diff --git a/Cargo.toml b/Cargo.toml
index c983a9de36..f7ba12d884 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,6 +15,7 @@ cfg-if = "1.0.0"
 chrono = "0.4.37"
 convert_case = "0.6.0"
 cuid2 = "0.1.2"
+idna = "0.5.0"
 jsonschema = "0.17.1"
 once_cell = "1.19.0"
 parse-display = "0.9.0"
@@ -31,6 +32,7 @@ serde_yaml = "0.9.34"
 syn = "2.0.58"
 thiserror = "1.0.58"
 tokio = "1.37.0"
+url = "2.5.0"
 urlencoding = "2.1.3"
 
 [profile.release]
diff --git a/packages/backend-rs/Cargo.toml b/packages/backend-rs/Cargo.toml
index 0c0fdd16ab..783f630942 100644
--- a/packages/backend-rs/Cargo.toml
+++ b/packages/backend-rs/Cargo.toml
@@ -22,6 +22,7 @@ basen = { workspace = true }
 cfg-if = { workspace = true }
 chrono = { workspace = true }
 cuid2 = { workspace = true }
+idna = { workspace = true }
 jsonschema = { workspace = true }
 once_cell = { workspace = true }
 parse-display = { workspace = true }
@@ -34,6 +35,7 @@ serde_json = { workspace = true }
 serde_yaml = { workspace = true }
 thiserror = { workspace = true }
 tokio = { workspace = true, features = ["full"] }
+url = { workspace = true }
 urlencoding = { workspace = true }
 
 [dev-dependencies]
diff --git a/packages/backend-rs/index.d.ts b/packages/backend-rs/index.d.ts
index 6bc51dc7c4..fae7ac46da 100644
--- a/packages/backend-rs/index.d.ts
+++ b/packages/backend-rs/index.d.ts
@@ -127,6 +127,11 @@ export interface NoteLike {
   replyId: string | null
 }
 export function checkWordMute(note: NoteLike, mutedWordLists: Array<Array<string>>, mutedPatterns: Array<string>): Promise<boolean>
+export function getFullApAccount(username: string, host?: string | undefined | null): string
+export function isSelfHost(host?: string | undefined | null): boolean
+export function isSameOrigin(uri: string): boolean
+export function extractHost(uri: string): string
+export function toPuny(host: string): string
 export function nyaify(text: string, lang?: string | undefined | null): string
 export interface AbuseUserReport {
   id: string
diff --git a/packages/backend-rs/index.js b/packages/backend-rs/index.js
index 868e632d85..647a63e912 100644
--- a/packages/backend-rs/index.js
+++ b/packages/backend-rs/index.js
@@ -310,12 +310,17 @@ if (!nativeBinding) {
   throw new Error(`Failed to load native binding`)
 }
 
-const { readServerConfig, stringToAcct, acctToString, checkWordMute, nyaify, AntennaSrcEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initIdGenerator, getTimestamp, genId, secureRndstr, IdConvertType, convertId } = nativeBinding
+const { readServerConfig, stringToAcct, acctToString, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, nyaify, AntennaSrcEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initIdGenerator, getTimestamp, genId, secureRndstr, IdConvertType, convertId } = nativeBinding
 
 module.exports.readServerConfig = readServerConfig
 module.exports.stringToAcct = stringToAcct
 module.exports.acctToString = acctToString
 module.exports.checkWordMute = checkWordMute
+module.exports.getFullApAccount = getFullApAccount
+module.exports.isSelfHost = isSelfHost
+module.exports.isSameOrigin = isSameOrigin
+module.exports.extractHost = extractHost
+module.exports.toPuny = toPuny
 module.exports.nyaify = nyaify
 module.exports.AntennaSrcEnum = AntennaSrcEnum
 module.exports.MutedNoteReasonEnum = MutedNoteReasonEnum
diff --git a/packages/backend-rs/src/config/server.rs b/packages/backend-rs/src/config/server.rs
index 200b97dc4c..e958f4dc43 100644
--- a/packages/backend-rs/src/config/server.rs
+++ b/packages/backend-rs/src/config/server.rs
@@ -170,7 +170,12 @@ pub fn read_server_config() -> ServerConfig {
     let cwd = env::current_dir().unwrap();
     let yml = fs::File::open(cwd.join("../../.config/default.yml"))
         .expect("Failed to open '.config/default.yml'");
-    serde_yaml::from_reader(yml).expect("Failed to parse yaml")
+    let mut data: ServerConfig = serde_yaml::from_reader(yml).expect("Failed to parse yaml");
+    data.url = url::Url::parse(&data.url)
+        .expect("Config url is invalid")
+        .origin()
+        .ascii_serialization();
+    data
 }
 
 pub static SERVER_CONFIG: Lazy<ServerConfig> = Lazy::new(read_server_config);
diff --git a/packages/backend-rs/src/misc/convert_host.rs b/packages/backend-rs/src/misc/convert_host.rs
new file mode 100644
index 0000000000..34a0792c62
--- /dev/null
+++ b/packages/backend-rs/src/misc/convert_host.rs
@@ -0,0 +1,67 @@
+use crate::config::server::SERVER_CONFIG;
+
+#[derive(thiserror::Error, Debug)]
+pub enum Error {
+    #[error("Idna error: {0}")]
+    IdnaError(#[from] idna::Errors),
+    #[error("Url parse error: {0}")]
+    UrlParseError(#[from] url::ParseError),
+    #[error("Hostname is missing")]
+    NoHostname,
+}
+
+#[crate::export]
+pub fn get_full_ap_account(username: &str, host: Option<&str>) -> Result<String, Error> {
+    Ok(match host {
+        Some(host) => format!("{}@{}", username, to_puny(host)?),
+        None => format!("{}@{}", username, extract_host(&SERVER_CONFIG.url)?),
+    })
+}
+
+#[crate::export]
+pub fn is_self_host(host: Option<&str>) -> Result<bool, Error> {
+    Ok(match host {
+        Some(host) => extract_host(&SERVER_CONFIG.url)? == to_puny(host)?,
+        None => true,
+    })
+}
+
+#[crate::export]
+pub fn is_same_origin(uri: &str) -> Result<bool, Error> {
+    Ok(url::Url::parse(uri)?.origin().ascii_serialization() == SERVER_CONFIG.url)
+}
+
+#[crate::export]
+pub fn extract_host(uri: &str) -> Result<String, Error> {
+    url::Url::parse(uri)?
+        .host_str()
+        .ok_or(Error::NoHostname)
+        .and_then(|v| Ok(to_puny(v)?))
+}
+
+#[crate::export]
+pub fn to_puny(host: &str) -> Result<String, idna::Errors> {
+    idna::domain_to_ascii(host)
+}
+
+#[cfg(test)]
+mod unit_test {
+    use super::{extract_host, to_puny};
+    use pretty_assertions::assert_eq;
+
+    #[test]
+    fn extract_host_test() {
+        assert_eq!(
+            extract_host("https://firefish.dev/firefish/firefish.git").unwrap(),
+            "firefish.dev"
+        );
+    }
+
+    #[test]
+    fn to_puny_test() {
+        assert_eq!(
+            to_puny("何もかも.owari.shop").unwrap(),
+            "xn--u8jyfb5762a.owari.shop"
+        );
+    }
+}
diff --git a/packages/backend-rs/src/misc/mod.rs b/packages/backend-rs/src/misc/mod.rs
index ba404ca0d0..9f842613bb 100644
--- a/packages/backend-rs/src/misc/mod.rs
+++ b/packages/backend-rs/src/misc/mod.rs
@@ -1,3 +1,4 @@
 pub mod acct;
 pub mod check_word_mute;
+pub mod convert_host;
 pub mod nyaify;
diff --git a/packages/backend/src/misc/check-hit-antenna.ts b/packages/backend/src/misc/check-hit-antenna.ts
index 5b795a2c21..2fa764c85b 100644
--- a/packages/backend/src/misc/check-hit-antenna.ts
+++ b/packages/backend/src/misc/check-hit-antenna.ts
@@ -3,8 +3,7 @@ import type { Note } from "@/models/entities/note.js";
 import type { User } from "@/models/entities/user.js";
 import type { UserProfile } from "@/models/entities/user-profile.js";
 import { Blockings, Followings, UserProfiles } from "@/models/index.js";
-import { getFullApAccount } from "@/misc/convert-host.js";
-import { checkWordMute, stringToAcct } from "backend-rs";
+import { checkWordMute, getFullApAccount, stringToAcct } from "backend-rs";
 import type { Packed } from "@/misc/schema.js";
 import { Cache } from "@/misc/cache.js";
 
diff --git a/packages/backend/src/misc/convert-host.ts b/packages/backend/src/misc/convert-host.ts
deleted file mode 100644
index 65cc3ca1f5..0000000000
--- a/packages/backend/src/misc/convert-host.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { URL } from "node:url";
-import config from "@/config/index.js";
-import { toASCII } from "punycode";
-import Logger from "@/services/logger.js";
-import { inspect } from "node:util";
-
-const logger = new Logger("convert-host");
-
-export function getFullApAccount(username: string, host: string | null) {
-	return host
-		? `${username}@${toPuny(host)}`
-		: `${username}@${toPuny(config.host)}`;
-}
-
-export function isSelfHost(host: string) {
-	if (host == null) return true;
-	return toPuny(config.host) === toPuny(host);
-}
-
-export function isSameOrigin(src: unknown): boolean | null {
-	if (typeof src !== "string") {
-		logger.debug(`unknown origin: ${inspect(src)}`);
-		return null;
-	}
-	try {
-		const u = new URL(src);
-		return u.origin === config.url;
-	} catch (e) {
-		logger.debug(inspect(e));
-		return false;
-	}
-}
-
-export function extractDbHost(uri: string) {
-	const url = new URL(uri);
-	return toPuny(url.hostname);
-}
-
-export function toPuny(host: string) {
-	return toASCII(host.toLowerCase());
-}
-
-export function toPunyNullable(host: string | null | undefined): string | null {
-	if (host == null) return null;
-	return toASCII(host.toLowerCase());
-}
diff --git a/packages/backend/src/misc/populate-emojis.ts b/packages/backend/src/misc/populate-emojis.ts
index 45e36649a1..f18b23c9a4 100644
--- a/packages/backend/src/misc/populate-emojis.ts
+++ b/packages/backend/src/misc/populate-emojis.ts
@@ -3,7 +3,7 @@ import { Emojis } from "@/models/index.js";
 import type { Emoji } from "@/models/entities/emoji.js";
 import type { Note } from "@/models/entities/note.js";
 import { Cache } from "./cache.js";
-import { isSelfHost, toPunyNullable } from "./convert-host.js";
+import { isSelfHost, toPuny } from "backend-rs";
 import { decodeReaction } from "./reaction-lib.js";
 import config from "@/config/index.js";
 import { query } from "@/prelude/url.js";
@@ -27,7 +27,7 @@ function normalizeHost(
 	noteUserHost: string | null,
 ): string | null {
 	// クエリに使うホスト
-	let host =
+	const host =
 		src === "."
 			? null // .はローカルホスト (ここがマッチするのはリアクションのみ)
 			: src === undefined
@@ -36,9 +36,7 @@ function normalizeHost(
 					? null // 自ホスト指定
 					: src || noteUserHost; // 指定されたホスト || ノートなどの所有者のホスト (こっちがリアクションにマッチすることはない)
 
-	host = toPunyNullable(host);
-
-	return host;
+	return host == null ? null : toPuny(host);
 }
 
 function parseEmojiStr(emojiName: string, noteUserHost: string | null) {
@@ -46,11 +44,7 @@ function parseEmojiStr(emojiName: string, noteUserHost: string | null) {
 	if (!match) return { name: null, host: null };
 
 	const name = match[1];
-
-	// ホスト正規化
-	const host = toPunyNullable(normalizeHost(match[2], noteUserHost));
-
-	return { name, host };
+	return { name, host: normalizeHost(match[2], noteUserHost) };
 }
 
 /**
diff --git a/packages/backend/src/misc/reaction-lib.ts b/packages/backend/src/misc/reaction-lib.ts
index db88b05900..4304d3b9e1 100644
--- a/packages/backend/src/misc/reaction-lib.ts
+++ b/packages/backend/src/misc/reaction-lib.ts
@@ -1,7 +1,7 @@
 import { emojiRegex } from "./emoji-regex.js";
 import { fetchMeta } from "./fetch-meta.js";
 import { Emojis } from "@/models/index.js";
-import { toPunyNullable } from "./convert-host.js";
+import { toPuny } from "backend-rs";
 import { IsNull } from "typeorm";
 
 export function convertReactions(reactions: Record<string, number>) {
@@ -23,7 +23,7 @@ export async function toDbReaction(
 ): Promise<string> {
 	if (!reaction) return (await fetchMeta()).defaultReaction;
 
-	reacterHost = toPunyNullable(reacterHost);
+	reacterHost = reacterHost == null ? null : toPuny(reacterHost);
 
 	if (reaction.includes("❤") || reaction.includes("♥️")) return "❤️";
 
diff --git a/packages/backend/src/models/repositories/drive-file.ts b/packages/backend/src/models/repositories/drive-file.ts
index b550745c5b..2321f20d4c 100644
--- a/packages/backend/src/models/repositories/drive-file.ts
+++ b/packages/backend/src/models/repositories/drive-file.ts
@@ -1,7 +1,7 @@
 import { db } from "@/db/postgre.js";
 import { DriveFile } from "@/models/entities/drive-file.js";
 import type { User } from "@/models/entities/user.js";
-import { toPuny } from "@/misc/convert-host.js";
+import { toPuny } from "backend-rs";
 import { awaitAll } from "@/prelude/await-all.js";
 import type { Packed } from "@/misc/schema.js";
 import config from "@/config/index.js";
diff --git a/packages/backend/src/queue/processors/db/export-blocking.ts b/packages/backend/src/queue/processors/db/export-blocking.ts
index 90da76b872..4fd222b3ef 100644
--- a/packages/backend/src/queue/processors/db/export-blocking.ts
+++ b/packages/backend/src/queue/processors/db/export-blocking.ts
@@ -4,7 +4,7 @@ import * as fs from "node:fs";
 import { queueLogger } from "../../logger.js";
 import { addFile } from "@/services/drive/add-file.js";
 import { format as dateFormat } from "date-fns";
-import { getFullApAccount } from "@/misc/convert-host.js";
+import { getFullApAccount } from "backend-rs";
 import { createTemp } from "@/misc/create-temp.js";
 import { Users, Blockings } from "@/models/index.js";
 import { MoreThan } from "typeorm";
diff --git a/packages/backend/src/queue/processors/db/export-following.ts b/packages/backend/src/queue/processors/db/export-following.ts
index 552f8d40c0..65d3673e70 100644
--- a/packages/backend/src/queue/processors/db/export-following.ts
+++ b/packages/backend/src/queue/processors/db/export-following.ts
@@ -4,7 +4,7 @@ import * as fs from "node:fs";
 import { queueLogger } from "../../logger.js";
 import { addFile } from "@/services/drive/add-file.js";
 import { format as dateFormat } from "date-fns";
-import { getFullApAccount } from "@/misc/convert-host.js";
+import { getFullApAccount } from "backend-rs";
 import { createTemp } from "@/misc/create-temp.js";
 import { Users, Followings, Mutings } from "@/models/index.js";
 import { In, MoreThan, Not } from "typeorm";
diff --git a/packages/backend/src/queue/processors/db/export-mute.ts b/packages/backend/src/queue/processors/db/export-mute.ts
index 87b140b762..f7906ac9f0 100644
--- a/packages/backend/src/queue/processors/db/export-mute.ts
+++ b/packages/backend/src/queue/processors/db/export-mute.ts
@@ -4,7 +4,7 @@ import * as fs from "node:fs";
 import { queueLogger } from "../../logger.js";
 import { addFile } from "@/services/drive/add-file.js";
 import { format as dateFormat } from "date-fns";
-import { getFullApAccount } from "@/misc/convert-host.js";
+import { getFullApAccount } from "backend-rs";
 import { createTemp } from "@/misc/create-temp.js";
 import { Users, Mutings } from "@/models/index.js";
 import { IsNull, MoreThan } from "typeorm";
diff --git a/packages/backend/src/queue/processors/db/export-user-lists.ts b/packages/backend/src/queue/processors/db/export-user-lists.ts
index e0c9cd8f3f..e6877f31fc 100644
--- a/packages/backend/src/queue/processors/db/export-user-lists.ts
+++ b/packages/backend/src/queue/processors/db/export-user-lists.ts
@@ -4,7 +4,7 @@ import * as fs from "node:fs";
 import { queueLogger } from "../../logger.js";
 import { addFile } from "@/services/drive/add-file.js";
 import { format as dateFormat } from "date-fns";
-import { getFullApAccount } from "@/misc/convert-host.js";
+import { getFullApAccount } from "backend-rs";
 import { createTemp } from "@/misc/create-temp.js";
 import { Users, UserLists, UserListJoinings } from "@/models/index.js";
 import { In } from "typeorm";
diff --git a/packages/backend/src/queue/processors/db/import-blocking.ts b/packages/backend/src/queue/processors/db/import-blocking.ts
index bb1920d2a8..e933b60783 100644
--- a/packages/backend/src/queue/processors/db/import-blocking.ts
+++ b/packages/backend/src/queue/processors/db/import-blocking.ts
@@ -1,10 +1,9 @@
 import type Bull from "bull";
 
 import { queueLogger } from "../../logger.js";
-import { stringToAcct } from "backend-rs";
+import { isSelfHost, stringToAcct, toPuny } from "backend-rs";
 import { resolveUser } from "@/remote/resolve-user.js";
 import { downloadTextFile } from "@/misc/download-text-file.js";
-import { isSelfHost, toPuny } from "@/misc/convert-host.js";
 import { Users, DriveFiles } from "@/models/index.js";
 import type { DbUserImportJobData } from "@/queue/types.js";
 import block from "@/services/blocking/create.js";
diff --git a/packages/backend/src/queue/processors/db/import-following.ts b/packages/backend/src/queue/processors/db/import-following.ts
index f7aa5afedd..77017fa9ff 100644
--- a/packages/backend/src/queue/processors/db/import-following.ts
+++ b/packages/backend/src/queue/processors/db/import-following.ts
@@ -1,10 +1,9 @@
 import { IsNull } from "typeorm";
 import follow from "@/services/following/create.js";
 
-import { stringToAcct } from "backend-rs";
+import { isSelfHost, stringToAcct, toPuny } from "backend-rs";
 import { resolveUser } from "@/remote/resolve-user.js";
 import { downloadTextFile } from "@/misc/download-text-file.js";
-import { isSelfHost, toPuny } from "@/misc/convert-host.js";
 import { Users, DriveFiles } from "@/models/index.js";
 import type { DbUserImportJobData } from "@/queue/types.js";
 import { queueLogger } from "../../logger.js";
diff --git a/packages/backend/src/queue/processors/db/import-muting.ts b/packages/backend/src/queue/processors/db/import-muting.ts
index cdd8eab88d..b0d8f40956 100644
--- a/packages/backend/src/queue/processors/db/import-muting.ts
+++ b/packages/backend/src/queue/processors/db/import-muting.ts
@@ -3,11 +3,10 @@ import type Bull from "bull";
 import { queueLogger } from "../../logger.js";
 import { resolveUser } from "@/remote/resolve-user.js";
 import { downloadTextFile } from "@/misc/download-text-file.js";
-import { isSelfHost, toPuny } from "@/misc/convert-host.js";
 import { Users, DriveFiles, Mutings } from "@/models/index.js";
 import type { DbUserImportJobData } from "@/queue/types.js";
 import type { User } from "@/models/entities/user.js";
-import { genId, stringToAcct } from "backend-rs";
+import { genId, isSelfHost, stringToAcct, toPuny } from "backend-rs";
 import { IsNull } from "typeorm";
 import { inspect } from "node:util";
 
diff --git a/packages/backend/src/queue/processors/db/import-user-lists.ts b/packages/backend/src/queue/processors/db/import-user-lists.ts
index 9995b0d0a3..2bdecfd389 100644
--- a/packages/backend/src/queue/processors/db/import-user-lists.ts
+++ b/packages/backend/src/queue/processors/db/import-user-lists.ts
@@ -4,14 +4,13 @@ import { queueLogger } from "../../logger.js";
 import { resolveUser } from "@/remote/resolve-user.js";
 import { pushUserToUserList } from "@/services/user-list/push.js";
 import { downloadTextFile } from "@/misc/download-text-file.js";
-import { isSelfHost, toPuny } from "@/misc/convert-host.js";
 import {
 	DriveFiles,
 	Users,
 	UserLists,
 	UserListJoinings,
 } from "@/models/index.js";
-import { genId, stringToAcct } from "backend-rs";
+import { genId, isSelfHost, stringToAcct, toPuny } from "backend-rs";
 import type { DbUserImportJobData } from "@/queue/types.js";
 import { IsNull } from "typeorm";
 import { inspect } from "node:util";
diff --git a/packages/backend/src/queue/processors/deliver.ts b/packages/backend/src/queue/processors/deliver.ts
index 45c2d3dc95..2abadeb9c4 100644
--- a/packages/backend/src/queue/processors/deliver.ts
+++ b/packages/backend/src/queue/processors/deliver.ts
@@ -4,7 +4,7 @@ import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instanc
 import Logger from "@/services/logger.js";
 import { Instances } from "@/models/index.js";
 import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
-import { toPuny } from "@/misc/convert-host.js";
+import { toPuny } from "backend-rs";
 import { StatusError } from "@/misc/fetch.js";
 import { shouldSkipInstance } from "@/misc/skipped-instances.js";
 import type { DeliverJobData } from "@/queue/types.js";
diff --git a/packages/backend/src/queue/processors/inbox.ts b/packages/backend/src/queue/processors/inbox.ts
index b0e1089246..46f9939a41 100644
--- a/packages/backend/src/queue/processors/inbox.ts
+++ b/packages/backend/src/queue/processors/inbox.ts
@@ -6,7 +6,7 @@ import Logger from "@/services/logger.js";
 import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js";
 import { Instances } from "@/models/index.js";
 import { fetchMeta } from "@/misc/fetch-meta.js";
-import { toPuny, extractDbHost } from "@/misc/convert-host.js";
+import { toPuny, extractHost } from "backend-rs";
 import { getApId } from "@/remote/activitypub/type.js";
 import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
 import type { InboxJobData } from "../types.js";
@@ -157,7 +157,7 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
 			}
 
 			// ブロックしてたら中断
-			const ldHost = extractDbHost(authUser.user.uri);
+			const ldHost = extractHost(authUser.user.uri);
 			if (await shouldBlockInstance(ldHost, meta)) {
 				return `Blocked request: ${ldHost}`;
 			}
@@ -168,8 +168,8 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
 
 	// activity.idがあればホストが署名者のホストであることを確認する
 	if (typeof activity.id === "string") {
-		const signerHost = extractDbHost(authUser.user.uri!);
-		const activityIdHost = extractDbHost(activity.id);
+		const signerHost = extractHost(authUser.user.uri!);
+		const activityIdHost = extractHost(activity.id);
 		if (signerHost !== activityIdHost) {
 			return `skip: signerHost(${signerHost}) !== activity.id host(${activityIdHost}`;
 		}
diff --git a/packages/backend/src/remote/activitypub/check-fetch.ts b/packages/backend/src/remote/activitypub/check-fetch.ts
index a170e3eb3a..c0d40278ee 100644
--- a/packages/backend/src/remote/activitypub/check-fetch.ts
+++ b/packages/backend/src/remote/activitypub/check-fetch.ts
@@ -2,7 +2,7 @@ import { URL } from "url";
 import httpSignature, { IParsedSignature } from "@peertube/http-signature";
 import config from "@/config/index.js";
 import { fetchMeta } from "@/misc/fetch-meta.js";
-import { toPuny } from "@/misc/convert-host.js";
+import { toPuny } from "backend-rs";
 import DbResolver from "@/remote/activitypub/db-resolver.js";
 import { getApId } from "@/remote/activitypub/type.js";
 import { shouldBlockInstance } from "@/misc/should-block-instance.js";
diff --git a/packages/backend/src/remote/activitypub/kernel/announce/note.ts b/packages/backend/src/remote/activitypub/kernel/announce/note.ts
index cc33b681eb..ae16c77dbd 100644
--- a/packages/backend/src/remote/activitypub/kernel/announce/note.ts
+++ b/packages/backend/src/remote/activitypub/kernel/announce/note.ts
@@ -5,7 +5,7 @@ import type { IAnnounce } from "../../type.js";
 import { getApId } from "../../type.js";
 import { fetchNote, resolveNote } from "../../models/note.js";
 import { apLogger } from "../../logger.js";
-import { extractDbHost } from "@/misc/convert-host.js";
+import { extractHost } from "backend-rs";
 import { getApLock } from "@/misc/app-lock.js";
 import { parseAudience } from "../../audience.js";
 import { StatusError } from "@/misc/fetch.js";
@@ -31,7 +31,7 @@ export default async function (
 	}
 
 	// Interrupt if you block the announcement destination
-	if (await shouldBlockInstance(extractDbHost(uri))) return;
+	if (await shouldBlockInstance(extractHost(uri))) return;
 
 	const lock = await getApLock(uri);
 
diff --git a/packages/backend/src/remote/activitypub/kernel/create/note.ts b/packages/backend/src/remote/activitypub/kernel/create/note.ts
index 92b0ffb1e0..512972f63a 100644
--- a/packages/backend/src/remote/activitypub/kernel/create/note.ts
+++ b/packages/backend/src/remote/activitypub/kernel/create/note.ts
@@ -4,7 +4,7 @@ import { createNote, fetchNote } from "../../models/note.js";
 import type { IObject, ICreate } from "../../type.js";
 import { getApId } from "../../type.js";
 import { getApLock } from "@/misc/app-lock.js";
-import { extractDbHost } from "@/misc/convert-host.js";
+import { extractHost } from "backend-rs";
 import { StatusError } from "@/misc/fetch.js";
 
 /**
@@ -25,7 +25,7 @@ export default async function (
 		}
 
 		if (typeof note.id === "string") {
-			if (extractDbHost(actor.uri) !== extractDbHost(note.id)) {
+			if (extractHost(actor.uri) !== extractHost(note.id)) {
 				return "skip: host in actor.uri !== note.id";
 			}
 		}
diff --git a/packages/backend/src/remote/activitypub/kernel/index.ts b/packages/backend/src/remote/activitypub/kernel/index.ts
index aa99f6c320..f64a0e2ee8 100644
--- a/packages/backend/src/remote/activitypub/kernel/index.ts
+++ b/packages/backend/src/remote/activitypub/kernel/index.ts
@@ -38,7 +38,7 @@ import block from "./block/index.js";
 import flag from "./flag/index.js";
 import move from "./move/index.js";
 import type { IObject, IActivity } from "../type.js";
-import { extractDbHost } from "@/misc/convert-host.js";
+import { extractHost } from "backend-rs";
 import { shouldBlockInstance } from "@/misc/should-block-instance.js";
 import { inspect } from "node:util";
 
@@ -70,7 +70,7 @@ async function performOneActivity(
 	if (actor.isSuspended) return;
 
 	if (typeof activity.id !== "undefined") {
-		const host = extractDbHost(getApId(activity));
+		const host = extractHost(getApId(activity));
 		if (await shouldBlockInstance(host)) return;
 	}
 
diff --git a/packages/backend/src/remote/activitypub/kernel/read.ts b/packages/backend/src/remote/activitypub/kernel/read.ts
index 53fa7fe63b..bdcb2e8b76 100644
--- a/packages/backend/src/remote/activitypub/kernel/read.ts
+++ b/packages/backend/src/remote/activitypub/kernel/read.ts
@@ -1,7 +1,7 @@
 import type { CacheableRemoteUser } from "@/models/entities/user.js";
 import type { IRead } from "../type.js";
 import { getApId } from "../type.js";
-import { isSelfHost, extractDbHost } from "@/misc/convert-host.js";
+import { isSelfHost, extractHost } from "backend-rs";
 import { MessagingMessages } from "@/models/index.js";
 import { readUserMessagingMessage } from "@/server/api/common/read-messaging-message.js";
 
@@ -11,7 +11,7 @@ export const performReadActivity = async (
 ): Promise<string> => {
 	const id = await getApId(activity.object);
 
-	if (!isSelfHost(extractDbHost(id))) {
+	if (!isSelfHost(extractHost(id))) {
 		return `skip: Read to foreign host (${id})`;
 	}
 
diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts
index 7d1d9d5aee..ad59930457 100644
--- a/packages/backend/src/remote/activitypub/models/note.ts
+++ b/packages/backend/src/remote/activitypub/models/note.ts
@@ -13,7 +13,7 @@ import { extractPollFromQuestion } from "./question.js";
 import vote from "@/services/note/polls/vote.js";
 import { apLogger } from "../logger.js";
 import { DriveFile } from "@/models/entities/drive-file.js";
-import { extractDbHost, isSameOrigin, toPuny } from "@/misc/convert-host.js";
+import { extractHost, isSameOrigin, toPuny } from "backend-rs";
 import {
 	Emojis,
 	Polls,
@@ -54,7 +54,7 @@ import { inspect } from "node:util";
 const logger = apLogger;
 
 export function validateNote(object: any, uri: string) {
-	const expectHost = extractDbHost(uri);
+	const expectHost = extractHost(uri);
 
 	if (object == null) {
 		return new Error("invalid Note: object is null");
@@ -64,9 +64,9 @@ export function validateNote(object: any, uri: string) {
 		return new Error(`invalid Note: invalid object type ${getApType(object)}`);
 	}
 
-	if (object.id && extractDbHost(object.id) !== expectHost) {
+	if (object.id && extractHost(object.id) !== expectHost) {
 		return new Error(
-			`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractDbHost(
+			`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractHost(
 				object.id,
 			)}`,
 		);
@@ -74,10 +74,10 @@ export function validateNote(object: any, uri: string) {
 
 	if (
 		object.attributedTo &&
-		extractDbHost(getOneApId(object.attributedTo)) !== expectHost
+		extractHost(getOneApId(object.attributedTo)) !== expectHost
 	) {
 		return new Error(
-			`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractDbHost(
+			`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractHost(
 				object.attributedTo,
 			)}`,
 		);
@@ -420,11 +420,11 @@ export async function resolveNote(
 	if (uri == null) throw new Error("missing uri");
 
 	// Abort if origin host is blocked
-	if (await shouldBlockInstance(extractDbHost(uri)))
+	if (await shouldBlockInstance(extractHost(uri)))
 		throw new StatusError(
 			"host blocked",
 			451,
-			`host ${extractDbHost(uri)} is blocked`,
+			`host ${extractHost(uri)} is blocked`,
 		);
 
 	const lock = await getApLock(uri);
diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts
index d564275f3f..e91280125f 100644
--- a/packages/backend/src/remote/activitypub/models/person.ts
+++ b/packages/backend/src/remote/activitypub/models/person.ts
@@ -1,7 +1,6 @@
 import { URL } from "node:url";
 import promiseLimit from "promise-limit";
 
-import config from "@/config/index.js";
 import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js";
 import type { Note } from "@/models/entities/note.js";
 import { updateUsertags } from "@/services/update-hashtag.js";
@@ -19,7 +18,7 @@ import { UserNotePining } from "@/models/entities/user-note-pining.js";
 import { genId } from "backend-rs";
 import { UserPublickey } from "@/models/entities/user-publickey.js";
 import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js";
-import { isSameOrigin, toPuny } from "@/misc/convert-host.js";
+import { isSameOrigin, toPuny } from "backend-rs";
 import { UserProfile } from "@/models/entities/user-profile.js";
 import { toArray } from "@/prelude/array.js";
 import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
@@ -164,8 +163,6 @@ export async function createPerson(
 	uri: string,
 	resolver?: Resolver,
 ): Promise<User> {
-	if (typeof uri !== "string") throw new Error("uri is not string");
-
 	if (isSameOrigin(uri)) {
 		throw new StatusError(
 			"cannot resolve local user",
diff --git a/packages/backend/src/remote/activitypub/models/question.ts b/packages/backend/src/remote/activitypub/models/question.ts
index 98d2f27a58..292e220dee 100644
--- a/packages/backend/src/remote/activitypub/models/question.ts
+++ b/packages/backend/src/remote/activitypub/models/question.ts
@@ -4,7 +4,7 @@ import { getApId, isQuestion } from "../type.js";
 import { apLogger } from "../logger.js";
 import { Notes, Polls } from "@/models/index.js";
 import type { IPoll } from "@/models/entities/poll.js";
-import { isSameOrigin } from "@/misc/convert-host.js";
+import { isSameOrigin } from "backend-rs";
 
 export async function extractPollFromQuestion(
 	source: string | IObject,
diff --git a/packages/backend/src/remote/activitypub/resolver.ts b/packages/backend/src/remote/activitypub/resolver.ts
index 7c0bd6e102..973f12cdc2 100644
--- a/packages/backend/src/remote/activitypub/resolver.ts
+++ b/packages/backend/src/remote/activitypub/resolver.ts
@@ -2,7 +2,7 @@ import config from "@/config/index.js";
 import type { ILocalUser } from "@/models/entities/user.js";
 import { getInstanceActor } from "@/services/instance-actor.js";
 import { fetchMeta } from "@/misc/fetch-meta.js";
-import { extractDbHost, isSelfHost } from "@/misc/convert-host.js";
+import { extractHost, isSelfHost } from "backend-rs";
 import { apGet } from "./request.js";
 import type { IObject, ICollection, IOrderedCollection } from "./type.js";
 import { isCollectionOrOrderedCollection, getApId } from "./type.js";
@@ -68,7 +68,7 @@ export default class Resolver {
 		if (typeof value !== "string") {
 			apLogger.debug("Object to resolve is not a string");
 			if (typeof value.id !== "undefined") {
-				const host = extractDbHost(getApId(value));
+				const host = extractHost(getApId(value));
 				if (await shouldBlockInstance(host)) {
 					throw new Error("instance is blocked");
 				}
@@ -95,7 +95,7 @@ export default class Resolver {
 		}
 		this.history.add(value);
 
-		const host = extractDbHost(value);
+		const host = extractHost(value);
 		if (isSelfHost(host)) {
 			return await this.resolveLocal(value);
 		}
diff --git a/packages/backend/src/remote/resolve-user.ts b/packages/backend/src/remote/resolve-user.ts
index f73b670273..0883386371 100644
--- a/packages/backend/src/remote/resolve-user.ts
+++ b/packages/backend/src/remote/resolve-user.ts
@@ -4,7 +4,7 @@ import { IsNull } from "typeorm";
 import config from "@/config/index.js";
 import type { User, IRemoteUser } from "@/models/entities/user.js";
 import { Users } from "@/models/index.js";
-import { toPuny } from "@/misc/convert-host.js";
+import { toPuny } from "backend-rs";
 import webFinger from "./webfinger.js";
 import { createPerson, updatePerson } from "./activitypub/models/person.js";
 import { remoteLogger } from "./logger.js";
diff --git a/packages/backend/src/server/activitypub.ts b/packages/backend/src/server/activitypub.ts
index 7c437d3d6d..7e5fb5a281 100644
--- a/packages/backend/src/server/activitypub.ts
+++ b/packages/backend/src/server/activitypub.ts
@@ -9,7 +9,7 @@ import renderKey from "@/remote/activitypub/renderer/key.js";
 import { renderPerson } from "@/remote/activitypub/renderer/person.js";
 import renderEmoji from "@/remote/activitypub/renderer/emoji.js";
 import { inbox as processInbox } from "@/queue/index.js";
-import { isSelfHost } from "@/misc/convert-host.js";
+import { isSelfHost } from "backend-rs";
 import {
 	Notes,
 	Users,
diff --git a/packages/backend/src/server/api/common/signup.ts b/packages/backend/src/server/api/common/signup.ts
index 7a8506b099..a267baf9c0 100644
--- a/packages/backend/src/server/api/common/signup.ts
+++ b/packages/backend/src/server/api/common/signup.ts
@@ -4,8 +4,7 @@ import { User } from "@/models/entities/user.js";
 import { Users, UsedUsernames } from "@/models/index.js";
 import { UserProfile } from "@/models/entities/user-profile.js";
 import { IsNull } from "typeorm";
-import { genId } from "backend-rs";
-import { toPunyNullable } from "@/misc/convert-host.js";
+import { genId, toPuny } from "backend-rs";
 import { UserKeypair } from "@/models/entities/user-keypair.js";
 import { UsedUsername } from "@/models/entities/used-username.js";
 import { db } from "@/db/postgre.js";
@@ -100,7 +99,7 @@ export async function signup(opts: {
 				createdAt: new Date(),
 				username: username,
 				usernameLower: username.toLowerCase(),
-				host: toPunyNullable(host),
+				host: host == null ? null : toPuny(host),
 				token: secret,
 				isAdmin:
 					(await Users.countBy({
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 f6a88b8366..5c3e19d9e0 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
@@ -1,6 +1,6 @@
 import define from "@/server/api/define.js";
 import { Emojis } from "@/models/index.js";
-import { toPuny } from "@/misc/convert-host.js";
+import { toPuny } from "backend-rs";
 import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
 import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
 import { ApiError } from "@/server/api/error.js";
diff --git a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts
index bf71cd2503..d4fb8c6e73 100644
--- a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts
+++ b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts
@@ -1,6 +1,6 @@
 import define from "@/server/api/define.js";
 import { Instances } from "@/models/index.js";
-import { toPuny } from "@/misc/convert-host.js";
+import { toPuny } from "backend-rs";
 import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
 
 export const meta = {
diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
index 653d187c2d..2ebad12ab7 100644
--- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
+++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
@@ -1,6 +1,6 @@
 import define from "@/server/api/define.js";
 import { Instances } from "@/models/index.js";
-import { toPuny } from "@/misc/convert-host.js";
+import { toPuny } from "backend-rs";
 
 export const meta = {
 	tags: ["admin"],
diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts
index ec9209ecd3..f1d42d8d93 100644
--- a/packages/backend/src/server/api/endpoints/ap/show.ts
+++ b/packages/backend/src/server/api/endpoints/ap/show.ts
@@ -4,7 +4,7 @@ import { createNote } from "@/remote/activitypub/models/note.js";
 import DbResolver from "@/remote/activitypub/db-resolver.js";
 import Resolver from "@/remote/activitypub/resolver.js";
 import { ApiError } from "@/server/api/error.js";
-import { extractDbHost } from "@/misc/convert-host.js";
+import { extractHost } from "backend-rs";
 import { Users, Notes } from "@/models/index.js";
 import type { Note } from "@/models/entities/note.js";
 import type { CacheableLocalUser, User } from "@/models/entities/user.js";
@@ -101,7 +101,7 @@ async function fetchAny(
 	me: CacheableLocalUser | null | undefined,
 ): Promise<SchemaType<(typeof meta)["res"]> | null> {
 	// Wait if blocked.
-	if (await shouldBlockInstance(extractDbHost(uri))) return null;
+	if (await shouldBlockInstance(extractHost(uri))) return null;
 
 	const dbResolver = new DbResolver();
 
diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts
index c4a6304d05..809d3ee0b1 100644
--- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts
+++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts
@@ -1,6 +1,6 @@
 import define from "@/server/api/define.js";
 import { Instances } from "@/models/index.js";
-import { toPuny } from "@/misc/convert-host.js";
+import { toPuny } from "backend-rs";
 
 export const meta = {
 	tags: ["federation"],
diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts
index cec7a04b3c..ab1cac2442 100644
--- a/packages/backend/src/server/api/endpoints/users/followers.ts
+++ b/packages/backend/src/server/api/endpoints/users/followers.ts
@@ -1,6 +1,6 @@
 import { IsNull } from "typeorm";
 import { Users, Followings, UserProfiles } from "@/models/index.js";
-import { toPunyNullable } from "@/misc/convert-host.js";
+import { toPuny } from "backend-rs";
 import define from "@/server/api/define.js";
 import { ApiError } from "@/server/api/error.js";
 import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
@@ -80,7 +80,7 @@ export default define(meta, paramDef, async (ps, me) => {
 			? { id: ps.userId }
 			: {
 					usernameLower: ps.username!.toLowerCase(),
-					host: toPunyNullable(ps.host) ?? IsNull(),
+					host: ps.host == null ? IsNull() : toPuny(ps.host),
 				},
 	);
 
diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts
index 68e6cb6d06..05ab3602b0 100644
--- a/packages/backend/src/server/api/endpoints/users/following.ts
+++ b/packages/backend/src/server/api/endpoints/users/following.ts
@@ -1,6 +1,6 @@
 import { IsNull } from "typeorm";
 import { Users, Followings, UserProfiles } from "@/models/index.js";
-import { toPunyNullable } from "@/misc/convert-host.js";
+import { toPuny } from "backend-rs";
 import define from "@/server/api/define.js";
 import { ApiError } from "@/server/api/error.js";
 import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
@@ -79,7 +79,7 @@ export default define(meta, paramDef, async (ps, me) => {
 			? { id: ps.userId }
 			: {
 					usernameLower: ps.username!.toLowerCase(),
-					host: toPunyNullable(ps.host) ?? IsNull(),
+					host: ps.host == null ? IsNull() : toPuny(ps.host),
 				},
 	);
 
diff --git a/packages/backend/src/services/messages/create.ts b/packages/backend/src/services/messages/create.ts
index f727c1846f..257a132e6b 100644
--- a/packages/backend/src/services/messages/create.ts
+++ b/packages/backend/src/services/messages/create.ts
@@ -7,7 +7,7 @@ import {
 	Mutings,
 	Users,
 } from "@/models/index.js";
-import { genId } from "backend-rs";
+import { genId, toPuny } from "backend-rs";
 import type { MessagingMessage } from "@/models/entities/messaging-message.js";
 import {
 	publishMessagingStream,
@@ -22,7 +22,6 @@ import renderNote from "@/remote/activitypub/renderer/note.js";
 import renderCreate from "@/remote/activitypub/renderer/create.js";
 import { renderActivity } from "@/remote/activitypub/renderer/index.js";
 import { deliver } from "@/queue/index.js";
-import { toPuny } from "@/misc/convert-host.js";
 import { Instances } from "@/models/index.js";
 
 export async function createMessage(
diff --git a/packages/backend/src/services/register-or-fetch-instance-doc.ts b/packages/backend/src/services/register-or-fetch-instance-doc.ts
index a5481458a0..bb044db287 100644
--- a/packages/backend/src/services/register-or-fetch-instance-doc.ts
+++ b/packages/backend/src/services/register-or-fetch-instance-doc.ts
@@ -4,7 +4,7 @@ import {
 } from "@/models/entities/instance.js";
 import { Instances } from "@/models/index.js";
 import { genId } from "backend-rs";
-import { toPuny } from "@/misc/convert-host.js";
+import { toPuny } from "backend-rs";
 import { Cache } from "@/misc/cache.js";
 import Logger from "@/services/logger.js";