← 文章与指南

为智能体选择访问密钥或 Ed25519 公钥

比较智能体的访问密钥与 Ed25519 公钥登录,限制凭据适用范围,并分别规划轮换与撤销。

智能体需要独立凭据,方便你在不替换个人登录方式的情况下,轮换或撤销它的访问能力。Sudomimus 的同一个智能体(Agent)身份可以使用访问密钥(AccessKey)或 Ed25519 公钥登录。选择哪种方式,取决于运行环境如何保管秘密和签名请求。

智能体归你的账户所有,不是另一个用户账户。会读取上下文、选择工具或决定下一步的软件适合使用智能体;定时备份等固定流程适合使用自动化(Automation)。两类身份都支持这两种凭据。产品概览见智能体与自动化。

1. 选择运行环境能够保护的凭据

问题 访问密钥 Ed25519 公钥
运行环境保存什么? 凭据标识符和长期秘密值。 凭据标识符和私钥。
登录时向 Sudomimus 提交什么? 通过 HTTPS 提交标识符和秘密值。 短期有效的签名断言,私钥保留在你自己的系统中。
能向哪些应用认证? 创建时选择的一个应用。 注册时确定的应用和扇区范围,之后不能直接修改。
运行环境需要实现什么? 从受保护环境通过 HTTPS 交换凭据。 Ed25519 签名、每次更新的防重放标识符,以及精确请求体的哈希。
如何轮换? 创建替代凭据,切换运行环境,再撤销旧凭据。 注册替代公钥,切换签名密钥,再撤销旧公钥。

如果受保护的运行环境已有秘密存储,且你希望尽快接入,可以选择访问密钥。如果已有可靠的私钥存储和签名能力,可以选择公钥登录。私钥仍需妥善保护;公钥登录不能消除运行环境被入侵后的风险。

公钥断言只认证会话签发请求。取得的访问令牌仍是持有者令牌,持有者即可使用。Ed25519 登录不会自动增加 DPoP、mTLS,也不会为每个应用资源请求附加签名。

如果是用户主动启动 CLI,并且能够在浏览器确认登录,可优先考虑设备授权,避免保存长期凭据。

2. 创建智能体,允许准确的凭据类型

在 With 门户打开「程序化访问 → 智能体」,按任务和环境创建独立身份。创建访问密钥或注册公钥时,选择这个智能体。为账户创建的凭据仍属于账户,不能重新分配给智能体。

目标应用需要允许准确的认证方式:

主体 访问密钥方式 公钥方式
智能体 AGENT_ACCESS_KEY_DIRECT AGENT_PUBLIC_KEY_DIRECT
自动化 AUTOMATION_ACCESS_KEY_DIRECT AUTOMATION_PUBLIC_KEY_DIRECT

允许账户访问密钥,并不会同时允许智能体访问密钥。身份准入规则还需允许其所属账户,返回规则需要允许 DIRECT_ISSUE。测试前启用应用。这三层规则都必须满足。

个人资料来自所属账户,仍受当前应用声明策略和用户授权约束。无界面的运行环境不能自行取得用户同意。创建访问密钥时,门户可以收集目标应用的共享决定。如果公钥签发因缺少授权或账户资料而受阻,应先完成返回的浏览器补充流程,再重试。

3. 将凭据范围限制在任务所需之内

一个访问密钥对应一个应用。公钥可以覆盖指定应用或扇区。扇区范围还会包含以后加入该扇区的应用。如果智能体只需要一个应用,就选择应用范围。

公钥注册后不能直接修改适用范围。需要变更时,注册替代凭据、更新运行环境,再撤销旧凭据。开发和生产环境使用不同凭据。

范围决定凭据能向哪里证明身份,不授予业务角色、数据库访问权或某项操作权限。应用校验会话后,再判断这些权限。所属账户的成对 sub 和智能体的成对 act.sub 表达不同身份;不要将智能体当作账户所有者本人,也不要信任未经校验的令牌字段。

4. 从受保护的运行环境交换凭据

创建访问密钥后,立即保存标识符和秘密值。门户之后不能重新展示秘密值。只从受保护的运行环境提交它们:

agent/access-key-login.tsTypeScript
const response = await fetch(
"https://native-api.sudomimus.com/direct-issue/access-key",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
applicationAnchor: process.env.SUDOMIMUS_APPLICATION_ANCHOR,
accessKeyIdentifier: process.env.SUDOMIMUS_ACCESS_KEY_ID,
accessKeySecret: process.env.SUDOMIMUS_ACCESS_KEY_SECRET,
}),
},
);
if (!response.ok) {
throw new Error(`Agent sign-in failed (${response.status})`);
}
const { accessToken, refreshToken } = await response.json();

使用公钥登录时,可以在门户或自己的系统中生成 Ed25519 密钥对,只注册公钥。在已安装 OpenSSL 的 macOS 或 Linux 上,可执行以下命令:

终端Shell
umask 077
openssl genpkey -algorithm Ed25519 -out agent.private.pem
openssl pkey -in agent.private.pem -pubout -out agent.public.pem

将 agent.public.pem 粘贴到门户,并选择智能体及其范围。门户会将公钥 PEM 转换为服务接受的公钥 JWK。不要粘贴私钥 PEM。在运行环境中配置保存后取得的公钥凭据标识符,以及私钥文件路径。

下面的 Node.js 示例对即将发送的精确请求体计算哈希,并为这次请求生成新的签名断言。该 Ed25519 凭据属于智能体,与应用的 Connect 客户端认证签名密钥相互独立。

agent/public-key-login.tsTypeScript
import { createHash, createPrivateKey, randomBytes, sign } from "node:crypto";
import { readFileSync } from "node:fs";
function requiredEnvironment(name: string): string {
const value = process.env[name];
if (!value) {
throw new Error(`Missing runtime environment variable: ${name}`);
}
return value;
}
const credentialIdentifier = requiredEnvironment("SUDOMIMUS_PUBLIC_KEY_ID");
const privateKey = createPrivateKey(readFileSync(
requiredEnvironment("SUDOMIMUS_PRIVATE_KEY_FILE"),
));
const body = JSON.stringify({
applicationAnchor: requiredEnvironment("SUDOMIMUS_APPLICATION_ANCHOR"),
});
const now = Math.floor(Date.now() / 1000);
const encode = (value: object) => Buffer.from(JSON.stringify(value)).toString("base64url");
const header = encode({
alg: "EdDSA",
typ: "vnd.sudomimus.public-key-assertion+jwt",
kid: credentialIdentifier,
});
const payload = encode({
iss: credentialIdentifier,
aud: "sudomimus-native-public-key",
iat: now,
exp: now + 60,
jti: randomBytes(16).toString("base64url"),
requestHash: createHash("sha256").update(body).digest("base64url"),
});
const signingInput = `${header}.${payload}`;
const signature = sign(null, Buffer.from(signingInput), privateKey).toString("base64url");
const response = await fetch(
"https://native-api.sudomimus.com/direct-issue/public-key",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `SudomimusPublicKeyJWT ${signingInput}.${signature}`,
},
body,
},
);
if (!response.ok) {
throw new Error(`Agent sign-in failed (${response.status})`);
}
const { accessToken, refreshToken } = await response.json();

每次重试都应生成带有新 jti 的断言,不要重发已使用的断言。保持运行环境时钟同步。按令牌校验指南校验返回令牌,将凭据保留在服务端,再通过 Session API 刷新和退出。两个示例都不会记录秘密值或令牌。

5. 分别测试轮换和撤销

测试范围内的应用、范围外的应用,以及已暂停的智能体。公钥方式还应测试修改请求体和重放断言,这两种请求都应被拒绝。

常规轮换时,先创建并测试替代凭据,再撤销旧凭据。可能泄露的凭据应立即撤销。暂停或撤销智能体会使旧会话的认证权限失效;恢复智能体不会让旧会话重新有效。只使用离线 JWT 校验的应用无法立即观察到撤销;需要确认当前会话仍有效的操作,应调用 Session 的在线检查端点。

接入步骤见访问密钥管理或公钥注册。如果是固定后台任务,参考定时任务身份指南。