VeriRandom公平抽取
首页/插件

扩展抽取能力,
但不越过公平边界

插件可以加页面、加服务、加悬浮窗按钮,也可以通过受控入口发起抽取。 它不能绕过授权、不能直接写历史,也不能替换证明链。

SRPX 包 Plugin SDK 签名索引 桌面限定

插件是什么

一个插件就是一个 .srpx 包:本质上是 ZIP,根目录放一份 manifest.yml,入口程序集里有一个非抽象的实现类。

  • 插件在应用启动、宿主构建之前加载,因此可以注册自己的服务与页面
  • 插件页面使用 plugin.<id>.* 命名空间,由插件自己拥有
  • 插件目录在 data/plugins;待安装的包先放在缓存目录等待激活
  • 动态插件只支持桌面端,手机与平板宿主不会加载

最小清单

manifest.yml
id: com.example.hello
name: Hello Plugin
description: 一个最小插件
version: 1.0.0
apiVersion: 3.0.0
entranceAssembly: HelloPlugin.dll
author: your-name
icon: icon.png
permissions: [UserInterface]
permissions 是声明式策略,不是沙箱:插件与宿主同进程、以完全信任运行。 它的用途是让插件自我约束、让安装时看得见、让宿主能提示。不写该字段表示「未声明」,而不是「什么都不允许」。

插件能做什么

添加页面与设置项

注册主页面、设置页或设置分组,使用与内置页面相同的注册助手与导航元数据。

注册服务

在 Initialize 中向宿主注册自己的服务与 Core 扩展,参与依赖注入。

悬浮窗按钮

在运行时注册悬浮窗按钮;是否显示由用户在悬浮窗设置里勾选,插件消失时对应项会被清理。

受控发起抽取

通过 IPluginDrawService 发起点名或抽奖,授权、课程限制与事务提交都由宿主负责。

API 边界

插件可用的稳定契约都在 Core 的 Abstract/Services 下,通过依赖注入获取。

契约用途
IMainView / ISettingsView / IDrawerView在 UI 线程上访问主界面、设置与抽取区域;空壳状态下是安全空操作
IAppNavigationService打开主窗口、设置窗口或闪抽窗口,保留原有的安全授权闸门
IAppLifecycleService转发应用启动与停止事件,供插件做初始化与清理
IFloatingWindowButtonRegistry运行时注册悬浮窗按钮与其描述
IPluginDrawService受控抽取入口:先授权,再复用点名 / 抽奖的完整流程

明确禁止

  • 不得直接调用事务提交服务、抽取会话或历史写入接口——那会绕过授权、课程限制与证明发布
  • 不得替用户跳过安全验证,也不得自行实现一套令牌或凭据逻辑
  • 不得把 API 密钥、名单或证明内容写进日志、遥测或 IPC 载荷

安装与信任链

插件可以手动安装 .srpx 包;如果使用可选的在线索引,索引与每个安装包都要经过校验:

签名索引

整个索引用 Ed25519 签名;客户端内置公钥,先验签再读取条目。

依赖闭包

按拓扑顺序解析插件依赖,检测循环;缺少依赖或版本不兼容时明确报错,而不是装上一半。

包哈希

每个包在安装前校验 SHA-256,然后交给插件管理器走统一的暂存与激活流程。

兼容性

用完整应用版本判断最低宿主版本;插件声明的 API 主版本必须与应用主版本一致。

在线索引只是获取插件的一种方式。用本地包安装走完全相同的校验与暂存流程,不需要账号或登录。

打包与开发

用 SDK 构建,把输出目录压缩为 .srpx,并确保 manifest.yml 位于包根目录。

终端
# 1. 引用与宿主主版本匹配的 SDK
dotnet add package SecRandom.PluginSdk

# 2. 构建
dotnet build -c Release

# 3. 打包:manifest.yml 必须在压缩包根目录
Compress-Archive -Path .\bin\Release\net10.0\* -DestinationPath .\HelloPlugin.zip
Move-Item .\HelloPlugin.zip .\HelloPlugin.srpx
  • SecRandom.Shared、SecRandom.Core 与 SecRandom.PluginSdk 会一起发布,版本与应用版本同步
  • 打包时排除宿主已经提供的程序集(应用、Avalonia、FluentAvalonia、运行时),避免包体冲突
  • 示例插件在仓库的 SecRandom.ExamplePlugin,可以作为最小骨架
  • 开发期间可以用 --externalPluginPath(可重复)指向外部插件目录

生命周期与限制

  • 入口继承 PluginBase:Initialize 注册服务,OnAppStarted / OnAppStopping 处理运行时生命周期
  • 宿主关闭前会逐个释放插件入口,插件应在这里停止定时器、IPC 与文件句柄
  • 插件导致未处理异常时,下次启动可以自动禁用该插件,避免反复崩溃
  • 卸载时会保留插件的配置目录,重新安装后可以继续使用
  • 插件页面 ID 必须在应用范围内唯一,不能与内置页面或其他插件冲突

先跑起来,再写插件

文档页有完整的构建命令与项目职责表;示例插件是最小可运行骨架。