Loading plugins/README.md +21 −3 Changes for plugins/README.md: 21 added lines, 3 removed lines. Original line number Diff line number Diff line # Felis server-side plugins These are the in-cluster and edge plugins for Felis. The Velocity proxy and the three loader mods ship the in-game first leg of the §10 account-link flow: a player who is already online mods (these on a standalone online-mode server only, see the warning under the module table) ship the in-game first leg of the §10 account-link flow: a player who is already online (so Mojang has verified their UUID) runs `/link`; the plugin asks felis-api to mint a one-time code for that UUID and shows it in chat. The player then enters the code on the web console → **Account** page (the second leg), which binds the Loading Loading @@ -40,6 +41,21 @@ no `felis-fabric`/`felis-forge`/`felis-neoforge` jar anywhere. They build from t the commands under [Building](#building) and are deployed by hand; the account-link flow they carry works, but nothing installs them for you. > **The loader mods are for a standalone server only.** Inside a Felis network the proxy's > `/link` already serves every backend and shadows a backend's own, so user game servers need > no mod. A mod answers `/link` only when its server runs `online-mode=true`: an offline-mode > server is either behind a proxy (whose `/link` applies) or cracked, where the UUID is > whatever the client claims. > > **The token a mod holds is a real credential.** It mints a link code for any UUID the mod > asks about, so whoever can read that server's files — its operator, any plugin or mod on > it, a copied backup — can bind a not-yet-linked player's Minecraft account to their own web > account. Put a mod only on a server whose operator you would trust with that, and give it > the `limbo` token: it opens the link-code, link-status and blacklist routes and nothing > else. The `velocity` token also approves op-logins and wakes or claims servers for any > player; it stays on the proxy host. `sudo felis rotate-token limbo` replaces a leaked > token (the login gate restarts onto the new value; copy it to the mod by hand). ## Architecture Each platform is an **independent** Gradle build with its own `settings.gradle`, Loading Loading @@ -232,8 +248,10 @@ Velocity), then set `api-base-url` and `service-token` — or provide precedence. The token is one of felis-api's per-caller internal tokens: the Velocity proxy uses the `velocity` token (Secret `felis/felis-service-token`), the login gate the `limbo` token (`felis-limbo-token`), and each serves only its own routes (a token on another caller's route gets `403 wrong_caller`). Treat it as a secret; `sudo felis rotate-token <caller>` replaces it. routes (a token on another caller's route gets `403 wrong_caller`). A loader mod takes the `limbo` token, on a standalone online-mode server only (see the warning under the module table). Treat it as a secret; `sudo felis rotate-token <caller>` replaces it. On **Velocity**, also set `root-domain` (and optionally `lobby-server`) in the same file to turn on §11 routing, and make sure `online-mode=true` in Loading plugins/fabric/src/main/java/best/lolicon/felis/fabric/FelisFabricMod.java +6 −0 Changes for plugins/fabric/src/main/java/best/lolicon/felis/fabric/FelisFabricMod.java: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import best.lolicon.felis.link.LinkCode; import best.lolicon.felis.link.LinkConfig; import best.lolicon.felis.link.LinkConfigLoader; import best.lolicon.felis.link.LinkException; import best.lolicon.felis.link.ModLinkPolicy; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.exceptions.CommandSyntaxException; Loading Loading @@ -68,6 +69,11 @@ public final class FelisFabricMod implements DedicatedServerModInitializer { source.sendFailure(Component.literal("/link 只能由玩家执行 / /link can only be run by a player.")); return 0; } if (!source.getServer().usesAuthentication()) { LOGGER.warn(ModLinkPolicy.OFFLINE_LOG); source.sendFailure(Component.literal(ModLinkPolicy.OFFLINE_REPLY)); return 0; } requestAndReply(source.getServer(), player); return 1; })); Loading plugins/forge/src/main/java/best/lolicon/felis/forge/FelisForgeMod.java +6 −0 Changes for plugins/forge/src/main/java/best/lolicon/felis/forge/FelisForgeMod.java: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import best.lolicon.felis.link.LinkCode; import best.lolicon.felis.link.LinkConfig; import best.lolicon.felis.link.LinkConfigLoader; import best.lolicon.felis.link.LinkException; import best.lolicon.felis.link.ModLinkPolicy; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.exceptions.CommandSyntaxException; Loading Loading @@ -73,6 +74,11 @@ public final class FelisForgeMod { source.sendFailure(Component.literal("/link 只能由玩家执行 / /link can only be run by a player.")); return 0; } if (!source.getServer().usesAuthentication()) { LOGGER.warn(ModLinkPolicy.OFFLINE_LOG); source.sendFailure(Component.literal(ModLinkPolicy.OFFLINE_REPLY)); return 0; } requestAndReply(source.getServer(), player); return 1; })); Loading plugins/neoforge/src/main/java/best/lolicon/felis/neoforge/FelisNeoForgeMod.java +6 −0 Changes for plugins/neoforge/src/main/java/best/lolicon/felis/neoforge/FelisNeoForgeMod.java: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import best.lolicon.felis.link.LinkCode; import best.lolicon.felis.link.LinkConfig; import best.lolicon.felis.link.LinkConfigLoader; import best.lolicon.felis.link.LinkException; import best.lolicon.felis.link.ModLinkPolicy; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.exceptions.CommandSyntaxException; Loading Loading @@ -77,6 +78,11 @@ public final class FelisNeoForgeMod { source.sendFailure(Component.literal("/link 只能由玩家执行 / /link can only be run by a player.")); return 0; } if (!source.getServer().usesAuthentication()) { LOGGER.warn(ModLinkPolicy.OFFLINE_LOG); source.sendFailure(Component.literal(ModLinkPolicy.OFFLINE_REPLY)); return 0; } requestAndReply(source.getServer(), player); return 1; })); Loading plugins/shared/src/main/java/best/lolicon/felis/link/ModLinkPolicy.java 0 → 100644 +24 −0 Changes for plugins/shared/src/main/java/best/lolicon/felis/link/ModLinkPolicy.java: 24 added lines, 0 removed lines. Original line number Diff line number Diff line package best.lolicon.felis.link; /** * ModLinkPolicy is what the Fabric, Forge and NeoForge mods say when they refuse * {@code /link}. A mod mints a link code for the UUID its server reports, and only an * online-mode server has checked that UUID with Mojang. Behind a proxy the backend runs * offline-mode and the proxy's own {@code /link} already serves every backend; on a * cracked server the UUID is whatever the client claims, so a code minted there would * let anyone link someone else's Minecraft account. The mods therefore link only on a * standalone online-mode server. */ public final class ModLinkPolicy { /** OFFLINE_REPLY is the chat line a player gets on an offline-mode server. */ public static final String OFFLINE_REPLY = "此服务器未开启正版验证,不能在这里绑定 / This server runs with online-mode off, so /link is unavailable here."; /** OFFLINE_LOG is the server-log line for the same refusal. */ public static final String OFFLINE_LOG = "Felis link: refused /link because online-mode is off. The mod links only on a standalone " + "online-mode server; behind the Felis proxy, the proxy serves /link."; private ModLinkPolicy() { } } Loading
plugins/README.md +21 −3 Changes for plugins/README.md: 21 added lines, 3 removed lines. Original line number Diff line number Diff line # Felis server-side plugins These are the in-cluster and edge plugins for Felis. The Velocity proxy and the three loader mods ship the in-game first leg of the §10 account-link flow: a player who is already online mods (these on a standalone online-mode server only, see the warning under the module table) ship the in-game first leg of the §10 account-link flow: a player who is already online (so Mojang has verified their UUID) runs `/link`; the plugin asks felis-api to mint a one-time code for that UUID and shows it in chat. The player then enters the code on the web console → **Account** page (the second leg), which binds the Loading Loading @@ -40,6 +41,21 @@ no `felis-fabric`/`felis-forge`/`felis-neoforge` jar anywhere. They build from t the commands under [Building](#building) and are deployed by hand; the account-link flow they carry works, but nothing installs them for you. > **The loader mods are for a standalone server only.** Inside a Felis network the proxy's > `/link` already serves every backend and shadows a backend's own, so user game servers need > no mod. A mod answers `/link` only when its server runs `online-mode=true`: an offline-mode > server is either behind a proxy (whose `/link` applies) or cracked, where the UUID is > whatever the client claims. > > **The token a mod holds is a real credential.** It mints a link code for any UUID the mod > asks about, so whoever can read that server's files — its operator, any plugin or mod on > it, a copied backup — can bind a not-yet-linked player's Minecraft account to their own web > account. Put a mod only on a server whose operator you would trust with that, and give it > the `limbo` token: it opens the link-code, link-status and blacklist routes and nothing > else. The `velocity` token also approves op-logins and wakes or claims servers for any > player; it stays on the proxy host. `sudo felis rotate-token limbo` replaces a leaked > token (the login gate restarts onto the new value; copy it to the mod by hand). ## Architecture Each platform is an **independent** Gradle build with its own `settings.gradle`, Loading Loading @@ -232,8 +248,10 @@ Velocity), then set `api-base-url` and `service-token` — or provide precedence. The token is one of felis-api's per-caller internal tokens: the Velocity proxy uses the `velocity` token (Secret `felis/felis-service-token`), the login gate the `limbo` token (`felis-limbo-token`), and each serves only its own routes (a token on another caller's route gets `403 wrong_caller`). Treat it as a secret; `sudo felis rotate-token <caller>` replaces it. routes (a token on another caller's route gets `403 wrong_caller`). A loader mod takes the `limbo` token, on a standalone online-mode server only (see the warning under the module table). Treat it as a secret; `sudo felis rotate-token <caller>` replaces it. On **Velocity**, also set `root-domain` (and optionally `lobby-server`) in the same file to turn on §11 routing, and make sure `online-mode=true` in Loading
plugins/fabric/src/main/java/best/lolicon/felis/fabric/FelisFabricMod.java +6 −0 Changes for plugins/fabric/src/main/java/best/lolicon/felis/fabric/FelisFabricMod.java: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import best.lolicon.felis.link.LinkCode; import best.lolicon.felis.link.LinkConfig; import best.lolicon.felis.link.LinkConfigLoader; import best.lolicon.felis.link.LinkException; import best.lolicon.felis.link.ModLinkPolicy; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.exceptions.CommandSyntaxException; Loading Loading @@ -68,6 +69,11 @@ public final class FelisFabricMod implements DedicatedServerModInitializer { source.sendFailure(Component.literal("/link 只能由玩家执行 / /link can only be run by a player.")); return 0; } if (!source.getServer().usesAuthentication()) { LOGGER.warn(ModLinkPolicy.OFFLINE_LOG); source.sendFailure(Component.literal(ModLinkPolicy.OFFLINE_REPLY)); return 0; } requestAndReply(source.getServer(), player); return 1; })); Loading
plugins/forge/src/main/java/best/lolicon/felis/forge/FelisForgeMod.java +6 −0 Changes for plugins/forge/src/main/java/best/lolicon/felis/forge/FelisForgeMod.java: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import best.lolicon.felis.link.LinkCode; import best.lolicon.felis.link.LinkConfig; import best.lolicon.felis.link.LinkConfigLoader; import best.lolicon.felis.link.LinkException; import best.lolicon.felis.link.ModLinkPolicy; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.exceptions.CommandSyntaxException; Loading Loading @@ -73,6 +74,11 @@ public final class FelisForgeMod { source.sendFailure(Component.literal("/link 只能由玩家执行 / /link can only be run by a player.")); return 0; } if (!source.getServer().usesAuthentication()) { LOGGER.warn(ModLinkPolicy.OFFLINE_LOG); source.sendFailure(Component.literal(ModLinkPolicy.OFFLINE_REPLY)); return 0; } requestAndReply(source.getServer(), player); return 1; })); Loading
plugins/neoforge/src/main/java/best/lolicon/felis/neoforge/FelisNeoForgeMod.java +6 −0 Changes for plugins/neoforge/src/main/java/best/lolicon/felis/neoforge/FelisNeoForgeMod.java: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import best.lolicon.felis.link.LinkCode; import best.lolicon.felis.link.LinkConfig; import best.lolicon.felis.link.LinkConfigLoader; import best.lolicon.felis.link.LinkException; import best.lolicon.felis.link.ModLinkPolicy; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.exceptions.CommandSyntaxException; Loading Loading @@ -77,6 +78,11 @@ public final class FelisNeoForgeMod { source.sendFailure(Component.literal("/link 只能由玩家执行 / /link can only be run by a player.")); return 0; } if (!source.getServer().usesAuthentication()) { LOGGER.warn(ModLinkPolicy.OFFLINE_LOG); source.sendFailure(Component.literal(ModLinkPolicy.OFFLINE_REPLY)); return 0; } requestAndReply(source.getServer(), player); return 1; })); Loading
plugins/shared/src/main/java/best/lolicon/felis/link/ModLinkPolicy.java 0 → 100644 +24 −0 Changes for plugins/shared/src/main/java/best/lolicon/felis/link/ModLinkPolicy.java: 24 added lines, 0 removed lines. Original line number Diff line number Diff line package best.lolicon.felis.link; /** * ModLinkPolicy is what the Fabric, Forge and NeoForge mods say when they refuse * {@code /link}. A mod mints a link code for the UUID its server reports, and only an * online-mode server has checked that UUID with Mojang. Behind a proxy the backend runs * offline-mode and the proxy's own {@code /link} already serves every backend; on a * cracked server the UUID is whatever the client claims, so a code minted there would * let anyone link someone else's Minecraft account. The mods therefore link only on a * standalone online-mode server. */ public final class ModLinkPolicy { /** OFFLINE_REPLY is the chat line a player gets on an offline-mode server. */ public static final String OFFLINE_REPLY = "此服务器未开启正版验证,不能在这里绑定 / This server runs with online-mode off, so /link is unavailable here."; /** OFFLINE_LOG is the server-log line for the same refusal. */ public static final String OFFLINE_LOG = "Felis link: refused /link because online-mode is off. The mod links only on a standalone " + "online-mode server; behind the Felis proxy, the proxy serves /link."; private ModLinkPolicy() { } }