Documentation
¶
Overview ¶
Package licensx 提供 Ed25519 license 签发/验签机制内核:JSON 载荷 + Ed25519 签名(覆盖除 signature 外全部字段)+ 到期/吊销判定 + 类型化哨兵 + 公钥注入。 纯 stdlib(crypto/ed25519 + encoding/json)。载荷结构是宿主产品决策—— customer/features/实例数等业务字段不入本包,内核收任意 JSON 载荷(固定序 序列化钩子:宿主用 json.Marshal 宿主 struct 产出,禁 map)。
包名循 pathx/stringx/secretx 的 x 后缀惯例(#486);另一层原因:子包目录 名 license 会与 macOS/Windows 大小写不敏感文件系统上的仓根 LICENSE 文件 冲突,无法共存检出。
密钥保管边界 ¶
签发私钥只在运营方(签发侧)手里,永不入仓、不进配置中心、不下发到验签侧。 本包不持有任何密钥常量:验签公钥由宿主注入(编译进二进制是防篡改惯例—— 改环境变量、换文件、改配置都拿不到授权能力;「公钥怎么交付」是宿主策略, 包只提供注入点)。轮换:Keygen 生成新密钥对,私钥落仓外路径,公钥随宿主 重新编译/分发——旧 license 将全部失效(签名对不上),轮换窗口要与客户约定。
fail-loud ¶
验签失败一律返回类型化哨兵错误(ErrSignatureInvalid / ErrExpired / ErrRevoked),消费方一律 errors.Is 判定,不要匹配错误文本。本包不做任何 静默降级:「降级不静默」的机制语义由 Load 兑现——非有效形态必带 Reason 可观测;「无效 license 拿不到授权能力」由宿主把 Status.Valid 映射到自己的 档位来兑现(community/enterprise 分支与能力词表是产品决策,留在宿主)。
典型用法 ¶
// 签发侧(运营方工具,私钥不出仓):
pub, priv, err := licensx.Keygen()
payload, err := json.Marshal(hostClaims{Customer: "acme", Features: []string{"sso"}})
lic := &licensx.License{
Serial: "2026-001",
IssuedAt: time.Now().UTC(),
Payload: payload, // 固定序序列化:json.Marshal 宿主 struct
}
err = licensx.Sign(lic, priv)
// 验签侧(产品二进制,公钥编译进宿主):
st := licensx.Load("/etc/app/license.json", compiledPub, time.Now())
if !st.Valid {
// community 档位运行,st.Reason 透出原因(不静默)
}
var claims hostClaims
err = json.Unmarshal(st.Payload, &claims) // 载荷按宿主结构解码
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrSignatureInvalid 签名校验失败(文件被篡改 / 公钥不匹配 / 签名或公钥 // 格式非法 / 载荷不是合法 JSON)。fail-loud:不提供任何「验签失败当有效」 // 的降级路径。 ErrSignatureInvalid = errors.New("license 签名校验失败") // ErrExpired license 已过期(Validate 判定;ExpiresAt nil/零值 = 永久授权)。 ErrExpired = errors.New("license 已过期") // ErrRevoked license 序列号在吊销清单内(清单由宿主从签发侧分发/拉取)。 ErrRevoked = errors.New("license 已吊销") )
哨兵错误(errors.Is 判定,消费方不要匹配错误文本)。
Functions ¶
Types ¶
type License ¶
type License struct {
// Serial 序列号(签发侧编号;吊销清单的键。可选,但要做吊销就必须有)。
Serial string `json:"serial,omitempty"`
// IssuedAt 签发时间。
IssuedAt time.Time `json:"issued_at,omitempty"`
// ExpiresAt 到期时间;nil/零值 = 永久授权。
ExpiresAt *time.Time `json:"expires_at,omitempty"`
// Payload 宿主载荷(任意 JSON)。必须固定序序列化:用 json.Marshal 宿主
// struct 产出(字段序 = 声明序),禁 map——键序随机则同一 license 两次
// 序列化字节不同,签名随之不确定。签名覆盖本字段的字节(经 encoding/json
// 紧凑化,键序保持)。
Payload json.RawMessage `json:"payload"`
// Signature base64 的 Ed25519 签名(覆盖除本字段外的全部字段)。
Signature string `json:"signature,omitempty"`
}
License license 文件内容(JSON 信封):载荷是宿主定义的任意 JSON,签名覆盖 除 Signature 外的全部字段——改任何一项都会验签失败。
type Status ¶
type Status struct {
// Loaded 文件存在且 JSON 解析成功(license 骨架可读)。
Loaded bool `json:"loaded"`
// Valid 签名 + 未过期 + 未吊销 全过。
Valid bool `json:"valid"`
// Serial 序列号(解析出来即透出,无论有效与否——吊销对账用)。
Serial string `json:"serial,omitempty"`
// ExpiresAt 到期时间(nil = 永久)。
ExpiresAt *time.Time `json:"expires_at,omitempty"`
// Payload 载荷原始字节(Valid 后宿主自行解码到自己的 claims 结构)。
Payload json.RawMessage `json:"payload,omitempty"`
// Reason 未装/无效/已过期/已吊销的原因(完全有效时为空)——不静默。
Reason string `json:"reason,omitempty"`
}
Status license 加载结果(机制结论,不含档位决策——Valid → 宿主档位的 映射是产品侧的事)。自解释:非有效形态必带 Reason(降级不静默)。
type ValidateOption ¶
type ValidateOption func(*validateConfig)
ValidateOption Validate 的选项(命名循仓规:XxxOption,禁裸 Option)。
func WithRevokedSerials ¶
func WithRevokedSerials(serials []string) ValidateOption
WithRevokedSerials 注入吊销清单(序列号集合;由宿主从签发侧分发/拉取并 传入)。nil/空 = 显式无吊销(回退默认,不算非法)。