主机生命周期管理(Client API)
适合 SaaS / RMM 业务系统自动化创建、分发、回收主机,无需让最终用户登录我们的控制台。
如果只是个人偶尔装一两台机器,用 user-web 控制台 点点鼠标就够了;本篇适用于”批量给客户开主机”或”CI/IaC 自动化部署 agent”场景。
何时用 Client API vs user-web
| 场景 | 推荐入口 |
|---|---|
| 个人 / 单机部署 | user-web 控制台 |
| SaaS 平台为多个最终用户分发 agent | Client API(本篇) |
| CI 流水线把测试机注册到云端调度 | Client API |
| IaC(Terraform 等)批量装上百台 Windows | Client API |
| 重置某台主机的 secret_key(怀疑泄露) | user-web(step-up 保护)+ Client API(受 API Key 保护) |
两条入口背后用同一套 business 逻辑:建主机 / 颁凭据 / KEK 封 secret 完全等价; 区别只是鉴权方式。
5 个端点速查
POST /v1/api/hosts ← 创建(可一次返凭据)
DELETE /v1/api/hosts/{id} ← 软删 + 失效旧 token
POST /v1/api/hosts/{id}/install-token ← 单独颁发凭据
POST /v1/api/hosts/{id}/rotate-secret ← 重置 secret_key + 颁凭据
GET /v1/api/hosts/{id}/installer?format=… ← 直接下 .ps1 / .cmd / .exe 安装包
鉴权统一 Authorization: Bearer ak_<key_id>.<secret>;全部免计费(账户管理动作)。
完整规范见 📥 下载 OpenAPI 或 📘 API.md。
推荐使用流(最常见路径)
“我的 SaaS 用户注册了新主机,给他生成一个 install.ps1 让他直接双击装”
KEY="ak_xxx.yyy" # 你的 API Key
NAME="customer-A-pc-01" # 给这台主机的展示名
# 1. 创建主机 → 拿到 host_id
HOST_ID=$(curl -s -X POST -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d "{\"name\":\"$NAME\"}" \
https://api.gateway.wechas.com/v1/api/hosts | jq -r .host.id)
# 2. 直接下 ps1 安装包(一次性 install_token + 主机专属 secretKey 已嵌入)
curl -s -H "Authorization: Bearer $KEY" \
"https://api.gateway.wechas.com/v1/api/hosts/$HOST_ID/installer?format=ps1" \
-o "install-$HOST_ID.ps1"
# 3. 把 install-$HOST_ID.ps1 转交客户机;客户双击或:
# powershell -ExecutionPolicy Bypass -File install-$HOST_ID.ps1
5 分钟内让客户机执行 ps1,超时 install_token 会失效(重新调一次 /installer 即可)。
凭据获取的 4 种方式
凭据 = secret_key + install_token + agent_pubkey + agent_privkey_seed + exe_url 整套,
保证 agent 在主机端能完整连回中枢。下面 4 种方式拿到的凭据等价,仅形态不同:
方式 A — 创建 + 一次返凭据(推荐用于”创建后立即装”)
curl -X POST -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"name":"pc-01","with_credentials":true}' \
https://api.gateway.wechas.com/v1/api/hosts
响应 201:
{
"host": { "id": "...", "name": "pc-01", "bid": 123456 },
"credentials": {
"host_id": "...",
"bid": 123456,
"secret_key": "0123456789abcdef0123456789abcdef",
"install_token": "URL-safe base64 ~43 字符",
"install_token_expires_at": "2026-05-14T08:45:00Z",
"agent_pubkey": "ed25519:base64...",
"agent_privkey_seed": "base64 raw 32B",
"api_endpoint": "https://api.gateway.wechas.com",
"ws_url": "wss://api.gateway.wechas.com/v1/internal/agent/ws",
"listen_port": 23456,
"exe_url": "https://.../m2-gateway.exe",
"exe_sha256": "hex64"
}
}
业务系统拿到 credentials 之后可以自己渲染 ps1 模板,或者再调 /installer 拿现成包。
方式 B — 直接下安装包(推荐用于”创建后直接发给最终用户”)
curl -H "Authorization: Bearer $KEY" \
"https://api.gateway.wechas.com/v1/api/hosts/$HOST_ID/installer?format=ps1" \
-o "install-$HOST_ID.ps1"
?format= 取值:
| format | 文件类型 | 何时用 |
|---|---|---|
ps1 | 纯 PowerShell 脚本(text/plain) | 常规脚本化部署、域控批量推 |
cmd | polyglot .cmd(双击可装) | 客户没装/不熟 PowerShell |
exe 或缺省 | 单文件 .exe(双击 UAC 即装) | 最易用,给非技术客户首选 |
文件名自动带 host_id 前缀(如 install-<host_id>.ps1),方便分发追踪。
方式 C — 已有主机单独颁发凭据
curl -X POST -H "Authorization: Bearer $KEY" \
https://api.gateway.wechas.com/v1/api/hosts/$HOST_ID/install-token
返同样的 InstallCredentials JSON。适用:
- 第一次没拿凭据(
with_credentials=false)现在补取 - 上一份 ps1 5 分钟过期失效,需要重发
- 用户要换台新机器装
方式 D — 重置 secretKey 同时颁发新凭据(破坏性)
curl -X POST -H "Authorization: Bearer $KEY" \
https://api.gateway.wechas.com/v1/api/hosts/$HOST_ID/rotate-secret
⚠️ 任何持旧 secretKey 加签的 L1 报文立即失效。仅在怀疑 secret 泄露时用。 现役 agent 在 token refresh 时会被中枢拒绝;需要在能立即重装 agent 的窗口里执行。
三语言 SDK 示例
SDK 0.3.x 已加入
hosts.create / hosts.delete / hosts.install_token / hosts.installer4 个方法。下面贴最小可运行片段。
Python
from gateway_new_sdk import Client
c = Client(base_url="https://api.gateway.wechas.com", api_key="ak_xxx.yyy")
# 创建并一次拿凭据
res = c.hosts.create(name="customer-pc-01", with_credentials=True)
print("new host_id =", res.host.id)
print("install_token =", res.credentials.install_token)
# 直接下 ps1 安装包
ps1 = c.hosts.installer(res.host.id, format="ps1")
open(f"install-{res.host.id}.ps1", "wb").write(ps1)
# 测试装完后 ping 一下
print(c.system_metrics(res.host.id))
Go
package main
import (
"context"
"fmt"
"os"
sdk "gateway-new/sdk-go"
)
func main() {
c, _ := sdk.New("https://api.gateway.wechas.com", "ak_xxx.yyy")
ctx := context.Background()
res, _ := c.Hosts.Create(ctx, sdk.CreateHostReq{
Name: "customer-pc-01", WithCredentials: true,
})
fmt.Println("new host_id =", res.Host.ID)
ps1, _ := c.Hosts.Installer(ctx, res.Host.ID, "ps1")
os.WriteFile("install-"+res.Host.ID.String()+".ps1", ps1, 0o600)
}
TypeScript
import { Client } from "gateway-new-sdk";
import { writeFile } from "node:fs/promises";
const c = new Client({ baseUrl: "https://api.gateway.wechas.com", apiKey: "ak_xxx.yyy" });
const { host, credentials } = await c.hosts.create({
name: "customer-pc-01",
withCredentials: true,
});
console.log("new host_id =", host.id);
const ps1 = await c.hosts.installer(host.id, "ps1");
await writeFile(`install-${host.id}.ps1`, ps1);
删除 / 回收主机
curl -X DELETE -H "Authorization: Bearer $KEY" \
https://api.gateway.wechas.com/v1/api/hosts/$HOST_ID
返 204。副作用:
hosts.deleted_at = now(),列表/查询都查不到- 未消费的
install_token立即失效 - 已上线 agent 在 token refresh 时被中枢拒绝(最长 1 小时后掉线)
- 不可恢复;需要重建用同样
name也行(host_id 不同)
错误码
| HTTP | error | 含义 |
|---|---|---|
| 400 | invalid_body / invalid_name / name_too_long / invalid_port / invalid_id | 入参校验 |
| 401 | unauthorized | API Key 缺失 / 错误 / 过期 / 被吊销 |
| 404 | not_found | host 不存在或不属于当前 API Key 持有人 |
| 409 | bid_collision | (user_id, bid) UNIQUE 冲突,重试 1 次即可 |
| 503 | exe_not_configured | 中枢未配 GATEWAY_EXE_URL(联系运维) |
安全建议
- API Key 妥善保管 — 持有它可创建/删除你账户下任意主机,建议放 Secret Manager 而不是源码
- install_token 5 分钟过期 — 拿到后立即转发,超时重调
/installer即可 - HTTPS 必选 —
secret_key/install_token/agent_privkey_seed都是敏感数据,请勿在日志里打印 - 生产环境用最小 scope 的 API Key — 在 user-web
控制台 → API 密钥里给 key 限制可写域 - 审计 — 中枢对所有创建/删除/凭据操作打 audit_log;user-web
操作历史可查