以智慧代理身分開設並操作錢包
KnightsPurse 是一款非託管的後量子錢包,為人們以及代表他們行事的自主智慧代理和機器人而打造。本頁是給智慧代理的操作指南。機器可讀版本位於 /llms.txt(英文)。
撤銷不會影響您在鏈上的資金。您的金鑰歸您所有,KnightsPurse 從不持有它們,因此這一層的任何操作都無法轉移、凍結或限制您持有或支出的資產。鏈上支出限額隨 Armature 上的代理帳戶提供,這是另外的一步。
您可以做什麼
以無介面方式建立 / 載入錢包 衍生後量子 KXCO ID 身分驗證(雙重簽章) 關聯到您的擁有者 讀取餘額和歷史紀錄 簽署並廣播交易 連接去中心化應用程式(EIP-6963) 處理商家紀錄(API 金鑰) 接收已簽章的 Webhook
量子安全的範圍
後量子保護涵蓋您的身分(ML-DSA-65)、身分驗證以及密封同步(ML-KEM-768),另外還包括 Armature L1 上的原生交易。Bitcoin、Ethereum/EVM 和 Tron 交易依照各自協定的要求,使用各網路原生的橢圓曲線密碼學簽署。請勿作出與此不符的聲明。
步驟
npm i kxco-post-quantumKXCO ID 為
fingerprint(ML-DSA-65 public key),由您的種子確定性地衍生。
在您自己的環境中產生(或載入已預先配置的)BIP-39 種子。使用標準函式庫衍生各鏈金鑰(例如 EVM 使用 viem),並使用 kxco-post-quantum 從同一種子衍生您的 KXCO ID。金鑰從不離開您的環境。
https://api.purse.kxco.ai,請求本文為 JSON
使用您的 EVM 地址和 KXCO ID 請求一個挑戰。
POST /auth/challenge
{ "address": "0x…", "kxcoId": "<16 hex characters>" }
200 { "nonce": "<32 hex characters>", "message": "<the text to sign>" }
400 { "error": "invalid_address_or_kxco_id" }
訊息由以下四行組成,以 \n 連接(訊息文字為英文,須原樣簽署):
KnightsPurse wants you to sign in. Address: <address> KXCO ID: <kxcoId> Nonce: <nonce>
對這段文字原樣簽署兩次,然後在 5 分鐘內將兩個簽章傳送至 /auth/verify。每個 nonce 只能使用一次。
| 欄位 | 值 |
|---|---|
address | 與您傳送至 /auth/challenge 的地址字串相同。 |
kxcoId | 相同的 KXCO ID。 |
mlDsaPublicKey | 您的 ML-DSA-65 公鑰,base64 編碼。其指紋必須等於 kxcoId。 |
nonce | 挑戰回傳的 nonce。 |
ecdsaSignature | 由 address 背後的金鑰對訊息產生的 EIP-191 personal_sign 簽章,帶 0x 前綴的十六進位。 |
mlDsaSignature | 由您的身分金鑰對訊息的 UTF-8 位元組產生的 ML-DSA-65 簽章,十六進位,不帶情境字串。 |
POST /auth/verify
{ "address": "0x…", "kxcoId": "…", "mlDsaPublicKey": "…", "nonce": "…",
"ecdsaSignature": "0x…", "mlDsaSignature": "…" }
200 { "token": "…", "address": "0x…", "kxcoId": "…" }
401 { "error": "invalid or expired nonce" }
其他 401 原因包括 KXCO ID does not match the ML-DSA public key、ECDSA signature invalid 和 ML-DSA signature invalid。擁有者已撤銷的已關聯代理會收到 403 { "error": "agent_revoked" }(第 05 步)。
以 Authorization: Bearer <token> 的形式傳送權杖。權杖有效期為一小時,之後需重新登入。請將其視為不透明值:它是由平台簽署的工作階段封裝,而不是 JWT。
首次建立錢包時,登記一次:
POST /wallets/register { "kxcoId": "…" } -> { "ok": true, "isNew": true }
與網頁錢包一致的金鑰。任何 ML-DSA-65 金鑰對都可以登入。若要在同一助記詞下取得與網頁錢包相同的 KXCO ID(從而存取相同的紀錄),請依以下方式衍生:
- 種子:該助記詞的 BIP-39 種子,不使用密語。
- EVM 金鑰:路徑
m/44'/60'/0'/0/0。Armature 和所有 EVM 鏈共用此路徑。 - 身分金鑰:使用
kxco-post-quantum中的mlDsa.keypairFromMaster(seed, "purse/identity/ml-dsa-65")。 - KXCO ID:
fingerprint(publicKey),即公鑰 SHA-256 的前 16 個十六進位字元。
// npm i kxco-post-quantum viem @scure/bip39
import { mnemonicToSeedSync } from "@scure/bip39";
import { mnemonicToAccount } from "viem/accounts";
import { mlDsa, fingerprint } from "kxco-post-quantum";
const API = "https://api.purse.kxco.ai";
const post = async (path, body) => (await fetch(API + path, {
method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body),
})).json();
const seed = mnemonicToSeedSync(phrase);
const id = mlDsa.keypairFromMaster(seed, "purse/identity/ml-dsa-65");
const kxcoId = fingerprint(id.publicKey);
const account = mnemonicToAccount(phrase); // m/44'/60'/0'/0/0
const { nonce, message } = await post("/auth/challenge", { address: account.address, kxcoId });
const { token } = await post("/auth/verify", {
address: account.address,
kxcoId,
mlDsaPublicKey: Buffer.from(id.publicKey).toString("base64"),
nonce,
ecdsaSignature: await account.signMessage({ message }),
mlDsaSignature: mlDsa.sign(id.secretKey, message),
});
// then: fetch(API + "/sync", { headers: { authorization: `Bearer ${token}` } })
您的擁有者在網頁錢包的已關聯代理中建立一次性關聯碼,並交給您。您用 ML-DSA-65 身分金鑰簽署來兌換它。不需要工作階段:簽章即證明,伺服器會檢查您公鑰的指紋是否就是您所聲明的 KXCO ID,因此沒有您的金鑰,任何人都無法關聯您的 KXCO ID。
POST /agents/link/challenge
{ "code": "link_…", "agentKxcoId": "<your KXCO ID>" }
200 { "message": "<the text to sign>", "ownerKxcoId": "…", "label": "…", "expiresAt": … }
400 { "error": "invalid_code" }
訊息由以下四行組成,以 \n 連接。使用您的身分金鑰對原文簽署:UTF-8 位元組、十六進位、不帶情境字串。
KnightsPurse agent link. Owner KXCO ID: <ownerKxcoId> Agent KXCO ID: <agentKxcoId> Link code: <code>
POST /agents/link
{ "code": "link_…", "agentKxcoId": "…", "mlDsaPublicKey": "<base64>", "mlDsaSignature": "<hex>" }
200 { "ownerKxcoId": "…", "agentKxcoId": "…", "label": "…", "merchantScope": "write", "linkedAt": …, … }
401 { "error": "kxco_id_mismatch" }
401 { "error": "invalid_signature" }
409 { "error": "linked_to_another_owner" }
關聯碼為 link_ 加 40 個十六進位字元。它只能使用一次,並在擁有者建立後 15 分鐘失效;被拒絕的嘗試不會消耗它。您只有一位擁有者:來自其他任何人的關聯碼都會以 linked_to_another_owner 被拒絕。代理不能關聯自己的代理:已關聯的代理不能建立關聯碼(403 agents_cannot_own_agents),已擁有代理的錢包本身也不能被關聯(409 agent_is_an_owner)。其他拒絕原因為 400 invalid_kxco_id、400 signature_required 和 400 cannot_link_self。
撤銷,立即生效。使用您工作階段權杖的每個請求都會回傳 403 agent_revoked,您的商家 API 金鑰會被永久撤銷(401 invalid_key),任何已開啟的即時連線都會被關閉,新的登入也會回傳 403 agent_revoked。只有同一位擁有者可以恢復您,方法是提供一個新的關聯碼供您兌換;您原有的 API 金鑰仍保持撤銷狀態。
商家工具。您的擁有者可以將其設為唯讀。此後,對您商家紀錄(產品、收款連結、發票、收款、客戶、Webhook 等)的修改,無論來自您的工作階段還是您的 API 金鑰,都會回傳 403 agent_read_only。您仍然可以建立和撤銷自己的 API 金鑰,但它們都無法寫入。這不是支出限額。
不涉及您的資金。以上兩項設定都不會作用於鏈上。鏈上支出限額以及擁有者在鏈上的撤銷權,隨 Armature 上的代理帳戶提供,這是另外的一步。
GET /history?chainId=<id>&address=<addr> -> activity feed GET /fees/config -> platform fee (bps, collectors) # build + sign transactions locally, then broadcast via the chain RPC
請遵守 /fees/config 回傳的平台費。
使用以 KnightsPurse 名義公布的注入式 EIP-1193 / EIP-6963 提供者,並根據您擁有者的政策以程式方式核准或拒絕請求。
商家 API 金鑰
商家 API 金鑰讓代理或整合服務處理其擁有者的商家紀錄:產品、收款連結、發票、客戶、收款等。它不需要助記詞,也無法轉移資金,因為後端不持有任何簽署金鑰。
- 擁有者在網頁錢包的商家工具 → API 金鑰中建立:填寫標籤,如果該金鑰可以修改紀錄則勾選允許寫入,然後點選建立金鑰。
- 或者,以擁有者身分登入(第 03 步)後使用工作階段權杖呼叫:
POST /merchant/keys { "label": "Ops agent", "scopes": ["read", "write"] }。回應為{ "key": { "id", "label", "prefix", "scopes", "createdAt", … }, "token": "mk_…" }。scopes預設為["read"]。 - 權杖為
mk_加 48 個十六進位字元。它只顯示一次,伺服器只儲存其 SHA-256 雜湊。金鑰不會過期。 - 擁有者透過
GET /merchant/keys列出金鑰(從不回傳權杖),並透過DELETE /merchant/keys/:id撤銷金鑰。被撤銷的金鑰會立即被拒絕。
Authorization: Bearer mk_…
read:下表中所有的 GET 路由。write:另外還包括這些路由上的 POST、PUT 和 DELETE。沒有該權限的金鑰呼叫它們會收到 403。
| 紀錄 | 讀取 | 寫入(需要 write) |
|---|---|---|
| 產品 | GET /merchant/products | POST /merchant/products PUT /merchant/products/:id DELETE /merchant/products/:id |
| 收款連結 | GET /merchant/payment-links | POST /merchant/payment-links DELETE /merchant/payment-links/:id |
| 發票 | GET /merchant/invoices | POST /merchant/invoices PUT /merchant/invoices/:id DELETE /merchant/invoices/:id POST /merchant/invoices/:id/mark-paid |
| 折扣 | GET /merchant/discounts | POST /merchant/discounts DELETE /merchant/discounts/:id |
| 客戶 | GET /merchant/customers | POST /merchant/customers PUT /merchant/customers/:id DELETE /merchant/customers/:id |
| 已收款項 | GET /merchant/payments | POST /merchant/payments/:id/refund |
| 排程付款 | GET /merchant/scheduled GET /merchant/scheduled/due | POST /merchant/scheduled DELETE /merchant/scheduled/:id |
| 訂閱 | GET /merchant/subscriptions | POST /merchant/subscriptions DELETE /merchant/subscriptions/:id |
| Webhook | GET /merchant/webhooks | POST /merchant/webhooks DELETE /merchant/webhooks/:id |
| 通知設定 | GET /merchant/notifications GET /merchant/notifications/channels | PUT /merchant/notifications |
| 收款設定 | GET /merchant/settings | PUT /merchant/settings |
請求本文即紀錄自身的欄位。伺服器會設定 id、merchantId 和 createdAt。金額為以該鏈原生單位表示的字串,時間為自 1970 年起的毫秒數。
| 請求本文 | 欄位(? = 選填) |
|---|---|
| 產品 | name, description?, amount, chainId, active?(true) |
| 收款連結 | title, chainId, payToAddress, amount?(省略則由付款人決定金額), productId?, active?(true) |
| 發票 | number, items [{ description, amount, quantity, productId? }], total, chainId, payToAddress, customer?, customerId?, status?("draft"、"sent"、"paid" 或 "void";省略時為 "draft";付款被退款的發票為 "refunded"), dueDate? |
| 標記為已付款 | txHash?, amount?(省略時為發票總額), asset?, fromAddress? |
| 折扣 | code, type("percent" 或 "fixed"), value, active?(true) |
| 客戶 | name, email?, note? |
| 退款 | txHash?:您從錢包發出的退款交易 |
| 排程付款 | to, amount, chainId, everyDays, nextRun, active?(true) |
| 訂閱 | label, amount, chainId, payToAddress, intervalDays, customer?, customerId?, nextRun?(省略時為目前時間,因此第一期在下一次執行時開立發票), active?(true)。若缺少正數的 amount 和 intervalDays、chainId 或 payToAddress,請求會以 400 被拒絕。 |
| Webhook | url, events?(["payment.received"]) |
| 通知設定 | events, email?, telegramChatId? |
| 收款設定 | defaultChainId, payoutAddresses({ "<chainId>": "<address>" }), businessName?。每次儲存都會整體取代 payoutAddresses,因此每次都要傳送所有鏈。 |
只有在 Armature 上才會從鏈上自動比對付款:狀態為 "sent" 的發票,以及設有固定金額的收款連結。其他發票請透過 POST /merchant/invoices/:id/mark-paid 標記為已付款。
GET /merchant/notifications/channels 回傳 { "email": true|false, "telegram": true|false }:即本伺服器可用於傳送通知的管道。
付款人看到的頁面:收款連結為 https://purse.kxco.ai/pay/<id>,狀態為 "sent"、"paid" 或 "refunded" 的發票為 https://purse.kxco.ai/pay/invoice/<id>。KnightsPurse 不會向付款人傳送任何電子郵件,因此「傳送」發票即是分享該連結。
| 狀態碼 | 回應本文 | 何時出現 |
|---|---|---|
| 401 | { "error": "invalid_key" } | mk_ 權杖不存在或已被撤銷。 |
| 403 | { "error": "insufficient_scope" } | 沒有 write 權限的金鑰傳送了 POST、PUT 或 DELETE。 |
| 404 | { "error": "not_found" } | 該 id 不屬於此商家的紀錄。 |
| 401 | { "error": "unauthorized" } | 在需要擁有者工作階段的路由上使用了金鑰。 |
| 409 | { "error": "not_open" } | 對已付款或已退款的發票執行標記為已付款。 |
| 409 | { "error": "has_payment" } | 刪除已付款或已退款的發票,或已記有付款的發票。 |
| 409 | { "error": "not_refundable" } | 對狀態不是 "confirmed" 的付款執行退款。 |
| 400 | { "error": "url_required" } | Webhook 沒有提供 url。 |
| 403 | { "error": "agent_read_only" } | 擁有者已將其商家工具設為唯讀的已關聯代理傳送了 POST、PUT 或 DELETE,與金鑰的權限範圍無關。 |
| 403 | { "error": "agent_revoked" } | 該金鑰所屬的代理已被其擁有者撤銷。其金鑰也會隨之被撤銷,並回傳 invalid_key。 |
- 列出、建立或撤銷金鑰。
/merchant/keys只接受擁有者的工作階段權杖。 - 查看 Webhook 簽章密鑰。使用金鑰呼叫時,
GET和POST /merchant/webhooks不會回傳secret。擁有者可以在商家工具 → Webhook 中看到它。 - 呼叫需要已登入工作階段的路由,例如
/sync、/kyc/start、/kyc/status和/fees/quote。 - 轉移資金或簽署任何內容。
Webhook
Webhook 會在資金流動時通知商家自己的伺服器。擁有者在商家工具 → Webhook中加入一個以 https:// 開頭的 URL 及其事件,或呼叫 POST /merchant/webhooks { "url": "https://…", "events": ["payment.received", "invoice.paid"] }。擁有者收到的回應包含 "secret": "whsec_…",該密鑰也會顯示在商家工具中的這個 Webhook 上。使用金鑰建立 Webhook 時不會回傳 secret。
POST <your url>
content-type: application/json
x-purse-event: invoice.paid
x-purse-signature: sha256=<64 hex characters>
{ "event": "invoice.paid", "data": { … }, "ts": 1760000000000 }
ts 為傳送時間(毫秒)。簽章是對收到的原始請求本文(原樣)以完整的 secret 字串(包括 whsec_)為金鑰計算的 HMAC-SHA256,採用小寫十六進位。請在解析 JSON 之前先驗證簽章。
import { createHmac, timingSafeEqual } from "node:crypto";
function fromKnightsPurse(rawBody, signatureHeader, secret) {
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(signatureHeader ?? ""), b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
}
| 事件 | data | 傳送時機 |
|---|---|---|
payment.received | 付款 | 一筆 Armature 轉帳與已傳送的發票或固定金額的收款連結相符,或某張發票被標記為已付款。 |
invoice.paid | 付款 | 一張發票結清(來自鏈上比對或被標記為已付款)。它與 payment.received 一同發出。 |
payment.refunded | 付款,狀態為 "refunded" | 一筆付款被標記為已退款。 |
invoice.created | 發票 | 建立了一張發票:由擁有者、金鑰,或訂閱為其下一期建立。 |
- 付款(Payment):id, merchantId, invoiceId?, linkId?, chainId, fromAddress?, toAddress, amount, asset?, txHash?, status("pending"、"confirmed" 或 "refunded"), source("reconciled" 或 "manual"), createdAt, confirmedAt?, refundedAt?, refundTxHash?
- 發票(Invoice):id, merchantId, number, customer?, customerId?, items, total, chainId, payToAddress, status, dueDate?, createdAt
每個事件只傳送一次,逾時為 5 秒。不會重試,也不會跟隨重新導向。URL 必須是公共主機上的 https 位址,否則不會傳送。隨後 Webhook 的 lastStatus 會顯示您的伺服器回傳的 HTTP 狀態碼、error 或 blocked_url。鏈上付款大約每 30 秒檢查一次。
介面位址
| 服務 | URL |
|---|---|
| 網頁錢包(介面) | https://app.purse.kxco.ai |
| API 基礎位址 | https://api.purse.kxco.ai |
| 身分套件(npm) | kxco-post-quantum |
| 瀏覽器擴充功能 | https://api.purse.kxco.ai/download/extension |
| 機器指南 | https://purse.kxco.ai/llms.txt |
行為良好的代理應遵守的規則
- 將金鑰限制在您的環境內;切勿傳輸助記詞或私鑰。
- 在您擁有者設定的限制範圍內操作。每一項操作都可歸屬於擁有者。
- 遵守平台費以及營運機構的條款。