主机生命周期管理(Client API)

适合 SaaS / RMM 业务系统自动化创建、分发、回收主机,无需让最终用户登录我们的控制台。

如果只是个人偶尔装一两台机器,用 user-web 控制台 点点鼠标就够了;本篇适用于”批量给客户开主机”或”CI/IaC 自动化部署 agent”场景。


何时用 Client API vs user-web

场景推荐入口
个人 / 单机部署user-web 控制台
SaaS 平台为多个最终用户分发 agentClient API(本篇)
CI 流水线把测试机注册到云端调度Client API
IaC(Terraform 等)批量装上百台 WindowsClient 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)常规脚本化部署、域控批量推
cmdpolyglot .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.installer 4 个方法。下面贴最小可运行片段。

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 不同)

错误码

HTTPerror含义
400invalid_body / invalid_name / name_too_long / invalid_port / invalid_id入参校验
401unauthorizedAPI Key 缺失 / 错误 / 过期 / 被吊销
404not_foundhost 不存在或不属于当前 API Key 持有人
409bid_collision(user_id, bid) UNIQUE 冲突,重试 1 次即可
503exe_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 操作历史 可查