docs: 重写 README

This commit is contained in:
Lemon-miaow committed 2026-10-02 16:08:42 +08:00
1 parent 109701a276
commit ce8c7893fe
14 files changed
+181 -1146

No files matched your search

+2 -2
View File
@@ -9,8 +9,8 @@
# The push trigger is limited to main rather than every branch, for the reason release.yml is # The push trigger is limited to main rather than every branch, for the reason release.yml is
# not repeated here: a branch with an open PR would otherwise run the whole suite twice per # not repeated here: a branch with an open PR would otherwise run the whole suite twice per
# push, once for refs/heads/<branch> and once for refs/pull/N/merge. Those are different # push, once for refs/heads/<branch> and once for refs/pull/N/merge. Those are different
# concurrency groups, so neither cancels the other, and this repository is private and billed # concurrency groups, so neither cancels the other, and both would occupy runners for the
# for both. A branch with no PR open yet is the one case that loses coverage, and opening the # same commit. A branch with no PR open yet is the one case that loses coverage, and opening the
# PR is what asks for the answer. # PR is what asks for the answer.
name: ci name: ci
+4
View File
@@ -51,3 +51,7 @@ AGENTS.md
docs/Felis-Spec-V4.1.md docs/Felis-Spec-V4.1.md
panel/DESIGN.md panel/DESIGN.md
panel/DESIGN-WEB-3SIDES.md panel/DESIGN-WEB-3SIDES.md
# Audit ledgers and readiness reviews stay on the maintainer's disk.
/AUDIT-*.md
/READINESS-*.md
-1013
View File
File diff suppressed because it is too large. Load diff
+4 -4
View File
@@ -212,13 +212,13 @@ export FELIS_IMAGE=felis:dev
export FELIS_ROOT_DOMAIN=<node-ip>.nip.io export FELIS_ROOT_DOMAIN=<node-ip>.nip.io
``` ```
By default the installer builds the newest **published GitHub release**. While this By default the installer builds the newest **published GitHub release**. Building the
repository is private that lookup — and the clone itself — needs a token, and building development tip needs an opt-in, and installing from a private fork additionally needs a
the development tip needs an opt-in: token for the release lookup and the clone:
```bash ```bash
export FELIS_GITHUB_TOKEN=<token with read access to the repo>
export FELIS_VERSION_BOOTSTRAP=dev # build main instead of the newest release export FELIS_VERSION_BOOTSTRAP=dev # build main instead of the newest release
export FELIS_GITHUB_TOKEN=<token> # private forks only: read access to the fork
``` ```
`dev` is also the escape hatch before the first `vX.Y.Z` tag exists: with no published `dev` is also the escape hatch before the first `vX.Y.Z` tag exists: with no published
+80 -52
View File
@@ -1,14 +1,22 @@
# Felis <div align="center">
<h1 align="center">
<img src="docs/assets/felis-logo.png" alt="Felis logo" width="270"><br>
Felis
</h1>
<p align="center">
基于 Kubernetes 的 Minecraft 服务器托管平台<br>
单条命令完成部署,自动管理服务器生命周期、备份与安全
<br><br>
<a href="README.md">简体中文</a> | <a href="README_EN.md">English</a>
</p>
</div>
**此项目仍处于早期开发阶段,您不该在任何生产环境使用该项目。若产生任何问题,贵用户的使用行为与 FelisMC 团队无任何民事刑事法律关系。** > [!CAUTION]
**THIS PROJECT IS STILL WIP, YOU SHOULD DO NOT USE THIS PROJECT IN ANY PRODUCTION USAGE. WE ARE NOT RESPOND FOR ANY LEGAL OR HUMANLY PROBLEM.** > **此项目仍处于早期开发阶段,您不该在任何生产环境使用该项目。若产生任何问题,贵用户的使用行为与 FelisMC 团队无任何民事刑事法律关系。**<br>
> **THIS PROJECT IS STILL WIP, YOU SHOULD DO NOT USE THIS PROJECT IN ANY PRODUCTION USAGE. WE ARE NOT RESPOND FOR ANY LEGAL OR HUMANLY PROBLEM.**
一款 Kubernetes 驱动的 Minecraft 服务器托管平台,一行命令部署,自动管理生命周期与安全。 <details>
A Kubernetes-driven Minecraft server hosting platform — one command to deploy, automatic lifecycle, backup, and security. <summary>目录</summary>
[简体中文](README.md) | [English](README_EN.md)
目录
- [特性](#特性) - [特性](#特性)
- [使用方式](#使用方式) - [使用方式](#使用方式)
@@ -16,53 +24,73 @@ A Kubernetes-driven Minecraft server hosting platform — one command to deploy,
- [开源协议](#开源协议) - [开源协议](#开源协议)
- [致谢](#致谢) - [致谢](#致谢)
</details>
## 特性 ## 特性
- **即开即玩**:玩家尝试连接时自动唤醒服务器,空闲后自动休眠,像游戏主机一样省资源。 * **按需启停**:玩家连接代理时自动启动目标服务器,启动期间玩家进入等待队列,服务器就绪后自动传送;服务器空闲后自动停止,释放内存。
- **Web 控制面板**:浏览器中查看服务器状态、在线玩家与资源用量,管理备份与恢复。
- **备份与恢复**:一键把整服数据(世界、配置、插件/模组,即整个 /data 卷)打包进集群内的归档库,支持从任意备份点回滚;默认安装就已启用(归档 PVC 与路径由安装器一并生成)。 * **Web 控制面板**:在浏览器中查看服务器状态、在线玩家与资源用量。
- **控制面数据库备份**:账号、服务器归属、配额与存档索引所在的数据库每天自动备份,每次升级迁移前先快照,出错可用 `felis db restore` 整库原子回滚;面板「维护与备份」页显示备份是否新鲜(见 [故障排查 §16](docs/troubleshooting.md))。 * 控制台(RCON)、白名单、封禁、OP 与 LuckPerms 权限管理
- **运维自检**:`sudo felis status` 一屏列出节点、控制面、游戏代理、每台服务器、备份与未解决的告警;`sudo felis doctor` 把健康检查全跑一遍,按区域给出问题和下一步去哪看,不发邮件;`sudo felis support-bundle` 打出一个脱敏的诊断包,求助时直接附上(见 [故障排查 §0](docs/troubleshooting.md))。 * 文件管理:新建、删除、重命名、分片上传、下载,以及停服状态下解压 zip,可用于导入世界
- **智慧回收(可选开启)**:超过 15 天无人游玩的世界自动备份后删除,释放磁盘空间;安装时设置 `FELIS_WORLDS_HOST_PATH`(k3s 默认 `/var/lib/rancher/k3s/storage`)即启用每日回收,不设置则不删任何世界。过期备份无论是否开启都会每天清理。 * 计划任务:按星期与时区定时执行命令、重启、停止、启动或备份,执行前在游戏内向玩家发送提醒
- **多核心支持**:兼容 Paper、Fabric、Forge、NeoForge,经由 Velocity 代理统一入口。
- **模组自助提交**:玩家自行上传模组包,服主审批通过后自动构建;构建产物进入镜像白名单,可直接选用为服务器镜像完成部署。 * **备份与恢复**:默认启用,归档 PVC 及其路径由安装器生成。
- **Passkey 登录**:支持指纹、面容、硬件密钥等无密码认证方式。 * 手动备份:将服务器的完整数据卷(`/data`,含世界、配置、插件与模组)归档至集群内的归档存储,可回滚至任一备份点
- **零信任安全**:面板流量由 Cloudflare Access 保护,集群内 API 不暴露到公网。 * 每日恢复点:当天有玩家进入过的服务器在停止后自动生成恢复点,默认保留 7 个,保存期限 90 天;恢复点单独轮换,不影响手动备份
* 下载与导出:支持下载单个备份(附 sha256 校验)、删除单个备份及导出完整世界
* 异地副本(可选):备份在主机上加密后同步至 S3 兼容存储(AWS S3、Cloudflare R2、Backblaze B2、MinIO 等)
* 控制面数据库:存放账号、服务器归属、配额与存档索引的数据库每日自动备份,每次升级迁移前额外创建快照,故障时可通过 `felis db restore` 整库原子回滚;面板「维护与备份」页显示最近一次备份的时效(参见 [故障排查 §16](docs/troubleshooting.md))
* **运维诊断**
* `sudo felis status`:汇总显示节点、控制面、游戏代理、各服务器、备份及未解决的告警
* `sudo felis doctor`:执行全部健康检查,按模块列出问题及排查方向,执行过程中不发送邮件
* `sudo felis support-bundle`:生成已脱敏的诊断包,供提交问题时附带(参见 [故障排查 §0](docs/troubleshooting.md))
* 看门狗:每 2 分钟执行一次巡检,异常持续时向平台所有者发送邮件告警,支持外部心跳监测
* **世界回收(可选)**:超过 15 天无人游玩的世界在备份后删除,以释放磁盘空间。安装时设置 `FELIS_WORLDS_HOST_PATH`(k3s 默认为 `/var/lib/rancher/k3s/storage`)即启用每日回收;未设置时不删除任何世界。过期备份的每日清理与此设置无关,始终执行。
* **多核心支持**:兼容 Paper、Fabric、Forge 与 NeoForge,统一经由 Velocity 代理接入。
* **模组包投稿**:玩家可上传模组包,经服主审批后自动构建并通过 Trivy 安全扫描;构建产物加入镜像白名单后,可直接选作服务器镜像。
* **安全**
* Passkey 登录:支持指纹、面容识别及硬件密钥等无密码认证方式
* 零信任访问:面板流量经 Cloudflare Access 保护,集群内部 API 不对公网开放
* **多机部署(实验性,默认关闭)**:由一台主控节点统一下发指令,其余节点仅运行游戏服务器,已停止的服务器可迁移至其他节点。该功能目前仅位于 main 分支,尚未完成三机验收(参见 [多机部署](docs/distributed.md))。
## 使用方式 ## 使用方式
在准备好的 Linux 主机上执行(已验证的发行版与架构见 [运维手册 §1](docs/operations.md#1-supported-hosts):CentOS Stream 9 aarch64 实机验证,Ubuntu 24.04 x86_64 每次推送由 CI 跑全新安装、重跑、升级和下面这条命令本身): 在已准备好的 Linux 主机上执行:
```bash ```bash
curl -fsSL https://raw.githubusercontent.com/FelisMC/Felis/main/deploy/bootstrap.sh | sudo bash curl -fsSL https://raw.githubusercontent.com/FelisMC/Felis/main/deploy/bootstrap.sh | sudo bash
``` ```
脚本将自动安装 K3s,在 K3s 内部署 PostgreSQL 与控制平面,并启动设置向导。完成后浏览器访问已配置的域名进入控制面板即可使用。 脚本将安装 K3s,在 K3s 中部署 PostgreSQL 与控制平面,随后启动设置向导。设置完成后,通过浏览器访问所配置的域名即可进入控制面板。
安装发布版时,二进制、全部镜像与 Velocity 插件都取自该版本在 CI 里预构建好的 release 附件,逐个核对 `SHA256SUMS` 后导入,主机上无需 Docker、Gradle 或 Go,也不从 Docker Hub 拉取;某个附件缺失或校验不符时,只有那一个镜像退回到本机构建,并给出提示(见 [故障排查 §15c](docs/troubleshooting.md))。附件也可以先拷到本机,再用 `FELIS_ARTIFACT_DIR=<绝对路径>` 安装,Felis 自己的二进制、镜像和插件就都取自这个目录;k3s 及其镜像、JRE、cloudflared、Velocity 和 Via 插件照旧从 GitHub 与 PaperMC 下载,RHEL、Fedora、openSUSE Leap 这类开着 SELinux 的主机还要从 rpm.rancher.io 装 k3s-selinux,系统软件包来自发行版的源。所以出网受限的主机要放行这几处的 HTTPS(或设 `https_proxy`),preflight 会在改动主机之前逐个探测,完全断网的主机目前装不了(地址清单见 [运维手册 §1](docs/operations.md#1-supported-hosts))。旧版本装在宿主上的 PostgreSQL 会在重跑时整库迁进 K3s,宿主上的那份停用保留,供回退(见 [运维手册 §4](docs/operations.md#4-upgrading-the-pieces-around-felis))。 * **支持的系统**:CentOS Stream 9(aarch64)已在实机上验证;Ubuntu 24.04(x86_64)在每次推送时由 CI 执行全新安装、重复安装、升级及上述安装命令(参见 [运维手册 §1](docs/operations.md#1-supported-hosts))。
动手之前,脚本先检查内存、磁盘、端口、网段冲突、已有的 Kubernetes 和外网连通,把所有问题一次列出并停下,主机上什么都没改(检查项见 [运维手册 §1](docs/operations.md#1-supported-hosts))。 * **安装前检查**:安装器在修改主机之前检查内存、磁盘、端口、网段冲突、已有的 Kubernetes 及外网连通性。发现问题时一次性列出全部问题并退出,主机保持原状(检查项参见 [运维手册 §1](docs/operations.md#1-supported-hosts))。
> **本仓库当前为私有**,上面这条会返回 404。请改用带凭据的形式;安装器自身也需要同一个 token * **升级**:重新执行安装命令即可将 felis-api 升级至新版本;`felis setup` 仅使用本机已安装的二进制,无法用于升级。重新执行时沿用已安装的根域名,发布通道需重新指定:跟随 main 分支的主机须同时设置 `export FELIS_VERSION_BOOTSTRAP=dev`。早期版本安装在宿主机上的 PostgreSQL 会在重新执行时整库迁入 K3s,宿主机上的原实例停用并保留,以便回退(参见 [运维手册 §4](docs/operations.md#4-upgrading-the-pieces-around-felis))。
> 去解析并下载 release,所以用 `sudo -E` 把它带进去:
>
> ```bash
> export FELIS_GITHUB_TOKEN=<对本仓库有读权限的 token>
> printf 'header = "Authorization: Bearer %s"\n' "$FELIS_GITHUB_TOKEN" \
> | curl -fsSL --config - -H "Accept: application/vnd.github.raw" \
> https://api.github.com/repos/FelisMC/Felis/contents/deploy/bootstrap.sh \
> | sudo -E bash
> ```
>
> token 经 stdin 交给 `curl --config -`,不放在命令行上:argv 在 `/proc` 下对本机任意用户可读,
> 而这正是安装器内部 `github_api` 采用同一写法的原因。
重跑这条命令也是把 felis-api 升到新版本的方式(`felis setup` 做不到,它用的是本机已有的二进制)。 <details>
重跑会沿用已安装的根域名,但**不会**沿用通道:若本机跟随 main,需一并 `export FELIS_VERSION_BOOTSTRAP=dev`。 <summary>安装来源与受限网络环境下的安装</summary>
<br>
安装发布版时,二进制文件、全部镜像及 Velocity 插件均取自该版本由 CI 预构建的 release 附件,逐一校验 `SHA256SUMS` 后导入。主机无需安装 Docker、Gradle 或 Go,也无需访问 Docker Hub。若某个附件缺失或校验失败,仅该镜像回退为本机构建,并输出提示(参见 [故障排查 §15c](docs/troubleshooting.md))。
也可将附件预先复制到主机,再通过 `FELIS_ARTIFACT_DIR=<绝对路径>` 安装,此时 Felis 自身的二进制、镜像与插件均从该目录读取。k3s 及其镜像、JRE、cloudflared、Velocity 与 Via 插件仍从 GitHub 和 PaperMC 下载;RHEL、Fedora、openSUSE Leap 等启用 SELinux 的主机还需从 rpm.rancher.io 安装 k3s-selinux;系统软件包来自发行版软件源。
因此,出站网络受限的主机须放行上述地址的 HTTPS 访问,或设置 `https_proxy`。preflight 会在修改主机之前逐一探测这些地址。目前暂不支持完全离线安装(地址清单参见 [运维手册 §1](docs/operations.md#1-supported-hosts))。
</details>
## 从源码构建 ## 从源码构建
本项目基于 Go 和 Node.js 开发: 本项目基于 Go 与 Node.js 开发:
```bash ```bash
# 后端(Go 1.26+) # 后端(Go 1.26+)
@@ -79,22 +107,22 @@ docker build -t felis:custom .
## 开源协议 ## 开源协议
本项目遵循 [AGPL-3.0-only](LICENSE) 开源协议。 本项目采用 [AGPL-3.0-only](LICENSE) 许可证。
### 协议注意事项 ### 协议注意事项
1. **衍生作品同样是 AGPL**:分发本项目的副本或基于本项目衍生的软件时,必须以 AGPL-3.0 开源,并保留原作者的版权声明和许可声明。 1. **衍生作品须采用 AGPL**:分发本项目副本或基于本项目的衍生软件时,须以 AGPL-3.0 开源,并保留原作者的版权声明与许可声明。
2. **通过网络提供服务同样要开源**(AGPL 第 13 条):如果你把修改过的 Felis 架起来给别人用,即使从不分发任何二进制,也必须向这些用户提供你那份修改后的完整源码。这是 AGPL 相对 GPL 的唯一实质区别,而 Felis 正是一个跑在网络上的托管平台,所以这一条基本总会触发。 2. **网络服务同样须提供源码**(AGPL 第 13 条):通过网络向他人提供经修改的 Felis 服务时,即使未分发任何二进制文件,也须向这些用户提供修改后的完整源码。这是 AGPL 与 GPL 唯一的实质区别;Felis 作为通过网络访问的托管平台,几乎所有部署场景都适用此条款。
3. **免责声明**:本项目按"原样"提供,作者不承担任何因使用本项目而产生的法律责任。 3. **免责声明**:本项目按"原样"提供,作者不承担因使用本项目而产生的任何法律责任。
## 致谢 ## 致谢
- [Kubernetes](https://kubernetes.io/):底层容器编排引擎 * [Kubernetes](https://kubernetes.io/):容器编排引擎
- [K3s](https://k3s.io/):轻量级 Kubernetes 发行版 * [K3s](https://k3s.io/):轻量级 Kubernetes 发行版
- [Cloudflare Zero Trust](https://www.cloudflare.com/zero-trust/):零信任安全基础设施 * [Cloudflare Zero Trust](https://www.cloudflare.com/zero-trust/):零信任安全基础设施
- [PostgreSQL](https://www.postgresql.org/):数据持久化 * [PostgreSQL](https://www.postgresql.org/):数据持久化
- [React](https://react.dev/):前端用户界面框架 * [React](https://react.dev/):前端用户界面框架
- [Vite](https://vitejs.dev/):前端构建工具 * [Vite](https://vitejs.dev/):前端构建工具
- [TailwindCSS](https://tailwindcss.com/):CSS 框架 * [TailwindCSS](https://tailwindcss.com/):CSS 框架
- [Bubble Tea](https://github.com/charmbracelet/bubbletea):TUI 框架 * [Bubble Tea](https://github.com/charmbracelet/bubbletea):TUI 框架
- [Minecraft](https://www.minecraft.net/):让这一切值得做 * [Minecraft](https://www.minecraft.net/):本项目服务的游戏
+78 -53
View File
@@ -1,11 +1,21 @@
# Felis <div align="center">
<h1 align="center">
<img src="docs/assets/felis-logo.png" alt="Felis logo" width="270"><br>
Felis
</h1>
<p align="center">
A Kubernetes-driven Minecraft server hosting platform<br>
One command to deploy, with automatic lifecycle, backup, and security
<br><br>
<a href="README.md">简体中文</a> | <a href="README_EN.md">English</a>
</p>
</div>
A Kubernetes-driven Minecraft server hosting platform — one command to deploy, automatic lifecycle, backup, and security. > [!CAUTION]
一款 Kubernetes 驱动的 Minecraft 服务器托管平台,一行命令部署,自动管理生命周期与安全。 > **THIS PROJECT IS STILL WIP, YOU SHOULD DO NOT USE THIS PROJECT IN ANY PRODUCTION USAGE. WE ARE NOT RESPOND FOR ANY LEGAL OR HUMANLY PROBLEM.**
[简体中文](README.md) | [English](README_EN.md) <details>
<summary>Table of Contents</summary>
Table of Contents
- [Features](#features) - [Features](#features)
- [Getting Started](#getting-started) - [Getting Started](#getting-started)
@@ -13,54 +23,69 @@ Table of Contents
- [License](#license) - [License](#license)
- [Acknowledgements](#acknowledgements) - [Acknowledgements](#acknowledgements)
</details>
## Features ## Features
- **Wake on Join**: Servers start automatically when a player connects, and stop when idle — like hibernate for your server. * **On-demand Start and Stop**: A server starts when a player connects to the proxy. The player waits in a queue during start-up and is transferred once the server is ready. Idle servers stop automatically to free memory.
- **Web Dashboard**: Monitor server status, online players, and resource usage from your browser, with backup and restore management.
- **Backup & Restore**: One-click snapshots of a server's whole data volume (worlds, config, plugins/mods — the entire /data volume) into the cluster's archive store, with rollback from any backup point — enabled by default (the installer renders the archive PVC and its path). * **Web Dashboard**: Monitor server status, online players, and resource usage from your browser.
- **Control-plane database backups**: The database holding accounts, server ownership, quotas and the archive index is backed up daily and snapshotted before every upgrade migrates it; `felis db restore` rolls it back atomically, and the panel's Maintenance & Backups page shows whether the newest backup is fresh (see [troubleshooting §16](docs/troubleshooting.md)). * Console (RCON), whitelist, bans, OPs and LuckPerms permissions
- **Self-check for operators**: `sudo felis status` shows the node, the control plane, the game proxy, every server, the backups and the open alerts on one screen; `sudo felis doctor` runs every health check once and lists each problem by area with where to look next, mailing nothing; `sudo felis support-bundle` writes one redacted diagnostics archive to attach when asking for help (see [troubleshooting §0](docs/troubleshooting.md)). * File manager: create, delete, rename, chunked upload, download, and unzip while the server is stopped; also used to import worlds
- **World Reaper** (opt in): Worlds idle for more than 15 days are automatically backed up and removed to free disk space. Enable it by setting `FELIS_WORLDS_HOST_PATH` at install time (on k3s: `/var/lib/rancher/k3s/storage`); without it, no world is ever deleted. * Schedules: run commands, restart, stop, start or back up by weekday and time zone, with an in-game warning to players beforehand
- **Multi-core Support**: Compatible with Paper, Fabric, Forge, and NeoForge, federated behind a Velocity proxy.
- **Modpack Submission**: Players submit custom modpacks; admin approval triggers an automatic build, and the result is whitelisted as a server image you can select to deploy. * **Backup & Restore**: Enabled by default; the installer renders the archive PVC and its path.
- **Passkey Login**: Passwordless authentication via fingerprint, face recognition, or hardware security keys. * Manual backups: archive a server's entire data volume (`/data`, including worlds, configuration, plugins and mods) to the cluster's archive store, with rollback to any backup point
- **Zero Trust Security**: Panel traffic protected by Cloudflare Access; the internal API is never exposed to the internet. * Daily restore points: a server played that day gets a restore point once it stops; by default 7 are kept for up to 90 days, rotated separately from manual backups
* Download and export: download a single backup (with sha256 verification), delete a single backup, or export a whole world
* Off-site copy (optional): backups are encrypted on the host and synced to S3-compatible storage (AWS S3, Cloudflare R2, Backblaze B2, MinIO and others)
* Control-plane database: the database holding accounts, server ownership, quotas and the archive index is backed up daily and snapshotted before every upgrade migration; `felis db restore` rolls it back atomically, and the panel's Maintenance & Backups page shows the age of the latest backup (see [troubleshooting §16](docs/troubleshooting.md))
* **Diagnostics**
* `sudo felis status`: a summary of the node, control plane, game proxy, each server, backups and open alerts
* `sudo felis doctor`: runs all health checks and lists problems by area with troubleshooting pointers; sends no email
* `sudo felis support-bundle`: generates a redacted diagnostics archive to attach to support requests (see [troubleshooting §0](docs/troubleshooting.md))
* Watchdog: runs a check every 2 minutes and emails the platform owners when a problem persists; supports an external heartbeat monitor
* **World Reaper** (optional): Worlds idle for more than 15 days are backed up and then removed to free disk space. Enable it by setting `FELIS_WORLDS_HOST_PATH` at install time (on k3s: `/var/lib/rancher/k3s/storage`); without it, no world is deleted. Expired backups are cleaned up daily regardless of this setting.
* **Multi-core Support**: Compatible with Paper, Fabric, Forge, and NeoForge, accessed through a single Velocity proxy.
* **Modpack Submission**: Players can upload modpacks. After admin approval, each modpack is built automatically and scanned with Trivy; the result is added to the image whitelist and can be selected as a server image.
* **Security**
* Passkey login: passwordless authentication via fingerprint, face recognition, or hardware security keys
* Zero-trust access: panel traffic is protected by Cloudflare Access, and the internal API is not exposed to the internet
* **Multi-node Deployment** (experimental, off by default): a single controller node issues all commands, the other nodes run game servers only, and a stopped server can be migrated to another node. Currently available only on the main branch; three-node acceptance testing is not yet complete (see [distributed mode](docs/distributed.md), in Chinese).
## Getting Started ## Getting Started
On a prepared Linux host, run (the verified distributions and architectures are listed in On a prepared Linux host, run:
[operations §1](docs/operations.md#1-supported-hosts): CentOS Stream 9 on aarch64 is verified
on a real host, and Ubuntu 24.04 on x86_64 gets a fresh install, rerun and upgrade in CI on
every push):
```bash ```bash
curl -fsSL https://raw.githubusercontent.com/FelisMC/Felis/main/deploy/bootstrap.sh | sudo bash curl -fsSL https://raw.githubusercontent.com/FelisMC/Felis/main/deploy/bootstrap.sh | sudo bash
``` ```
The script installs K3s, deploys PostgreSQL and the control plane inside it, and launches a setup wizard. Once done, open your browser at the configured domain. A PostgreSQL an earlier release installed on the host is moved into K3s on the next rerun, and the host copy is stopped and kept for a rollback (see [Operations §4](docs/operations.md#4-upgrading-the-pieces-around-felis)). The script installs K3s, deploys PostgreSQL and the control plane inside it, and launches a setup wizard. When setup completes, open the configured domain in a browser to reach the control panel.
Before it changes anything, the script checks RAM, disk, ports, network-range clashes, any Kubernetes already there and outbound access, lists every problem at once and stops with the host untouched (the checks are in [operations §1](docs/operations.md#1-supported-hosts)). * **Supported hosts**: CentOS Stream 9 (aarch64) is verified on physical hardware; Ubuntu 24.04 (x86_64) is tested in CI on every push with a fresh install, a rerun, an upgrade and the install command above (see [operations §1](docs/operations.md#1-supported-hosts)).
> **This repository is currently private**, so the command above returns 404. Use the * **Preflight checks**: Before modifying the host, the installer checks memory, disk, ports, network range conflicts, existing Kubernetes installations and outbound connectivity. If any check fails, it lists all problems and exits, leaving the host unchanged (see [operations §1](docs/operations.md#1-supported-hosts) for the checks).
> credentialed form instead; the installer itself needs the same token to resolve and
> download the release, so pass it through with `sudo -E`:
>
> ```bash
> export FELIS_GITHUB_TOKEN=<a token with read access to this repository>
> printf 'header = "Authorization: Bearer %s"\n' "$FELIS_GITHUB_TOKEN" \
> | curl -fsSL --config - -H "Accept: application/vnd.github.raw" \
> https://api.github.com/repos/FelisMC/Felis/contents/deploy/bootstrap.sh \
> | sudo -E bash
> ```
>
> The token reaches `curl --config -` over stdin instead of the command line: argv is
> readable by any local user via `/proc`, and that is exactly why the installer's
> internal `github_api` uses the same form.
Rerunning this command is also how you upgrade felis-api to a newer version (`felis setup` * **Upgrading**: Rerun the install command to upgrade felis-api to a newer version; `felis setup` only uses the binary already installed on the host and cannot upgrade it. A rerun keeps the installed root domain, and the release channel must be specified again: hosts that follow the main branch must also set `export FELIS_VERSION_BOOTSTRAP=dev`. A PostgreSQL instance installed on the host by an earlier release is migrated into K3s during the rerun; the original instance on the host is stopped and retained for rollback (see [operations §4](docs/operations.md#4-upgrading-the-pieces-around-felis)).
cannot — it uses the binary already installed on the host). The rerun keeps the installed
root domain but **not** the channel: if this host follows main, also <details>
`export FELIS_VERSION_BOOTSTRAP=dev`. <summary>Installation sources and restricted networks</summary>
<br>
A release installation takes the binary, all images and the Velocity plugin from the release assets prebuilt in CI, verifying each against `SHA256SUMS` before import. The host requires no Docker, Gradle or Go, and no access to Docker Hub. If an asset is missing or fails verification, only that image falls back to a local build, and the installer prints a notice (see [troubleshooting §15c](docs/troubleshooting.md)).
The assets can also be copied to the host in advance and installed with `FELIS_ARTIFACT_DIR=<absolute path>`; the Felis binary, images and plugin are then read from that directory. k3s and its images, the JRE, cloudflared, Velocity and the Via plugins are still downloaded from GitHub and PaperMC; hosts with SELinux enabled, such as RHEL, Fedora and openSUSE Leap, additionally install k3s-selinux from rpm.rancher.io; system packages come from the distribution's repositories.
A host with restricted outbound access must therefore allow HTTPS to these addresses or set `https_proxy`. Preflight probes each address before changing the host. Fully offline installation is not yet supported (see [operations §1](docs/operations.md#1-supported-hosts) for the address list).
</details>
## Build from Source ## Build from Source
@@ -81,22 +106,22 @@ docker build -t felis:custom .
## License ## License
The source code is released under [AGPL-3.0-only](LICENSE). This project is licensed under [AGPL-3.0-only](LICENSE).
### License Notes ### License Notes
1. **Derivative works are AGPL too**: Any distribution of this project or of software derived from it must be released under AGPL-3.0 and must include the original copyright notice and license statement. 1. **Derivative works must use AGPL**: Any distribution of this project or of software derived from it must be released under AGPL-3.0 and must include the original copyright notice and license statement.
2. **Running it as a network service also triggers the source obligation** (AGPL section 13): if you host a modified Felis for other people to use, you must offer those users the complete source of your modified version — even if you never distribute a binary. This is the one substantive difference between AGPL and GPL, and since Felis is a hosting platform reached over a network, it will essentially always apply. 2. **Network services must also provide source** (AGPL section 13): anyone who offers a modified Felis to others over a network must provide those users with the complete source of the modified version, even without distributing any binary. This is the only substantive difference between AGPL and GPL; as Felis is a hosting platform accessed over a network, this clause applies to virtually every deployment.
3. **Disclaimer**: This project is provided "as is", without warranty of any kind. 3. **Disclaimer**: This project is provided "as is", without warranty of any kind.
## Acknowledgements ## Acknowledgements
- [Kubernetes](https://kubernetes.io/): Container orchestration engine * [Kubernetes](https://kubernetes.io/): Container orchestration engine
- [K3s](https://k3s.io/): Lightweight Kubernetes distribution * [K3s](https://k3s.io/): Lightweight Kubernetes distribution
- [Cloudflare Zero Trust](https://www.cloudflare.com/zero-trust/): Zero trust security infrastructure * [Cloudflare Zero Trust](https://www.cloudflare.com/zero-trust/): Zero trust security infrastructure
- [PostgreSQL](https://www.postgresql.org/): Data persistence * [PostgreSQL](https://www.postgresql.org/): Data persistence
- [React](https://react.dev/): User interface framework * [React](https://react.dev/): User interface framework
- [Vite](https://vitejs.dev/): Frontend build tool * [Vite](https://vitejs.dev/): Frontend build tool
- [TailwindCSS](https://tailwindcss.com/): CSS framework * [TailwindCSS](https://tailwindcss.com/): CSS framework
- [Bubble Tea](https://github.com/charmbracelet/bubbletea): TUI framework * [Bubble Tea](https://github.com/charmbracelet/bubbletea): TUI framework
- [Minecraft](https://www.minecraft.net/): What makes this all worthwhile * [Minecraft](https://www.minecraft.net/): The game this project serves
+4 -7
View File
@@ -62,9 +62,7 @@ type updateTarget struct {
// The URL is the one-liner both READMEs hand out, read at a tag rather than main: the // The URL is the one-liner both READMEs hand out, read at a tag rather than main: the
// script's release channel installs the newest release's binary, and main can carry // script's release channel installs the newest release's binary, and main can carry
// installer changes that binary was never tested with. installerRef picks the tag and // installer changes that binary was never tested with. installerRef picks the tag and
// renderApplyGuidance substitutes it for {ref}. While the repo is private the URL // renderApplyGuidance substitutes it for {ref}.
// answers 404 (raw.githubusercontent.com hides private repos), which is why the trailer
// below points at the README's token'd form for that case.
const installerRerun = "curl -fsSL https://raw.githubusercontent.com/FelisMC/Felis/{ref}/deploy/bootstrap.sh | sudo bash" const installerRerun = "curl -fsSL https://raw.githubusercontent.com/FelisMC/Felis/{ref}/deploy/bootstrap.sh | sudo bash"
// installerRerunDeps is the same re-run with FELIS_UPGRADE_DEPS=1, which lets it move an // installerRerunDeps is the same re-run with FELIS_UPGRADE_DEPS=1, which lets it move an
@@ -436,12 +434,11 @@ func renderApplyGuidance(res updater.Result, selected map[string]bool, force boo
// completed install (shouldRunHostBootstrapBeforeConfig only enters the host // completed install (shouldRunHostBootstrapBeforeConfig only enters the host
// bootstrap while an install marker is missing), so the installer re-run is the one // bootstrap while an install marker is missing), so the installer re-run is the one
// worked path for every component Felis installs and there is no per-component exception left // worked path for every component Felis installs and there is no per-component exception left
// to scope. Two caveats stay because following the advice without them bites real // to scope. One caveat stays because following the advice without it bites real
// hosts: the channel is not persisted anywhere (a bare re-run on a main host quietly // hosts: the channel is not persisted anywhere (a bare re-run on a main host quietly
// moves it onto releases), and the private repo's one-liner needs the read token // moves it onto releases).
// back in the environment before it can resolve anything.
if offeredInstaller { if offeredInstaller {
b.WriteString("\nRe-running the installer applies each installer command above: it fetches the newest version on\nthe channel in effect and re-applies the bundle (release is the default). The channel\nis not persisted, so pass FELIS_VERSION_BOOTSTRAP=dev if this host tracks main. While\nthis repo is private, the one-liner above 404s without a token; the README's install\nsection has the token'd form that works. felis setup is not this path: on a completed\ninstall it opens the config console and installs nothing newer. Restart game servers\nafterwards.\n") b.WriteString("\nRe-running the installer applies each installer command above: it fetches the newest version on\nthe channel in effect and re-applies the bundle (release is the default). The channel\nis not persisted, so pass FELIS_VERSION_BOOTSTRAP=dev if this host tracks main.\nfelis setup is not this path: on a completed install it opens the config console and\ninstalls nothing newer. Restart game servers afterwards.\n")
} }
return b.String() return b.String()
} }
+2 -2
View File
@@ -87,7 +87,7 @@
# Docker Hub, nor built (FELIS_GAME_STACK=latest aside: no release # Docker Hub, nor built (FELIS_GAME_STACK=latest aside: no release
# ships that stack, so its game images are built here). A file missing # ships that stack, so its game images are built here). A file missing
# from it or not matching its SHA256SUMS stops the install. # from it or not matching its SHA256SUMS stops the install.
# FELIS_GITHUB_TOKEN GitHub token; REQUIRED while the repo is private # FELIS_GITHUB_TOKEN GitHub token; needed only when installing from a private fork
# FELIS_REF branch/tag/sha — pins the build, overrides the channel, and forces a # FELIS_REF branch/tag/sha — pins the build, overrides the channel, and forces a
# source build (naming a ref asks for that tree, not a published asset) # source build (naming a ref asks for that tree, not a published asset)
# FELIS_RELEASE a published release tag (v1.2.3) the release channel installs, from # FELIS_RELEASE a published release tag (v1.2.3) the release channel installs, from
@@ -197,7 +197,7 @@ DOCKER_INSTALLED=""
# hour. # hour.
RELEASE_JSON_TAG="" RELEASE_JSON_TAG=""
RELEASE_JSON="" RELEASE_JSON=""
# Optional GitHub credential, needed while this repository is private: GitHub answers # Optional GitHub credential, needed only for a private fork: GitHub answers
# 404 (not 403) for a repo the caller cannot see, so without it both the release lookup # 404 (not 403) for a repo the caller cannot see, so without it both the release lookup
# and the clone fail as "not found". Exported because git's credential helper below runs # and the clone fail as "not found". Exported because git's credential helper below runs
# as a child process and reads it from the environment — which is also why it is never # as a child process and reads it from the environment — which is also why it is never
Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

-2
View File
@@ -396,8 +396,6 @@ curl -fsSL <raw-url>/deploy/uninstall.sh | sudo bash -s -- --yes # keep the
curl -fsSL <raw-url>/deploy/uninstall.sh | sudo bash -s -- --purge # remove the data too curl -fsSL <raw-url>/deploy/uninstall.sh | sudo bash -s -- --purge # remove the data too
``` ```
With a private repository, fetch it the way the README fetches `bootstrap.sh`.
Both modes remove the `felis-*` systemd units and `cloudflared-felis.service`, the Both modes remove the `felis-*` systemd units and `cloudflared-felis.service`, the
Velocity user, `/opt/felis`, `/usr/local/bin/felis`, the release assets an interrupted Velocity user, `/opt/felis`, `/usr/local/bin/felis`, the release assets an interrupted
install left in `/var/lib/felis/artifacts`, the installer's cloudflared binary (unless install left in `/var/lib/felis/artifacts`, the installer's cloudflared binary (unless
-3
View File
@@ -2534,9 +2534,6 @@ an unpublished tag stops the install before anything changes. [SH-TESTED]
`curl -fsSL <that URL> | grep -c FELIS_RELEASE` prints `0` for one of those; `curl -fsSL <that URL> | grep -c FELIS_RELEASE` prints `0` for one of those;
run it with `FELIS_REF=v1.2.3` instead, which builds that tag from source run it with `FELIS_REF=v1.2.3` instead, which builds that tag from source
(slower, and it needs the build resources of §15c). (slower, and it needs the build resources of §15c).
- While the repository is private, read the installer through the README's
token'd form with `?ref=v1.2.3` after `contents/deploy/bootstrap.sh`, and run
it as `sudo -E FELIS_RELEASE=v1.2.3 bash`.
### Whole-host disaster recovery: what comes back, and from where ### Whole-host disaster recovery: what comes back, and from where
+3 -4
View File
@@ -26,10 +26,9 @@
// verified 2026-07-05) so Felis's UA is load-bearing there; PaperMC does NOT // verified 2026-07-05) so Felis's UA is load-bearing there; PaperMC does NOT
// enforce (a bare request got HTTP 200 on 2026-07-04) so its UA is only etiquette. // enforce (a bare request got HTTP 200 on 2026-07-04) so its UA is only etiquette.
// Also: felis-api's topology coord is now the real repository slug, not a placeholder, // Also: felis-api's topology coord is now the real repository slug, not a placeholder,
// so that component resolves at runtime like the others — but only with a credential. // so that component resolves at runtime like the others, unauthenticated. A private
// The repo is private, so an unauthenticated poll gets GitHub's 404-for-hidden-repo and // fork answers an unauthenticated poll with GitHub's 404-for-hidden-repo and degrades
// degrades to "latest unknown"; FELIS_GITHUB_TOKEN is what lights it up. k3s and // to "latest unknown"; FELIS_GITHUB_TOKEN is what lights it up there.
// cloudflared are public and need none.
// //
// - ALSO BUILT + UNIT-VERIFIED: the VersionGatherer's extraction core and dispatch. // - ALSO BUILT + UNIT-VERIFIED: the VersionGatherer's extraction core and dispatch.
// Three pure extractors turn raw system text into a Version — a `--version` banner // Three pure extractors turn raw system text into a Version — a `--version` banner
+3 -3
View File
@@ -40,9 +40,9 @@ type github struct {
baseURL string // e.g. "https://api.github.com" baseURL string // e.g. "https://api.github.com"
userAgent string // MUST be non-empty — GitHub 403s a UA-less request userAgent string // MUST be non-empty — GitHub 403s a UA-less request
// token is an optional credential. Empty means unauthenticated, which is the right // token is an optional credential. Empty means unauthenticated, which is the right
// posture for the public repos Felis tracks (k3s, cloudflared) and is what the // posture for the public repos Felis tracks (felis-api, k3s, cloudflared) and is what
// 60-req/h note above is about. It is load-bearing only for felis-api's own repo // the 60-req/h note above is about. It is load-bearing only when felis-api is built
// while that repo is private, where its absence does not look like an auth failure: // from a private fork, where its absence does not look like an auth failure:
// GitHub answers 404 — not 401 or 403 — for a private repo the caller cannot see, so // GitHub answers 404 — not 401 or 403 — for a private repo the caller cannot see, so
// "no token" is indistinguishable from "no release published yet" by status alone. // "no token" is indistinguishable from "no release published yet" by status alone.
// latestStable says both in the error rather than making an operator guess. // latestStable says both in the error rather than making an operator guess.
+1 -1
View File
@@ -282,7 +282,7 @@ before the proxy Connects them.
> no real game client has joined through the stack, so §27 scenario 10 stays > no real game client has joined through the stack, so §27 scenario 10 stays
> **FAIL (live-unverified)** until such a join is exercised. The client-independent > **FAIL (live-unverified)** until such a join is exercised. The client-independent
> faces (proxy edge, subdomain MOTD, login boundary, backend registration) are > faces (proxy edge, subdomain MOTD, login boundary, backend registration) are
> exercised on a live deployment — see `AUDIT-2026-09-22.md`. > exercised on a live deployment.
## Building ## Building