Skip to content

竞品源码分析 ​

结论先行:功能面没有一家对手同时具备我们手里的四件套;真正的品类缺口是 对象存储/CDN > 账号安全深度 > 服务端接入面。

取证口径:浅克隆到 refs/(不入库)后源码 grep,格内 文件:行 为对方仓库内定位; 未找到写 NOT FOUND,不猜;「未取证」≠ 对方没有。README 宣称与源码不符处单独标注。 快照口径:2026-11-01(正文取证;覆盖率复测 2026-11-09,状态校正与增补 2026-11-15)。 历史快照见 git 历史。与 blessing 的协议兼容事实不在本文,见 compat-matrix.md 与 migration.md。

许可证边界:只吸收概念与公开协议事实;禁止复制、翻译改写 GPL/AGPL 竞品源码、译文或测试 fixture;引用一律注明对方路径与行号。

竞品名录 ​

仓库栈许可HEAD一句话定位
blessing-skin-serverPHP Laravel + MySQLMIT2026-03-30品类事实标准,生态与教程存量十年
DraslGo + SQLite/MySQLGPL-3.02026-11-01Go 自托管同类,工程纪律标杆
chrly(Ely.by 皮肤系统)Go + RedisApache-2.02024-07-09在役最大 Yggdrasil 生态的皮肤单件,纹理签名见长
HRPAuthGo + MySQL + RedisAGPL-3.02026-11-13认证核心 + OAuth2 中心,MFA 全家桶
element-skinGo + PostgreSQL + Redis + Vue 3MIT2026-10-25功能面广,覆盖率闸 90%
vSkinPython FastAPI + Vue 3 + SQLiteGPL-3.02026-05-29基本停更,反面教材价值为主
PigeonSkin-Server见仓(Cloudflare 免费服务方案)未取证见仓新入场者,已占通用搜索结果位
LittleSkin premium-verification 等生态件PHPAGPL/GPL见仓CN 事实标准的正版资格校验件

总判断 ​

  • 功能面:SEO 服务端渲染体系、启动器深链兼容、材质生命周期闭环(像素定模型 → 违规处置 → 引用清点后彻底删除并留证)、签名插件市场与任务运行器——没有一家同时具备,我方四项皆有。 差距不在功能列表,在可发布性与契约完整性(矩阵「错误契约 / 覆盖率 / 公共配置」三行)。
  • 品类缺口(按量级):
    1. 对象存储 + CDN 刷新——各家全无(§矩阵首两行)。我方以 immutable + 可改写 URL 的插件 filter 换取 5182 RPS 的纹理分发,代价记在 compat-matrix.md §4。
    2. 账号安全深度(TOTP / WebAuthn·passkey / 邮箱二要素 / 恢复密钥)——HRPAuth 一家拉满并经 源码核验,其余各家与我方都停在「密码 + 邮箱验证」层面(§重量级补录)。
    3. 不挂 authlib-injector 的接入面(services / discovery / certificates 族)——Drasl 整族挂载; chrly /textures/signed 与 HRPAuth-Spigot 是同一方向的另一种形态。
  • 伪需求:签名密钥轮换(authlib-injector 无轮换消费路径,规范亦不建议换 key)、像素级材质 hash(与 blessing 迁移字节对账冲突,internal/migrate/scan.go:66-80)、three.js 全景首页 (伤 LCP 与 SSR 优势冲突)、换 PostgreSQL/Redis 栈(单站规模无收益)。
  • 已补的历史缺口:邮箱验证流与站内通知中心曾列此处,2026-11-02 落地(矩阵 §账号 对应行)。

能力矩阵 ​

图例:✅ 有并可用 · ⚠️ 有但残缺/有坑 · ❌ 确认没有 · — 不适用 ·「未取证」本次没查。

材质与存储 ​

能力SkinNexuselement-skinvSkinblessingDrasl
对象存储抽象(S3/OSS/COS)❌ 本地盘 texture/pipeline.go:64❌ service/texture/storage.go:24-33❌ texture.py:20-21⚠️ 有驱动开关、无云适配器(config/filesystems.php:54-57)❌ 本地盘 common.go:476-509
CDN 失效/刷新调用❌ cdn_enabled 只管真实 IP(main.go:192-208)❌❌❌ cdn_address 只改前端资源域名(config/options.php:45)❌
内容寻址 URL + immutable✅ main.go:793,965⚠️ Nginx 直出、Go 不发缓存头(profile.go:30-34)⚠️ 头声明基址与挂载点不一致(yggdrasil_routes.py:58-59 vs :109-112)✅ routes/static.php:8⚠️ 内容寻址但 web 解析发 no-cache(front.go:1200-1206)
删除:行 + 文件 + 引用计数✅ account/delete.go:47-51 + store/texdelete.go:152-155❌ 只删行,DeleteFile 零调用(service/texture/admin.go:150-195)→ 文件永久堆积❌ 只删行不计数(texture.py:71-85)✅ 计数为 1 才删(SkinlibController.php:337-356)✅ DeleteSkinIfUnused(common.go:627-655)
删除留证(墓碑)✅ migrations/0019_texture_tombstones.sql❌❌❌ 举报行留着但 tid 悬空(ReportController.php:115-124)❌
删除并发收敛✅ 按 hash GET_LOCK 跨上传/删除(texdelete.go:48-78)❌❌❌ 举报处置 delete 跳过引用计数守卫(ReportController.php:112-113)⚠️ 依赖 SQLite/WAL 串行化
手臂模型来源✅ 像素判定(texture/model.go:38-57),客户端声明仅建议值❌ 采信表单(profile/store.go:24-29)❌ 表单原样入库(site_routes.py:158)❌ 表单枚举(SkinlibController.php:206)未取证
旧 22×17 披风补边✅ 新上传补边、存量不改写(pipeline.go:141-189)❌ 收下不归一(storage.go:82-84)❌ 同(image_utils.py:23)❌ 直接拒收(SkinlibController.php:234,255-256)未取证
违规处置状态化✅ textures.moderation 列 + UPDATE 拦截(store/skinlib.go:314-317);按行不按 hash 是已知限制❌ 仅 is_public,可无门槛转回公开(management.go:55-63)❌ 无管理端材质路由(admin_routes.py:39-286)⚠️ 行删了重复检查就放行(SkinlibController.php:282-291)未取证

契约与工程纪律 ​

能力SkinNexuselement-skinvSkinblessingDrasl
错误体带稳定 code✅ {error, code, message} + 码目录(adminapi/errors.go:31-35、i18n/codes.go)✅ 三元组 + 18 值闭合枚举、不含文案(util/response.go:18-37,107-124)❌ 只有人话(site_routes.py:168)❌ 粗粒度整数 0/1/2(app/helpers.php:27-46)未取证
错误契约被机器强制✅ AST 闸(errorgate/errorgate.go + 测试)⚠️ 未见对应闸❌ 无 CI❌未取证
覆盖率阈值⚠️ 地板 45.0(实测 52.5)ci.yml:35-53✅ 硬阈值 90%(docker-publish.yml:75-83)❌⚠️ 上传 Codecov 不设阈值(CI.yml:78-85)未取证
集成测试真库矩阵✅ 六腿 MySQL 8/5.7 + MariaDB 11.4/10.11 + PG16 + SQLite 文件库(ci.yml:72-180)未取证❌ 无 CI⚠️ PHPUnit 矩阵✅ SQLite/MySQL 双后端
前端测试/lint 门禁❌⚠️ 有配置无 job⚠️ 同✅ yarn lint + fmt:check + type:check未取证
公共配置白名单✅ 5 键硬编码(webapi.go:414-431)✅ 17 键列举并校验数量(publicsite/public.go:64-69)⚠️ 白名单内联大 handler✅ 4 键(HomeController.php:35-40)❌ 模板直读 Config
env 唯一清单 + 未知变量告警✅ config/env.go AST 双向对齐 + check-docs.mjs❌❌❌❌
迁移前自动备份❌(只换镜像不回滚 schema,deploy.md §2.5)未取证未取证⚠️ 依赖手工✅ 默认开(db.go:273-289)
未实现诚实返回 501✅ 宁可不注册(Union 关闭 = 全 404)未取证未取证未取证✅ discovery.go:185-188,文档显式标未实现
多前缀挂同一 handler✅ 单 mux 分面未取证未取证⚠️ 三张路由表✅ ""、/auth、/authlib-injector/authserver 三前缀(main.go:396-438)

账号、内容与生态 ​

能力SkinNexuselement-skinvSkinblessingDrasl
邮箱验证流✅ 发信 + 点击消费 + 宽限期 + 两侧验证门(webapi/email_verify.go、internal/verifygate/)✅ Redis 码 TTL300 + 重发限流(verification.go:100-121)✅ SQLite 码 TTL300⚠️ 验证链接永不过期(SendEmailVerification.php:15)❌ 邀请码注册(main.go:673-722)
找回密码令牌强度✅ 单次消费 + 摘要存储 + 失败合并(webapi/password.go:121,204-246)未取证未取证✅ 1h 过期 + IP 节流(AuthController.php:270-294)未取证
站内通知中心✅ 通知表 + 通知页 + 未读筛选(webapi/notifications.go、NotificationsPage.vue)✅ notices/receipts/targets 三表❌✅ notifications + SiteMessage❌
皮肤库检索⚠️ 关键词 LIKE description、2 排序(store/skinlib.go:103-129)✅ hash/名称/上传者 + 游标分页(library.go:41-101)❌ 仅分页⚠️ 任意 sort 列直进 orderBy(SkinlibController.php:69-71)未取证
服务端渲染内容页✅ 首页 + 详情页 + llms.txt + sitemap(texturepage.go、landing.go)❌ SPA 壳❌ SPA 壳⚠️ Twig 壳⚠️ Go 模板整页,SEO 未取证
服务端材质渲染✅ Go 立方体光栅带版本号(avatargen.go:37-44)❌ 仅客户端 WebGL⚠️ 仅截脸✅ 独立包 + WebP 协商 + 缓存(TextureController.php:61-79)未取证
签名插件市场 / 任务运行器✅ Ed25519 签名包 + 注册表 + 持久任务 lease❌❌⚠️ 目录制无签名链❌
正版账号绑定❌ 仅 authsource.go 接口占位未取证⚠️ 仅微软 OAuth 登录—❌ 无 MSA/XBL/XSTS
/publickeys + 证书族❌ 未挂(元数据 signaturePublickey,authlib 口径)未取证未取证❌✅ 整族挂载(main.go:454-470)

注(Drasl 证书族的消费方):authlib-injector 对 /publickeys 与 profile key 都用本地实现回应 (httpd/PublickeysFilter.java:40-61、ProfileKeyFilter.java:48-82),真正消费者是不挂 agent 的 原生客户端(-Dminecraft.api.*.host),不是老启动器。

重量级补录(chrly / HRPAuth,2026-11-15 一手取证) ​

只记本次实际取证的维度;未查维度不写格子。

维度SkinNexuschrly(Ely.by)HRPAuth
纹理签名✅ 自签,公钥走元数据✅ signer/signer.go + /textures/signed/{username}(服务端直发签名 profile)+ 公钥 DER/PEM 双端点—(Ygg 面在兄弟仓)
Mojang 兜底/代理✅ ygg_mojang_fallback✅ 代理 + worker 模式 UUID 解析(api/mojang/mojang.go)—
账号安全深度密码(12 旧算式 + argon2id)+ 邮箱验证 + 失败锁定—✅ TOTP / WebAuthn·passkey / 邮箱二要素 / 恢复密钥 / 验证码(services/webauthn_service.go 等)
API 契约治理错误码目录 + AST 闸;launcher 面 openapi.yaml—✅ HA-Contract 集中 OpenAPI + 错误码表
发布工程GHCR + 三引擎 CIDocker 镜像✅ 6 平台二进制 + 多架构 manifest + digest 钉死 + 配置模板渲染
旧客户端兼容✅ legacy/usm/CustomSkinAPI 路由✅ /skins?name=、/cloaks?name=—
游戏服务端接入无✅ /textures/signed 供服务端 API✅ HRPAuth-Spigot(Velocity)

注:HRPAuth 的 Yggdrasil/皮肤面拆在兄弟仓(HRPAuth-Yggdrasil-API / HASkinLib / HASkinProxy), 本次未读其源码、仅按 README 声明计。两家强项恰好落在总判断的第 2、3 条品类缺口上。

可借鉴的工程纪律 ​

  1. 闭环后再宣传:element-skin「材质文件 LRU 缓存」README 有、源码无;但其 90% 覆盖率闸是真的。
  2. 错误契约不带文案:{object, operation, reason, params} 把失败分类与人话彻底分开。
  3. 公共配置白名单并校验数量:多一个键就报错(publicsite/public.go:64-69)。
  4. 迁移前自动备份(Drasl 默认开):我方迁移引擎只进不退,破坏性变更前靠手工备份(deploy.md §2.6)。
  5. 未实现就 501:声明面与实现面分离,不伪装成功。
  6. 多前缀挂一个 handler:协议方言差异集中在挂载层。
  7. Webhook 投递器(element-skin):两阶段 DB claim + lease、退避、死信、SSRF 防护 DialContext、 投递前权限复核——我方 internal/jobs 已有 lease/退避,差距在死信与投递前复核。
  8. 不留长期兼容分支:两家 Go 竞品都无 v1/v2 双写包袱。

各家安全与正确性反面教材 ​

仅作技术对照,不作宣传素材。

  • vSkin:限流与 OAuth state 都存进程内存(重启即失效);材质头基址与实际挂载点不一致; 删除没删到行也返回 ok(site_routes.py:209-215)。
  • element-skin:DeleteFile 全路径零调用,行删了文件永留盘上;RecountUsage 无生产调用方。
  • blessing:举报处置 delete 跳过引用计数守卫(ReportController.php:112-113),共享内容连坐; 验证链接永不过期;列表排序列名来自请求直进 orderBy。
  • Drasl:/web/texture/resolved/{token} 的 AEAD token 无时间戳(common.go:52-73), 「防缓存」URL 实际永久有效;services.go:69 cape 仍是 TODO。

AGPL-3.0 · SkinNexus