台帳
App の記録のハッシュを積み、ある時点にその値が確定していたことを後から示す範囲です。
台帳に載るのは App が作ったハッシュ(c)だけです。記録の本文、利用者の識別子、日時は送りません。本文と salt は App か Vault に残します。
開発者コンソール
認証
App のサーバが operate 鍵で、要求ごとに JWT を署名します。session JWT は使いません。
JWT claims
{
"iss": "app:<app_id>",
"aud": "clavis-ledger",
"app_id": "<app_id>",
"htm": "POST",
"htu": "/archive/ledger/v1/entries",
"body_sha256": "<本文の SHA-256(hex)。本文が無いときは付けない>",
"iat": 1790000000,
"exp": 1790000060,
"jti": "<一度だけ使う値>"
}htuパスとクエリです。/archive から書きます。exp − iat300 秒までです。jti同じ値は二度通りません。
コミットメント
App のサーバで計算
salt = 32 バイトの乱数(記録ごとに新しく作る)
c = SHA-256( "clavis.commit.v1" ‖ salt ‖ canonical_json(record) )
slot = HMAC-SHA256( App の秘密鍵, "asset:<asset_id>:v<version>" ) // 任意salt記録ごとに乱数で作ります。使い回すと、本文の推測を防げません。canonical_jsonキーを昇順に並べた JSON です。slot同じ資産の同じ版に、二つ目の記録を確定させないための枠です。App の秘密鍵を Clavis に渡す必要はありません。
opening(App か Vault に残す)
{
"salt": "<base64url 32 バイト>",
"record": { "kind": "transfer", "asset": "a-1", "from": "u1", "to": "u2" }
}手順
1. record と salt から c を作ります。所有権の移転なら slot も作ります。
2. 葉を送ります。応答の receipt を記録と一緒に残します。
RequestPOST https://api.clavis.nexus/archive/ledger/v1/entries Authorization: ClavisApp <JWT> Content-Type: application/json { "leaf": { "v": 1, "c": "<64 hex>", "slot": "<64 hex(任意)>" } }201 Response
{ "log_id": "<Org の UUID>", "status": "created", "index": 41, "leaf_hash": "<64 hex>", "receipt": "clavis.ledger.receipt/v1\nclavis.nexus/ledger/<Org の UUID>\n41\n<base64>\n\n— clavis-ledger-log <base64>\n" }3. 約 1 分後、チェックポイントに入った包含証明を取ります。入る前は 202 です。
RequestGET https://api.clavis.nexus/archive/ledger/v1/proof?index=41 Authorization: ClavisApp <JWT>200 Response
{ "log_id": "<Org の UUID>", "index": 41, "size": 42, "checkpoint": "clavis.nexus/ledger/<Org の UUID>\n42\n<base64 root>\ntime 1790000060\n\n— clavis-ledger-log <base64>\n", "inclusion": ["<base64>", "<base64>"] }4. 相手に渡すときは証拠束を取り、opening を足します。外部の時刻証明(anchor)は 1 時間ほどで付きます。
RequestGET https://api.clavis.nexus/archive/ledger/v1/bundle?index=41 Authorization: ClavisApp <JWT>200 Response
{ "anchor_status": "stamped", "bundle": { "version": 3, "log_id": "<Org の UUID>", "leaf": { "v": 1, "c": "<64 hex>" }, "index": 41, "receipt": "<signed note>", "checkpoint": "<signed note>", "inclusion": ["<base64>"], "anchor": { "index": 7, "checkpoint": "<signed note>", "inclusion": ["<base64>"], "tsa": [{ "url": "<TSA>", "response": "<RFC 3161 応答(base64)>" }] }, "jwks": { "keys": [], "master": {} } } }
読み取り
最新のチェックポイント
GET https://api.clavis.nexus/archive/ledger/v1/checkpoint
Authorization: ClavisApp <JWT>二つのチェックポイントの整合性証明
GET https://api.clavis.nexus/archive/ledger/v1/consistency?first=<size>&second=<size>
Authorization: ClavisApp <JWT>チェックポイントの時刻証明
GET https://api.clavis.nexus/archive/ledger/v1/anchor-proof?size=<size>
Authorization: ClavisApp <JWT>index の葉
GET https://api.clavis.nexus/archive/ledger/v1/entries/<index>
Authorization: ClavisApp <JWT>c から index
GET https://api.clavis.nexus/archive/ledger/v1/entries?c=<64 hex>
Authorization: ClavisApp <JWT>- 読めるのは、その App が属する Org のログだけです。
- 解約後も読み取りと証拠束の取得はできます。
Vault
opening を失わないための保管庫です。App 側で暗号化してから置きます。台帳とは独立して消せます。
置く
PUT https://api.clavis.nexus/archive/vault/v1/objects/<object_id>?subject_tag=<64 hex>&expires_at=<unix>
Authorization: ClavisApp <JWT>
Content-Type: application/octet-stream
<暗号文>取る
GET https://api.clavis.nexus/archive/vault/v1/objects/<object_id>
Authorization: ClavisApp <JWT>消す
DELETE https://api.clavis.nexus/archive/vault/v1/objects/<object_id>
Authorization: ClavisApp <JWT>本人単位で消す
DELETE https://api.clavis.nexus/archive/vault/v1/subjects/<subject_tag>
Authorization: ClavisApp <JWT>object_id英数字・ハイフン・下線で 16〜128 文字です。上書きはできません。subject_tag任意です。App の利用者 ID から作った HMAC など、Clavis が人を特定できない値にします。expires_at任意です。過ぎたものは毎日消します。
検証
- 証拠束は Clavis が止まっていても検証できます。必要なのは証拠束と集約 JWKS の Master 公開鍵です。
- 受領証とチェックポイントは C2SP signed note(Ed25519)、包含・整合性証明は RFC 9162、時刻証明は RFC 3161 です。
- opening があれば c を計算し直し、記録の中身まで確かめられます。
- ブラウザで確かめるときは
archive.clavis.nexus/verifyに証拠束を貼ります。証拠束は送信されません。
上限
- 新しい葉 1 件を台帳の 1 回と数えます。同じ c の再送と読み取りは数えません。
- 葉の追記と Vault への保存には、台帳の購入と当月の残りが要ります。
- App ごとに毎分 600 要求までです。
- Vault は 1 件 256 KiB、Org 合計 1 GiB までです。
エラー
共通形は error、message、requestId、trace_id です。
401 APP_AUTH_FAILEDJWT を検証できません。message の値(signature、claims、expired、body_mismatch など)を直してください。409 DUPLICATE_JTIその jti は使われています。要求ごとに新しい JWT を作ってください。409 APP_NOT_ACTIVEApp のホストが未確認です。402 CARD_CLOSEDmessage が declaration のときは用途に台帳がありません。それ以外は購入か当月の残りです。400 INVALID_LEAFleaf は v、c、slot(任意)だけです。c と slot は小文字の 64 hex です。409 SLOT_TAKENその slot には別の c が確定しています。版を進めてから送ってください。202 pendingまだチェックポイントか時刻証明に入っていません。時間をおいて取り直してください。409 OBJECT_EXISTS同じ object_id があります。消してから置き直してください。413 OBJECT_TOO_LARGE / 507 VAULT_QUOTA_EXCEEDEDVault の上限を超えています。429 RATE_LIMITED毎分の上限を超えました。