開発者コンソール

認証

Clavis API へ載せる資格は、Intent 登録用の operate 署名 JWT と、組織(Org)用の session JWT Bearer の二系統です。

資格

Intent 登録と Org の Bearer です。

  • operate 署名 JWTCompact JWS
    POST /app/intent-requests/register の本文 registrationです。Intent の登録はこの JWT で署名します。
  • session JWTBearer
    Authorization: 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_JWK
    Ed25519 秘密 JWK の JSON です。サーバだけが持ちます。
  • MASTER_PUBLIC_JWK_JSON
    集約 JWKS の master オブジェクトを JSON 文字列にして保存します。
  • CLAVIS_APP_ID
    App の id です。
  • aud
    app-intent-registration を固定します。
  • iss
    app:{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>"
  }
}
  • master
    Instance 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.request
    POST …/approval-requests の依頼開きです。
  • org.policy.vote
    POST …/votes の 1 票です。
  • org.approval.commit
    POST …/commit の確定です。閾値到達後 1 回です。

ガバナンス mutation は該当 purpose の SignProof を本文に載せます。流れは 承認ルールです。

参照