License 管理(License v2)
适用范围
本页面仅 超级管理员(super_admin) 可见。普通用户被 License 限制时,会在请求被拒后看到 HTTP 402 错误码及友好提示。
Sira AI 通过 License v2 控制功能可用性。每个企业部署都需要一个 RSA 签名的 .lic 授权文件,文件中声明了允许使用的功能(feature_set)、有效期和资源上限。
核心概念
授权文件 (.lic)
.lic 是一段 离线签名 的 JSON:
{ header, license, signature } # RSA-PSS / SHA-256 / MGF1- 由独立的
license-server/工具链生成(私钥保存在签发方,永远不进后端) - 后端仅持有公钥,校验签名,不可伪造
- 上传后会写入
license_v2_org_grants表,缓存进内存网关
授权范围(Grant)
一个 grant 既可以是 全局(适用整个部署),也可以是 某个组织专属:
| 类型 | organization_id | 用途 |
|---|---|---|
| 全局 grant | NULL | 单租户私有化部署的默认 grant,或多租户的 fallback |
| 组织 grant | 指定 org id | 只对该租户生效 |
查询时优先匹配组织 grant,找不到再回落到全局 grant。
License 等级
四级,向下兼容:
TRIAL (0) < STANDARD (1) < PROFESSIONAL (2) < ENTERPRISE (3)授予 PROFESSIONAL 自动包含所有 STANDARD 和 TRIAL 功能。
运行模式(LICENSE_V2_MODE)
通过环境变量 LICENSE_V2_MODE 控制网关行为:
| 模式 | 行为 | 适用场景 |
|---|---|---|
off | 完全跳过校验,所有功能放行 | 仅开发环境 |
warning | 允许执行但记录审计日志 | 上线前观测期 |
grace | 距离首次安装 30 天内放行;之后按 enforce | 新部署默认值 |
enforce | 缺失功能直接拒绝 (HTTP 402) | 生产环境 |
默认值是
grace,30 天宽限期从license_grace_period表中第一条记录写入时算起。超过期限后必须导入正式.lic才能继续使用受限功能。
功能目录(22 项)
按命名空间分组,最低等级 表示购买该等级及以上才包含此功能:
渠道接入 (channel.*)
| 功能键 | 中文名 | 最低等级 |
|---|---|---|
channel.web_client | Web 客户端渠道 | TRIAL |
channel.wecom | 企业微信渠道 | STANDARD |
channel.feishu | 飞书渠道 | STANDARD |
channel.openai_compat | OpenAI 兼容接口 | STANDARD |
channel.weixin_kf | 微信客服渠道 | PROFESSIONAL |
channel.langgraph_compat | LangGraph 兼容接口 | PROFESSIONAL |
编排器 (orchestrator.*)
| 功能键 | 中文名 | 最低等级 |
|---|---|---|
orchestrator.single | 单智能体编排 | TRIAL |
orchestrator.supervisor | 监督者编排 | STANDARD |
orchestrator.collaboration | 协作编排 | STANDARD |
orchestrator.workflow | 工作流编排 | STANDARD |
orchestrator.conditional | 条件路由编排 | PROFESSIONAL |
orchestrator.external | 外部系统编排 (Dify / n8n) | PROFESSIONAL |
orchestrator.deep_agents | DeepAgents 编排 | PROFESSIONAL |
orchestrator.knowledge_base | 知识库编排 | PROFESSIONAL |
沙箱 (sandbox.*)
| 功能键 | 中文名 | 最低等级 |
|---|---|---|
sandbox.user_pc | 员工 PC 沙箱 | PROFESSIONAL |
sandbox.dedicated | 企业专用沙箱 | ENTERPRISE |
sandbox.decision_audit | AI 决策审计 | ENTERPRISE |
技能包 (skill.*)
| 功能键 | 中文名 | 最低等级 |
|---|---|---|
skill.basic | 基础技能包 | STANDARD |
skill.import_export | 技能包导入 / 导出 | PROFESSIONAL |
skill.version_history | 技能包版本历史还原 | PROFESSIONAL |
部署形态 (deploy.*)
| 功能键 | 中文名 | 最低等级 |
|---|---|---|
deploy.dmz_bridge | DMZ 中转部署 | ENTERPRISE |
deploy.multi_tenant | 多租户隔离 | ENTERPRISE |
上传 / 更新授权文件
- 进入 管理员后台 → License 管理
- 点击右上角「导入授权」
- 选择本地
.lic或.json文件 - 后端 RSA 校验通过后立即生效,无需重启
过期处理
当全局 grant 的 valid_until 早于当前时间时,平台会触发 LicenseExpirationMiddleware,几乎所有 API 都会返回 HTTP 402 LICENSE_EXPIRED。以下路径不受过期影响(避免管理员被锁死):
/health/api/auth/login、/api/auth/refresh/api/admin/license/*(用来上传新授权)/docs、/redoc、/openapi.json/api/agent-system/sandboxes/install/*、enrollment/consume、by-id/*(保证已部署的员工沙箱仍能联通)
管理界面分页
/admin/license 顶部状态条显示:当前模式、当前等级、生效范围(org id 或"全局")、宽限期剩余天数。
| 标签 | 说明 |
|---|---|
| 功能权限 | 按命名空间分组列出 22 个功能键,标注「已授权 / 未授权」、最低等级、描述 |
| 资源使用 | 用量条 (人数 / 并发) 和 grant limits 字典 |
| 用量审计 | 近 7 天的 allow / deny 总量;分页查看每条 license_usage_events,可按 outcome 过滤 |
| 系统信息 | 部署指纹(取代旧版硬件 ID) |
受限请求的响应格式
后端拒绝时返回 HTTP 402,body:
{
"code": "FEATURE_NOT_LICENSED",
"feature": "sandbox.dedicated",
"feature_display_name": "企业专用沙箱",
"required_tier": "ENTERPRISE",
"current_tier": "PROFESSIONAL",
"organization_id": "org_default",
"message": "...",
"upgrade_url": null
}upgrade_url 由环境变量 LICENSE_UPGRADE_URL 配置,用于在前端引导用户升级。
用量审计
每次 gate.require() 命中(无论放行还是拒绝)都会写一条 license_usage_events:
| 列 | 说明 |
|---|---|
event_id | 16 字节十六进制,UNIQUE,至少一次去重 |
feature | 功能键 |
quantity | 默认 1 |
occurred_at | 客户端时间 |
extra_data | {outcome, source, mode, reason, current_tier, required_tier} |
与旧版 v1 区别
- v1 的
licenses/license_usages/license_events三张表已在 migration 013 中删除 - v1 的"按文件路径"中间件被精简为只剩过期检查,每个具体功能改为代码里
@require_feature装饰器 - v1 的 6 个 legacy feature key(
ai_bot/web_access/mcp_tools等)已经移除
开发者:在新接口上添加授权检查
依赖注入路径(FastAPI + Depends(get_current_user)):
from app.services.license_v2 import require_feature
@router.post("/sandboxes/dedicated")
@require_feature("sandbox.dedicated")
async def create_dedicated_sandbox(
payload: DedicatedSandboxCreate,
current_user: dict = Depends(get_current_user),
):
...非 DI 路径(Bearer token 或内部调用):
from app.services.license_v2 import get_gate
get_gate().require("sandbox.decision_audit", organization_id=org_id)两种方式拒绝时都会抛 FeatureNotLicensed,由全局异常处理器映射为 HTTP 402。
相关文档
- Sandbox 集成总览(依赖
sandbox.user_pc/sandbox.dedicated) - 技能包管理(依赖
skill.basic等) - Channel Gateway DMZ 中转(依赖
deploy.dmz_bridge)
