梨窝 Agent 发文功能指南——从零开始调通非对称 API
梨窝 Agent 发文功能指南
梨窝的 /api/v1/notes 接口提供了一套完全脱离浏览器端「密码 + 验证码 + cf-trace」的非对称公私钥鉴权专线。自动化程序、AI 助手或 CI/CD 脚本可以直接通过它来发布和管理梨记文章。
为什么需要 Agent 接口?
普通的管理后台需要:
- 密码登录
- 验证码(cf-trace)
- 浏览器环境 + JavaScript
对于 AI 助手或自动化脚本来说,这些步骤太重了。Agent 接口只需要一个 JWT,拿着私钥签个名就能调通所有发文接口。
第一步:生成密钥对
使用 Ed25519 算法(PEM 格式):
openssl genpkey -algorithm ed25519 -out agent.key
openssl pkey -in agent.key -pubout -out agent.pub
agent.pub→ 放到服务器项目根目录,改名为.agent.pubagent.key→ 由发文方保管,用于签发 JWT
第二步:签发 JWT 并调用
安装依赖:
pip install pyjwt requests
签发 JWT:
import time, jwt
with open('agent.key', 'rb') as f:
key = f.read()
token = jwt.encode({
'sub': 'agent',
'name': '任意显示名',
'role': 'agent',
'exp': int(time.time()) + 300 # 5 分钟有效期
}, key, algorithm='EdDSA')
然后带上 token 请求:
curl -H "Authorization: Bearer $TOKEN" https://你的域名/api/v1/notes
接口一览
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/v1/notes | 获取全部文章列表 |
| GET | /api/v1/notes/{slug} | 获取指定文章详情 |
| POST | /api/v1/notes | 创建新文章 |
| PUT | /api/v1/notes/{slug} | 更新指定文章 |
| DELETE | /api/v1/notes/{slug} | 删除指定文章 |
POST / PUT 请求体:
{
"title": "文章标题",
"tags": ["标签1"],
"excerpt": "摘要",
"content": "Markdown 正文"
}
踩过的坑:jsonwebtoken 的 InvalidAlgorithm bug
最开始怎么调都过不了验证,服务器日志一直报:
Agent public key signature verification failed for token
但同样的 JWT 和公钥在本地 Rust 单元测试里却能通过。最后定位到是 jsonwebtoken v10 的一个 bug —— 当 Validation.algorithms 同时包含 EdDSA 和其他算法(如 RS256/RS384/RS512)时,EdDSA 签名验证会直接返回 InvalidAlgorithm,即使 EdDSA 本身就在列表里。
修复很简单:把显式设置 algorithms 的这行删掉,用 Validation::new(Algorithm::EdDSA) 的默认值(只含 EdDSA)就行了。
- let mut validation_asym = Validation::new(Algorithm::EdDSA);
- validation_asym.algorithms = vec![Algorithm::EdDSA, Algorithm::RS256, Algorithm::RS384, Algorithm::RS512];
+ let validation_asym = Validation::new(Algorithm::EdDSA);
注意事项
- JWT 有效期建议不要太长(5 分钟够了),每次调用前重新签一个
- 公钥是
.agent.pub文件,也可以用LILY_AGENT_PUB_KEY环境变量直接传 - 即使公钥泄露,攻击者没有本地私钥也无法伪造 JWT
- 目前仅支持 EdDSA(Ed25519),暂时不支持 RSA 系算法
- 增删改操作会自动触发服务端缓存的重载,不需要重启
附:通用发文脚本
改一下 SERVER_URL 和 KEY_PATH 就能直接用:
import json, sys, time, requests
from pathlib import Path
import jwt
SERVER_URL = "https://你的域名"
KEY_PATH = Path("agent.key")
def make_jwt():
with open(KEY_PATH, "rb") as f:
key = f.read()
return jwt.encode({
"sub": "agent",
"name": "AI助手",
"role": "agent",
"exp": int(time.time()) + 300,
}, key, algorithm="EdDSA")
# 调用示例
token = make_jwt()
resp = requests.post(
f"{SERVER_URL}/api/v1/notes",
json={"title": "标题", "tags": ["标签"], "excerpt": "摘要", "content": "# 正文"},
headers={"Authorization": f"Bearer {token}"},
)
print("OK" if resp.ok else "FAIL", resp.status_code)
依赖:pip install pyjwt requests