認証
Clavis API へ載せる資格は、Intent 登録用の operate 署名 JWT と、組織(Org)用の session JWT Bearer の二系統です。
資格
Intent 登録と Org の Bearer です。
operate 署名 JWTCompact JWSPOST /app/intent-requests/registerの本文registrationです。Intent の登録はこの JWT で署名します。session JWTBearerAuthorization: Bearer <token>です。Org の API(/orgs/{org}/…)に載せます。開発者コンソールが Passkey ログインのあとに保持します。poll_secret48 hex(ヘッダ)X-Intent-Poll-Secretです。register 応答でサーバだけが受け取ります。status に使います。
operate 署名 JWT
組み立てと署名は @clavis.nexus/app の createAppClient です。operate 秘密鍵は Ed25519 秘密 JWK です。
TypeScript
import { createAppClient } from "@clavis.nexus/app";
function requiredEnv(name: string): string {
const value = process.env[name];
if (!value) throw new Error(`missing env: ${name}`);
return value;
}
const app = createAppClient({
apiOrigin: "https://api.clavis.nexus",
appId: requiredEnv("CLAVIS_APP_ID"),
operatePrivateJwk: requiredEnv("OPERATE_PRIVATE_JWK"),
masterPublicJwkJson: requiredEnv("MASTER_PUBLIC_JWK_JSON"),
});OPERATE_PRIVATE_JWKEd25519 秘密 JWK の JSON です。サーバだけが持ちます。MASTER_PUBLIC_JWK_JSON集約 JWKS の master オブジェクトを JSON 文字列にして保存します。CLAVIS_APP_IDApp の id です。audapp-intent-registration を固定します。issapp:{app_id} です。発行する App を示します。
session JWT
開発者コンソールでログインしたあとの Org Bearer です。
- 開発者コンソールで Passkey ログインしたあと、session JWT を Bearer として付けます。
- Org のメンバー操作(ガバナンス、App 設定など)に使います。台帳は App の operate 鍵で呼びます。
Request 例
POST https://api.clavis.nexus/orgs/<org>/approval-requests
Authorization: Bearer <session JWT>
Content-Type: application/jsonガバナンス API は session に加え、本文の sign_proof が要ります。手順は 承認ルールです。
poll_secret
status のヘッダ付与と応答 signature の検証は getStatus が行います。
TypeScript
const stored = await loadPollSecret(requestId);
const status = await app.getStatus(requestId, stored.pollSecret, stored.messageDigest);payload.poll_secretstring (48 hex)必須register 応答です。リクエストごとに新しい値です。サーバだけが保持します。X-Intent-Poll-Secretrequest header必須status に載せます。無いと 401、不一致なら 403 です。
GET status(SDK が送る HTTP)
GET https://api.clavis.nexus/app/intent-requests/status/<request_id>
X-Intent-Poll-Secret: <poll_secret>集約 JWKS
環境変数に保存した Master 公開鍵と status 応答署名の検証です。公開 URL は GET https://api.clavis.nexus/.well-known/jwks.json です。
Response(抜粋)
{
"keys": [
{
"kty": "EC",
"crv": "P-256",
"kid": "<child-kid>",
"alg": "ES256",
"x": "<base64url>",
"y": "<base64url>"
}
],
"master": {
"kty": "EC",
"crv": "P-256",
"kid": "clavis-master",
"alg": "ES256",
"x": "<base64url>",
"y": "<base64url>"
}
}masterInstance Master 公開鍵です。createAppClient の masterPublicJwkJson に、このオブジェクトの JSON 文字列を渡します。keys[]用途スロットごとの子公開鍵です。
SignProof
status 応答とガバナンス本文に載せます。
- Intent 完了の
GET /statusではpayload.sign_proofに含まれます。 - Org ガバナンス API の本文にも載せます。このとき purpose が操作を束縛します。
shownは Connect が署名の直前に見せた一行です。challenge に含まれるため、書き換えた SignProof は検証で落ちます。Connect を通らない署名には付きません。
SignProof(形・抜粋)
{
"v": 1,
"intent_type": "generic_sign",
"intent_id": "<intent_id>",
"message_digest": "<base64url>",
"purpose": "generic.sign",
"sub": "<passkey sub>",
"rp_id": "<rp id>",
"origin": "https://<host>",
"challenge": "<base64url>",
"binding_nonce": "<base64url>",
"shown": { "app": "<App の表示名>", "host": "<App のホスト>", "action": "<display_hint>" },
"credential_id": "<base64url>",
"counter_previous": 0,
"authenticator_data": "<base64url>",
"client_data_json": "<base64url>",
"signature": "<base64url>",
"public_key_cose_b64u": "<base64url>",
"signed_at": 1710000000
}org.approval.requestPOST …/approval-requests の依頼開きです。org.policy.votePOST …/votes の 1 票です。org.approval.commitPOST …/commit の確定です。閾値到達後 1 回です。
ガバナンス mutation は該当 purpose の SignProof を本文に載せます。流れは 承認ルールです。