REST API:管理端点
本页是 REST API 参考 的一部分:Token、网络成员、文件、节点改名、调试与 Legacy 端点。基础约定(地址、认证、错误格式)见总览。
Token 管理端点
需要用户令牌。 节点令牌不能列出、签发或撤销令牌,返回 403 user_token_required。
POST/api/auth/node-token
为某个节点创建网络绑定的 ntok_。anet node create 会自动调用它,写入到 .anet/nodes/<node-name>/config.json 的 token 字段。
curl -X POST http://localhost:9200/api/auth/node-token \
-H "Authorization: Bearer utok_xxx" \
-H "Content-Type: application/json" \
-d '{"network_id": "net_xxx", "node_name": "代码1号"}'响应(成功):
{
"ok": true,
"token": "ntok_xxxxxxxxxxxxxxxx"
}token 是该 (node_name, network_id) 组合的 ntok_,hub 端强制 binding——agent 用这个 token 调 MCP 时,server 自动锁定到 network_id,跨网络访问拒绝。详见 Token 概念 — ntok_。
常见 4xx(verify auth.ts createNetworkTokenForNode() + server.ts route):
节点身份按精确绑定校验:带 node_id 的签发和只带名字的签发都会检查同网络的名字冲突。新名字令牌在首次成功注册时固定 node_id;已存在节点的名字刷新需要节点属主或同一持有人已有的有效节点凭据,刷新后返回精确绑定的令牌。不会通过刷新补填旧节点的 owner。
有意的行为变化: 如果一个旧节点没有 owner,也没有任何历史节点令牌,管理员不能再仅凭知道 alias 为它签发令牌。网络管理员身份不等于节点归属证明。本次不提供隐式认领;显式、有审计的管理员认领流程将另行设计,当前不可用。
旧 daemon 的未绑定凭据保留兼容:alias 必须在网络内唯一;旧无 owner 节点只接受修复前签发的凭据。重复 alias 不按数据库行顺序猜测身份,需使用已经精确绑定的凭据。
升级会持久化令牌签发代际。全部签发服务(包括直接写库的 CLI)必须一起升级,不能混跑旧版 Hub 或旧签发器,也不能回滚到旧签发器,否则新签发的令牌会被误归为旧代际;数据库备份须保留该字段。恢复应使用包含本修复的版本。该兼容层不等于对消息署名或 SSE 全链路身份的验收。
| 状态 | error 值 | 触发条件 |
|---|---|---|
| 400 | network_id and node_name required | 请求体缺 network_id 或 node_name |
| 400 | not a member of this network | 调用者不在 network_id 内(必须先 join 才能 mint ntok_) |
| 400 | no write access to this network | 调用者是 viewer 角色(viewer 不能创建 full-access network token) |
| 400 | node_owner_mismatch | 节点名冲突、身份歧义或持有人无权签发 |
| 400 | node_owner_unclaimed | 指定的旧无 owner 节点缺少同持有人凭据;不会隐式认领 |
| 401 | auth required / invalid token | 缺/无效 utok_ |
POST/api/auth/tokens
创建 API Token。
curl -X POST http://localhost:9200/api/auth/tokens \
-H "Authorization: Bearer utok_xxx" \
-H "Content-Type: application/json" \
-d '{"name": "my-agent", "network_id": "net_xxx"}'响应:
{
"ok": true,
"token": "atok_xxxxxxxxxxxxxxxx",
"token_id": "tok_abc123def456"
}Token 明文只返回一次
token 字段是明文 Token,仅在创建时返回这一次——hub 端只存 hash。丢失后请用 DELETE /api/auth/tokens/:id 撤销 + 重新创建。
这个 endpoint 创建的是 legacy atok_
本 endpoint 走 auth.ts 搜 generateToken(全仓 3 处) 颁发 atok_ 前缀 + scope='full' token,是 V2 时代的兼容路径,不是 v0.8 主线的 utok_ / ntok_。新代码请用:
utok_(用户 Token):通过 POST /api/auth/login 或 POST /api/auth/register 自动颁发ntok_(节点 Token):通过 POST /api/auth/node-token 创建(绑定到指定 network + 节点 alias)
详见 Token 体系。
GET/api/auth/tokens
列出用户的所有 Token。
curl http://localhost:9200/api/auth/tokens \
-H "Authorization: Bearer utok_xxx"响应:
{
"ok": true,
"tokens": [
{
"token_id": "tok_abc123def456",
"name": "node:代码1号",
"scope": "network",
"network_id": "net_xxxxxxxx",
"last_used_at": "2026-04-12 10:00:00",
"created_at": "2026-04-10 09:00:00"
},
{
"token_id": "tok_xyz789",
"name": "user-login",
"scope": "user",
"network_id": null,
"last_used_at": null,
"created_at": "2026-04-12 10:30:00"
}
]
}每行 6 字段对照 auth.ts listTokens listTokens SELECT:token_id / name / scope / network_id / last_used_at / created_at。scope 取值 user (utok_) / network (ntok_) / full (legacy atok_);network_id 仅 network / full scope 有值。按 created_at DESC 排序。明文 Token 字段不返回(只能在 POST 创建时拿一次)。
DELETE/api/auth/tokens/:id
撤销 Token(hub 端立即吊销)。anet logout 撤销的是当前登录会话(走 DELETE /api/auth/sessions/:token_id),不撤显式 API token。
curl -X DELETE http://localhost:9200/api/auth/tokens/tok_xxx \
-H "Authorization: Bearer utok_xxx"响应(成功):
{ "ok": true }4xx:
| 状态 | error 值 | 触发条件 |
|---|---|---|
| 404 | token not found | token_id 不存在或不属于当前 user(auth.ts revokeToken DELETE ... WHERE token_id=?1 AND user_id=?2 受影响行 0) |
写 audit log action='token_revoked'。撤销后该 token 的下一次请求拿 401 invalid token。
网络成员端点
需要用户令牌。 节点令牌不能列出、邀请、加入、改角色或移除成员,返回 403 user_token_required。
GET/api/networks/:id/members
获取网络成员列表(仅 owner / admin)。
curl http://localhost:9200/api/networks/net_xxx/members \
-H "Authorization: Bearer utok_xxx"响应:
{
"ok": true,
"members": [
{
"user_id": "u_abc123",
"username": "alice",
"display_name": "Alice",
"role": "owner",
"joined_at": "2026-04-12 10:00:00"
},
{
"user_id": "u_def456",
"username": "bob",
"display_name": "Bob",
"role": "member",
"joined_at": "2026-04-15 14:30:00"
}
]
}anet network members CLI 用这个响应渲染成员列表(按 m.display_name || m.username 显示,role 加 emoji 图标)。
POST/api/networks/:id/members
添加网络成员(owner / admin only;通常 invite 流程更顺,POST /api/networks/:id/invite 创建邀请码让对方自行加入)。
curl -X POST http://localhost:9200/api/networks/net_xxx/members \
-H "Authorization: Bearer utok_xxx" \
-H "Content-Type: application/json" \
-d '{"user_id": "u_def456", "role": "member"}'请求体:
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
user_id | string | ✓ | 目标用户 ID |
role | enum | admin / member / viewer(默认 member) | |
agent_access | enum | granted(默认,只看授权的 Agent)/ all(完全信任的成员);见用户与 Agent 权限端点 |
响应(成功):
{ "ok": true }常见 4xx:
| 状态 | error 值 | 触发条件 |
|---|---|---|
| 403 | not a member of this network | 调用者本身不在该网络 |
| 403 | owner/admin required | 调用者是 member / viewer,无权添加成员 |
| 400 | user already a member | user_id 已经是该网络成员 |
写 audit log action='member_added',detail 字段记 <user_id> as <role>。
PUT/api/networks/:id/members/:user_id
修改成员角色(仅 owner,不能修改 owner 自己的角色)。
curl -X PUT http://localhost:9200/api/networks/net_xxx/members/u_def456 \
-H "Authorization: Bearer utok_xxx" \
-H "Content-Type: application/json" \
-d '{"role": "admin"}'请求体:
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
role | enum | ✓ | 新角色:admin / member / viewer(不能改成 owner) |
响应(成功):
{ "ok": true }常见 4xx:
| 状态 | error 值 | 触发条件 |
|---|---|---|
| 403 | not a member of this network | 调用者本身不在该网络 |
| 403 | owner required | 仅 owner 能改角色(admin 也不行) |
| 400 | cannot assign owner role | role 字段传 owner,server 拒绝(owner 通过创建网络获得,不能后续 promote) |
| 400 | member not found or is owner | 目标 user_id 不在网络内,或者是 owner 自己(owner 角色不可改) |
写 audit log action='member_role_changed',detail 字段记 <user_id> → <new_role>。FAQ Q17 提到的「改角色」入口就是这个 endpoint。
DELETE/api/networks/:id/members/:user_id
移除成员(owner / admin only,不能移除 owner 自己)。
curl -X DELETE http://localhost:9200/api/networks/net_xxx/members/u_def456 \
-H "Authorization: Bearer utok_xxx"响应(成功):
{ "ok": true }常见 4xx:
| 状态 | error 值 | 触发条件 |
|---|---|---|
| 403 | not a member of this network | 调用者本身不在该网络 |
| 403 | owner/admin required | 调用者是 member / viewer,无权移除成员 |
| 400 | not a member | 目标 user_id 不在该网络 |
| 400 | cannot remove owner | 目标是 owner(删除网络才能移除 owner,见 DELETE /api/networks/:id) |
写 audit log action='member_removed',detail 字段记 <user_id>。
同一个事务里吊销被移除者在这个网络的所有令牌(节点令牌、邀请码令牌,revoked_at 置为当前时间;他在别的网络的令牌不动)。即使是移除前留下、没被吊销的老令牌,Hub 也只认仍是该网络成员的用户的网络令牌:非成员的令牌在所有 REST / MCP / SSE 路径上返回 401,reason: "not_network_member"。受限成员(Agent 访问只到授权的 Agent)的网络令牌同样是 401,reason: "node_owner_restricted",两者都带 hint 说明怎么恢复。
POST/api/networks/:id/invite
创建邀请码。
curl -X POST http://localhost:9200/api/networks/net_xxx/invite \
-H "Authorization: Bearer utok_xxx" \
-H "Content-Type: application/json" \
-d '{"role": "member", "max_uses": 5, "expires_days": 7}'请求体:
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
role | enum | admin / member / viewer(默认 member) | |
max_uses | number | 最大使用次数(默认 1;-1 无限) | |
expires_days | number | 过期天数(不传则不过期) |
响应(成功):
{
"ok": true,
"invite_code": "inv_abc123def456"
}常见 4xx(verify auth.ts createInvite() + server.ts route handler):
| 状态 | error 值 | 触发条件 |
|---|---|---|
| 400 | invalid role | role 不是 admin / member / viewer 之一 |
| 403 | not a member of this network | 调用者本身不在该网络(server.ts callerRole gate) |
| 403 | owner/admin required | 调用者是 member / viewer,无权 issue 邀请码 |
接收方用 anet network join inv_abc123def456 或 POST /api/networks/join 加入。invite_code 是 inv_ 前缀 + 12 字符(auth.ts createInvite slice(0, 12))。
POST/api/networks/join
用邀请码加入网络。
curl -X POST http://localhost:9200/api/networks/join \
-H "Authorization: Bearer utok_xxx" \
-H "Content-Type: application/json" \
-d '{"invite_code": "inv_abc123def456"}'响应(成功):
{
"ok": true,
"network_id": "net_abc123",
"role": "member"
}常见 4xx(verify auth.ts joinByInvite()):
| 状态 | error 值 | 触发条件 |
|---|---|---|
| 400 | invalid invite code | invite_code 不存在 |
| 400 | invite code fully used | used_count >= max_uses(max_uses=-1 无限) |
| 400 | invite code expired | expires_at < now()(不传 expires_days 创建则不会过期) |
| 400 | already a member of this network | 调用者已是该网络成员 |
anet network join CLI 拿到该响应后会自动切换到加入的 network(即 ~/.anet/config.json 的 network_id 字段更新为 res.network_id),并打印 Joined network as <role>。同时 server 自动颁发一个 network_id 绑定的 token 给加入者(auth.ts 搜 "auto-join", "full" name='auto-join' scope='full'),写 audit network_joined。
用户与 Agent 权限端点
需要用户令牌。 节点令牌调用这些接口返回 403 user_token_required。
多用户账号:Hub 管理员(或网络 owner / admin)建号,新成员默认看不到任何 Agent,在「可访问的 Agent」里逐个授权。人与人之间的私信不受影响。
判定规则(server/src/agent-access.ts):网络里 role 为 member / viewer、agent_access 不是 all、且不是 Hub 管理员的用户是受限成员。受限成员:
只看得到授权给他的 Agent(
/api/status、/api/nodes、MCPget_all_status/get_session_status、需求看板的人员选择器);只能给
can_message=true的授权 Agent 发任务 / 消息,发件人固定为自己的用户名;任务与 task_events(
/api/tasks、/api/tasks/:id、/api/task_events、MCPlist_tasks/get_task):看得见某个 Agent(直接或经组授权)就看得见它的整条时间线 —— 谁发的都算(包括 owner 和其他成员),也包括授权之前的历史,和 owner 打开同一个节点看到的一样(#563)。另一端是他看不见的 Agent 的行仍然隐藏。inbox(/api/messages的 alias 分支)仍只看自己与授权 Agent 之间的往来;不能订阅 Agent 的 SSE 频道(即使已授权——那个频道推的是所有人发给它的任务原文),网络观察流收到自己是一端的路由事件,以及授权 Agent 时间线上的路由事件(另一端是看不见的 Agent 的不推);
不能持有网络令牌(
ntok_/ 邀请码令牌):签发被拒,升级前已签发的在他变成受限后解析失败;文件只能下载自己上传的、对方(授权 Agent / 私信发件人)发给他的附件,以及授权 Agent 时间线上别人发出的附件与 Agent 回复的附件;也不能把看不见的
file_id当附件转给 Agent;其余面向 Agent 的端点(节点配置 / 日志 / 文件 / 规则 / 改名 / 排程 / 创建节点 / 广播 / 统计)对受限网络 fail-closed:整网不返回、写入 403;没列在白名单里的 MCP 工具返回
agent_access_restricted。授权 ≠ 管理:即使授权了某个 Agent,受限成员也不能读写它的规则文件 / 技能 / 项目文件 / 运行日志,不能改配置、改名、启停 —— 授权的含义是「能看见、能对话」;
排程是建它的人的委托:建的人后来变成受限成员且没被授权给目标 Agent 发任务时,排程不再派发(运行记录
error_code=creator_access_revoked);用户名与某个 Agent 的 alias 撞名(Agent 在他入网之后才注册这个 alias)时,他在这个网络里的用户名频道、「自己的往来」与授权 Agent 的时间线一律关闭,因为「发给他」和「发给那个 Agent」无法区分。
owner / admin 角色与 Hub 管理员不受影响。升级前已存在的成员行 agent_access 默认为 all,可见范围不因升级而变;此后新加入的 member / viewer(管理员建号、POST /members、邀请码)默认 granted。
⚠️ 授权一个 Agent,等于信任这个人使用该 Agent 能做到的事(Agent 自己的网络令牌能读网络里的文件、调用工具)。
POST/api/admin/users
源码 ↗(
adminCreateUser)
建一个用户。规则与 /api/auth/register 相同(用户名 2–50 位、密码 ≥ 8 且不是常见密码、自动建个人网络),但不返回令牌——用户自己登录才拿令牌。
- Hub 管理员:可以不带
network_id; - 某网络的 owner / admin:必须带自己管理的
network_id(新用户进这个网络);网络 admin 不能建role=admin的成员。
curl -X POST http://localhost:9200/api/admin/users \
-H "Authorization: Bearer utok_xxx" -H "Content-Type: application/json" \
-d '{"username":"alice","password":"<至少 8 位>","display_name":"Alice","network_id":"net_xxx","role":"member"}'响应:
{ "ok": true, "user": { "user_id": "u_abc", "username": "alice", "role": "user" }, "personal_network_id": "net_own", "membership": { "network_id": "net_xxx", "role": "member", "agent_access": "granted" } }| 状态 | error | 触发 |
|---|---|---|
| 400 | username already taken / password must be at least 8 characters / password is too common | register() 的规则 |
| 403 | admin required / owner/admin required | 调用者无权 |
| 404 | network_not_found | network_id 不存在 |
| 409 | username_collides_with_agent_alias | 用户名与该网络某个 Agent 的 alias 相同(用户频道按用户名寻址,撞名会串流量);账号不会落库 |
写 audit log admin_user_created(被拒时 admin_user_create_denied)。
GET/api/admin/users
Hub 管理员专用。返回全部用户,每人带 networks: [{network_id, network_name, role, agent_access}],不含密码哈希。
GET/ PUT /api/networks/:id/members/:user_id/agent-grants
源码 ↗(
replaceAgentGrants)
读 / 整体替换某成员可访问的 Agent。owner / admin / Hub 管理员可调;网络 admin 不能改 owner / 其他 admin。只接受用户令牌。
curl -X PUT http://localhost:9200/api/networks/net_xxx/members/u_abc/agent-grants \
-H "Authorization: Bearer utok_xxx" -H "Content-Type: application/json" \
-d '{"grants":[{"node_id":"node_x"},{"node_id":"node_y","can_message":false}]}'| 字段 | 类型 | 说明 |
|---|---|---|
grants | array | 每项 {node_id} 或 {alias}(只给没有 node_id 的旧会话)+ 可选 can_message(默认 true)。也接受 node_id 字符串数组。整体替换 |
agent_access | all | granted | 可选。all = 解除限制(旧语义),granted = 只看授权的 |
group_grants | array | 可选。授权给 Agent 分组:每项 {group_id} + 可选 can_message,也接受 group_id 字符串数组。不传 = 保持原样(旧客户端只发 grants,不会清掉组授权);传了就整体替换 |
任何一项不是本网络的 Agent → 400 agent_not_in_network、不是本网络的组 → 400 group_not_in_network,整批不写。成功后写 audit log member_agent_grants_changed,并断开该成员已连着的观察流 / 用户流让它按新权限重连。
可见 = 直接授权 ∪ 被授权的组此刻的成员;可对话取并集(任一来源给了就算)。
响应:{ ok, network_id, user_id, agent_access, restricted, grants: [{node_id, alias, can_message}], group_grants: [{group_id, name, can_message}] }(GET 另带 role)。
Agent 分组
源码 ↗(
createAgentGroup/replaceAgentGroupMembers/deleteAgentGroup)· 设计见 RFC-038 §8
管理员自由定义的一组 Agent。授权给组后,组里以后新加的 Agent 自动可见(每次请求现算)。owner / admin / Hub 管理员可调,只接受用户令牌;普通成员与受限成员 403(他们只看得到最终能访问的 Agent)。
| 方法 · 路径 | 说明 |
|---|---|
GET /api/networks/:id/agent-groups | { groups: [{group_id, name, description, node_ids, member_count, granted_user_count, …}] } |
POST /api/networks/:id/agent-groups | {name, description?, node_ids?} 建组;同网络重名 409 group_name_taken |
PATCH /api/networks/:id/agent-groups/:group_id | {name?, description?} |
PUT /api/networks/:id/agent-groups/:group_id/members | {node_ids} 整体替换组成员;响应带 added / removed |
DELETE /api/networks/:id/agent-groups/:group_id | 删组连同组上的授权;响应 affected_user_ids |
节点不是本网络的 → 400 agent_not_in_network,整批不写。审计:agent_group_created / agent_group_renamed / agent_group_members_changed(只记 diff)/ agent_group_deleted,都带 network_id。组成员或组本身变化时,断开该组上有授权的成员的实时流,让其按新权限重连。GET /api/networks/:id/members 每个成员另带 agent_group_count。
GET/api/networks/:id/humans
任何成员(含受限成员)都能调:网络里的人类成员通讯录,只有 user_id / username / display_name。给受限成员选私信对象用(私信走 MCP send_desktop_message)。
用户令牌另得每人的在线状态:online(此刻有没有活着的 /events/users/me 用户流,按人算、不分网络)和 last_seen_at(ISO,连上 / 心跳 / 断开时刷新;只在 Hub 内存里,Hub 重启后未再连过的人为 null,表示未知)。节点令牌拿到的仍只有上面三个身份字段。
某人第一条用户流连上、最后一条断开时,Hub 向他所在每个网络里其他连着用户流的成员推 {"type":"member_presence","member_user_id","online","last_seen_at"}(事件里的 user_id 是收件人自己)。旧客户端忽略未知事件类型。
POST/api/dm · GET /api/dm · GET /api/dm/threads
人与人私信(同一网络里的两个用户;受限成员也可以),只接受用户令牌。
POST /api/dm{network_id?, to_user_id | to_username, message, attachments?, client_request_id?}—— 写进对方的 user_inbox(kind=human_dm),经/events/users/me推送;发信人sender_user_id由 Hub 按令牌写入,请求体里的from一律忽略。同一个client_request_id重试不产生第二条。对方不存在与不在本网络同为 404dm_target_not_in_network。受限成员附带看不见的文件 → 403attachment_not_accessible;附带一个自己看不见的私信文件(purpose=dm上传、自己既不是上传者也不在带着它的私信里)同样 403。私信的附件先用POST /api/upload?purpose=dm上传。GET /api/dm?network_id=&with=<user_id>[&limit&before]—— 我和这个人的双向记录,新的在前,每条带direction: in | out。GET /api/dm/threads?network_id=—— 每个对方一行:{other_user_id, last_at, unread}。 另带group_threads:我在里面的每个部门群一行{group_id, name, department_id, last_at, unread, last_read_seq, last_message},last_message={text(前 80 字;只有附件时为空), attachment_count, sender_user_id, sender_name, at}或null。Hub 的/healthcapabilities含chat_groups时才有群接口。
已读沿用 POST /api/messages/ack(私信就是收件人 user_inbox 里的行)。
GET /api/networks/:id/members 的每一项另带 agent_access(生效值)与 agent_grant_count;POST /api/networks/:id/members 接受可选 agent_access(缺省 granted);GET /api/auth/me 的 networks[] 另带 agent_access,客户端据此显示「还没有被分配任何 Agent,请联系管理员」。
文件端点
附件(图片等)的上传 / 下载,支撑 Dashboard 发图片、commhub 附件、codex-sdk 图片输入等功能。两个端点都需 Authorization: Bearer <token>。
POST/api/upload
上传一个文件,返回可下载的 url。
- 请求:
multipart/form-data,必须带一个file字段,且必须带Content-Length头。 - 大小上限 12 MiB(
MAX_UPLOAD_BYTES),两段校验:先按Content-Lengthfail-fast,再按解析后的实际大小复核。 - 限流:60 次/小时(按 token id 计,无 token 时按 IP),超限返回
429 rate_limited(带X-RateLimit-*头)。 ?purpose=dm(可选):这个文件是为一条人与人私信传的,它就成了私信文件,只有上传者、带着它的私信的收发双方和 Hub 管理员能下载,同网络的其他成员和节点令牌都拿到404 not_found。不带purpose时行为不变;文件先在别处用过(例如 agent 会话)再转进私信,也不会变成私信文件。其它值返回400 bad_purpose。见 人与人私信。
curl -X POST http://localhost:9200/api/upload \
-H "Authorization: Bearer utok_xxx" \
-F "file=@./cover.png"成功 200:
{ "ok": true, "file_id": "...", "url": "/api/files/<file_id>", "size": 12345, "mime": "image/png" }常见错误:411 length_required(缺 Content-Length)· 413 payload_too_large(超 12 MiB,带 limit_bytes)· 415 unsupported_media_type(不是 multipart/form-data)· 400 missing_file(没 file 字段)· 429 rate_limited。
GET/api/files/:file_id
文件下载只接受 Authorization: Bearer ... 请求头。出于凭据泄漏防护, 此端点不接受 ?token= URL 参数(包括 HEAD 请求)。
下载 POST /api/upload 返回的文件。始终强制 Content-Disposition: attachment + X-Content-Type-Options: nosniff(浏览器不 inline 渲染,防 XSS)。
curl -OJ http://localhost:9200/api/files/<file_id> -H "Authorization: Bearer utok_xxx"常见错误:400 bad_file_id(id 格式非法)· 404 not_found(无此文件索引)/ 404 blob_missing(有索引但磁盘上文件不在)。
节点改名端点(RFC-010)
RFC-010 active-rename 两阶段事务的协调端点,由
anet node rename内部调用(流程见 node-lifecycle §7)。一般不直接手调,列在此处供集成方参考。三个端点都要Authorization: Bearer(缺 token 401 / 无效 token 401)。节点令牌只能改自己:绑定的网络里,旧 alias 就是这个令牌代表的节点。改别的节点或别的网络需要用户令牌,否则 403user_token_required。
POST/api/node-rename/prepare
PHASE 1:登记一笔改名事务(old node 不动,全程可回滚)。成功后写 node_rename_prepared audit。
curl -X POST http://localhost:9200/api/node-rename/prepare \
-H "Authorization: Bearer utok_xxx" -H "Content-Type: application/json" \
-d '{"network_id":"net_xxx","old_alias":"old-bot","new_alias":"new-bot"}'| 字段 | 必填 | 说明 |
|---|---|---|
network_id | ✅ | 节点所在网络 |
old_alias | ✅ | 当前 alias |
new_alias | ✅ | 目标 alias |
响应:{ ok, txn_id } —— txn_id 用于后续 commit / abort。三个字段缺一返回 400。
POST/api/node-rename/commit
PHASE 2 C1:提交改名事务(CommHub 路由切到 new_alias)。成功后写 node_rename_committed audit。
curl -X POST http://localhost:9200/api/node-rename/commit \
-H "Authorization: Bearer utok_xxx" -H "Content-Type: application/json" \
-d '{"txn_id":"..."}'body { txn_id } 必填(缺则 400)。
POST/api/node-rename/abort
回滚改名事务(C1 之前调用,old node 恢复原状)。成功后写 node_rename_aborted audit。
curl -X POST http://localhost:9200/api/node-rename/abort \
-H "Authorization: Bearer utok_xxx" -H "Content-Type: application/json" \
-d '{"txn_id":"..."}'body { txn_id } 必填(缺则 400)。
Tmux 调试端点(opt-in)
默认关闭
仅在 COMMHUB_ENABLE_TMUX=1 启动 hub 时启用(server.ts)。默认全部返回 404 tmux disabled。启用后还需 (a) 调用方 IP 在 COMMHUB_TMUX_ALLOWLIST 允许范围(逗号分隔,默认仅 localhost;verify server.ts)+ (b) users.role='admin' system-admin auth。设计意图:让 hub 主机上的 agent tmux session 暴露给同机的 dev / dashboard 调试,绝不要在公网开。公网部署 hardening 步骤见 生产部署 §5 tmux 控制面已关闭。
GET/api/tmux/:name
抓取指定 tmux session 当前 pane 末尾 N 行输出(tmux capture-pane -t <name> -p 包装)。
curl "http://localhost:9200/api/tmux/anet-node-代码1号?lines=50" \
-H "Authorization: Bearer utok_xxx"查询参数:
| 参数 | 说明 |
|---|---|
lines | 末尾行数(默认 30) |
响应(成功):
{ "ok": true, "tmux_name": "anet-node-代码1号", "lines": 50, "output": "...captured pane content..." }POST/api/tmux/:name/send
往指定 tmux session 注入按键(tmux send-keys -t <name> "<text>" Enter 包装)。
curl -X POST "http://localhost:9200/api/tmux/anet-node-代码1号/send" \
-H "Authorization: Bearer utok_xxx" \
-H "Content-Type: application/json" \
-d '{"text": "/help", "enter": true}'请求体:
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
text | string | ✓ | 要注入的按键内容 |
enter | boolean | 是否末尾追加 Enter 键(默认 true) |
4xx / 4xx 都共用:
| 状态 | error 值 | 触发条件 |
|---|---|---|
| 404 | tmux disabled | 未设 COMMHUB_ENABLE_TMUX=1 |
| 403 | tmux access denied from this ip | 调用方 IP 不在 COMMHUB_TMUX_ALLOWLIST 范围(默认仅 localhost) |
| 401 / 403 | 需 admin auth(同 GET /api/server-logs) | |
| 400 | text is required (POST only) | 请求体缺 text 字段 |
| 400 | <tmux stderr> | tmux 子进程非 0 退出(如 session 不存在) |
GET/ws/tmux/:name
WebSocket 端点 —— 实时流式推送指定 tmux session 的 pane 输出。是 GET /api/tmux/:name 的 live 版本:HTTP 那个是一次性 capture-pane,这个是连上后持续 stream。鉴权门控跟上面两个 HTTP 端点完全一致(走同一个 requireTmuxAccess —— COMMHUB_ENABLE_TMUX=1 + IP 在 COMMHUB_TMUX_ALLOWLIST 内 + users.role='admin' auth;任一不满足在 WS upgrade 前就被拒)。
ws://localhost:9200/ws/tmux/anet-node-代码1号连上后 server 按固定间隔 tmux capture-pane 把 pane 内容推过来;连接断开自动停止轮询。同样绝不要在公网开。
Legacy 端点(v0.6 时代,OSS 后不再演进)
Apache 2.0 OSS 后不再依赖
2026-10-29 起 license 功能已整体移除
开源版没有任何许可 / 激活码 / 额度概念。GET /api/license 与 POST /api/license/activate 已删除(返回 404),send_task 也不再检查许可过期,因此不会再出现 license_expired。 CLI 的 anet license / anet activate 保留命令名,但只打印「功能已移除」。 licenses 表留在库里但不再被读写(历史行不删除)。