flyemoji 584d31fc49 feat(bootstrap): refuse to install an unverified Velocity fork jar
FELIS_VELOCITY_FORK_JAR replaces the proxy every player connects through, and the
only thing checked about it was that the path pointed at a readable file. A
truncated copy, a stale build left at the same path, or the two-patch jar where
the three-patch one was meant all installed silently.

It now requires FELIS_VELOCITY_FORK_JAR_SHA256 and refuses on a mismatch, hashing
stdin rather than the path for the reason install_via_plugins already documents:
sha256sum escapes its output line for a filename carrying a backslash or newline,
and the leading "\" that adds fails every comparison. The absent-digest refusal
prints the jar's actual hash, so the first run after a deliberate rebuild is one
copy-paste rather than an investigation.

The comparison ignores case and internal spaces. The fork is built on a developer
machine, which is usually Windows, and nothing there prints a digest the way
sha256sum does: Get-FileHash returns uppercase and certutil has shipped the bytes
space-separated. Comparing raw would refuse two of the three spellings of the
correct answer and word the refusal as tampering.

No digest is hardcoded, which is the half of the request this does not deliver.
The fork is built from Felis-Legacy and has never been reproduced on a second
machine, so a constant here would pin one machine's output rather than the fork.
The comment that previously asserted the build "is not byte-reproducible" is gone
too -- it was stated more confidently than the evidence supports. The fork jars on
disk carry Gradle's constant 1980-02-01 entry timestamps, so the usual reason a
jar differs between builds is already absent; that is not proof it reproduces, and
neither claim should sit in the script unmeasured.

This is deliberately not a supply-chain signature and the comment says so: an
operator who can write the jar can write the digest. What it buys is that a path
stops being an identity, and that every later re-run re-checks the same build.

Scope: the fork jar only. The else branch still curls stock Velocity from PaperMC
with no verification at all, and that is the branch a default install takes. The
digest is already in hand there -- Fill v3 returns checksums.sha256 and its
download URL is content-addressed on that same value -- and papermc_latest_jar
discards it. Left alone rather than widened into this change.

deploy/bootstrap_test.sh covers the gate's two refusals, its happy path, and the
two Windows digest spellings. Each case extracts the block under test out of
bootstrap.sh with awk and runs it with die/log stubbed, rather than transcribing
it -- a transcribed copy passes forever after someone edits the original. The
extraction is length-bounded: awk runs an unmatched end pattern to EOF, which
would quietly feed the rest of bootstrap.sh to the shell under test. bootstrap.sh
itself cannot run here; it wants root, a package manager and k3s.

A `shell` CI job runs that plus a syntax check over every tracked script. The
syntax step dispatches on each file's shebang instead of running `sh -n` across
the board. The blanket form looks fine and is a false green: on a developer
machine `sh` is usually bash and accepts everything, while the runner's `sh` is
dash. Verified against the real thing rather than an approximation -- inside
ubuntu:24.04, where /bin/sh is /usr/bin/dash, the dispatching loop passes all six
scripts and the blanket loop dies at bootstrap.sh:191 on the first of its 14
arrays.

Refs: Felis-Legacy #19
2026-07-28 18:01:39 +09:00
2026-07-20 18:53:25 +09:00
2026-07-20 18:53:25 +09:00
2026-07-12 01:42:07 +08:00

Felis

A Kubernetes-driven Minecraft server hosting platform — one command to deploy, automatic lifecycle, backup, and security.
一款 Kubernetes 驱动的 Minecraft 服务器托管平台,一行命令部署,自动管理生命周期与安全。

简体中文 | English

Table of Contents

Features

  • Wake on Join: Servers start automatically when a player connects, and stop when idle — like hibernate for your server.
  • Web Dashboard: Monitor server status, online players, and resource usage from your browser, with backup and restore management.
  • Auto Backup & Restore: Scheduled world backups with one-click rollback from any backup point.
  • World Reaper: Worlds idle for more than 15 days are automatically backed up and removed to free disk space.
  • Multi-core Support: Compatible with Paper, Fabric, Forge, and NeoForge, federated behind a Velocity proxy.
  • Modpack Submission: Players submit custom modpacks; admin approval triggers automatic build and deployment.
  • Passkey Login: Passwordless authentication via fingerprint, face recognition, or hardware security keys.
  • Zero Trust Security: Panel traffic protected by Cloudflare Access; the internal API is never exposed to the internet.

Getting Started

On a prepared Linux host, run:

curl -fsSL https://raw.githubusercontent.com/MliroLirrorsIngenuity/Felis/main/deploy/bootstrap.sh | sudo bash

The script installs K3s, deploys the control plane, and launches a setup wizard. Once done, open your browser at the configured domain.

Build from Source

Felis is built with Go and Node.js:

# Backend (Go 1.26+)
go build -o felis ./cmd/felis

# Frontend (Node.js 22+)
cd panel
npm ci
npm run build

# Docker image
docker build -t felis:custom .

License

The source code is released under AGPL-3.0-only.

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.
  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.
  3. Disclaimer: This project is provided "as is", without warranty of any kind.

Acknowledgements

Languages
Go 62.7%
TypeScript 22.5%
Shell 7.4%
Java 7.1%
Dockerfile 0.2%
Other 0.1%