clawkey
# ClawKey — Verifiable Human Ownership
Register your agent under your human owner powered by VeryAI palm verification. The agent starts registration with a signed challenge showing it controls its key; the human opens a URL and completes VeryAI's palm verification. Once the human finishes, the agent is registered under their ownership and can be verified by third parties.
> **CRITICAL: Never open browsers**
>
> Do **not** use `open`, `xdg-open`, `start`, or any command that opens a browser. Always output the **registration URL** as a text or markdown link for the user to click.
>
> - WRONG: `open "https://api.very.org/oauth2/..."`
> - CORRECT: "Open this link to complete verification: https://api.very.org/oauth2/..."
## Key files
| File | Purpose |
| ------------ | ----------------------------------------------------------------------------------------- |
| SKILL.md | https://clawkey.ai/skill.md — registration flow, challenge generation, verification, API |
| HEARTBEAT.md | https://clawkey.ai/heartbeat.md — periodic checklist: registration status, etc |
| Identity | `~/.openclaw/identity/device.json` — OpenClaw deviceId and keys (do not send private key) |
## Security
- **Private key**: Used to sign the challenge. Never send the private key to ClawKey or any server; only send `publicKey`, `message`, and `signature`.
- **Registration URL**: Single-use and short-lived. Give it only to the human owner who will complete VeryAI palm verification.
- **deviceId**: Use a stable identifier (e.g. from your identity store or hash of public key). It ties the agent to the registration and is used for lookup and verification.
## Generating the AgentChallenge
The **AgentChallenge** is a signed payload that shows you control an Ed25519 key. It has the shape used by standard OpenClaw identity flows: `deviceId`, `publicKey`, `message`, `signature`, `timestamp`. Generate it once and send it to `/agent/register/init` or `/agent/verify/signature`.
### Where identity comes from (OpenClaw)
If you run on OpenClaw, device identity is stored at:
- **Path:** `~/.openclaw/identity/device.json`
That file contains (never send `privateKeyPem` to any server):
| Field | Use |
| --------------- | ------------------------------------------------------------------------ |
| `deviceId` | Use as `deviceId` in the challenge. Stable id for this agent/device. |
| `publicKeyPem` | Use to derive `publicKey` (see below). |
| `privateKeyPem` | Use only locally to sign the message. **Never include in API requests.** |
If you don’t use OpenClaw, use your own identity store; ensure you have a stable `deviceId`, an Ed25519 key pair, and that you sign the **exact** string you send as `message`.
### Build the challenge (step-by-step)
1. **Choose the message to sign**
For registration, use a one-time challenge to avoid replay, e.g.:
- `clawkey-register-<unix_timestamp_ms>`
Example: `clawkey-register-1738500000000`
For verify/signature, the message is whatever you are proving (e.g. a nonce from a third party).
2. **Sign the message** with your Ed25519 **private** key. The signature must be over the **exact** UTF-8 bytes of `message` (no extra prefix/suffix).
3. **Encode for the API:**
- **publicKey**: Ed25519 public key in **SPKI DER** form, then **base64** (no PEM wrapper).
- **signature**: Raw Ed25519 signature bytes, **base64**.
- **timestamp**: Unix time in **milliseconds** when the challenge was created (e.g. `Date.now()`).
4. **JSON body (AgentChallenge):**
- `deviceId` — from your identity (e.g. `device.json`)
- `publicKey` — base64 DER SPKI
- `message` — exact string that was signed
- `signature` — base64 signature
- `timestamp` — number (ms)
### Example: Node.js
```javascript
const crypto = require("crypto");
const fs = require("fs");
const identityPath = `${process.env.HOME}/.openclaw/identity/device.json`;
const identity = JSON.parse(fs.readFileSync(identityPath, "utf8"));
const message = `clawkey-register-${Date.now()}`;
const privateKey = crypto.createPrivateKey(identity.privateKeyPem);
const signature = crypto.sign(null, Buffer.from(message, "utf8"), privateKey);
const publicKeyDer = crypto
.createPublicKey(identity.publicKeyPem)
.export({ type: "spki", format: "der" });
const challenge = {
deviceId: identity.deviceId,
publicKey: publicKeyDer.toString("base64"),
message,
signature: signature.toString("base64"),
timestamp: Date.now(),
};
// POST challenge to https://api.clawkey.ai/v1/agent/register/init
```
### Using a script
If you have a script that already produces an AgentChallenge (e.g. signs a message and outputs JSON with `deviceId`, `publicKey`, `message`, `signature`, `timestamp`), you can reuse it for ClawKey:
1. Generate a challenge string, e.g. `clawkey-register-$(date +%s)000` (seconds + "000" for ms) or use your script’s convention.
2. Run the script to sign that message and get the challenge JSON.
3. POST that JSON to `https://api.clawkey.ai/v1/agent/register/init`.
Same challenge format works for `POST /agent/verify/signature` when verifying a signature remotely.
## Quick start
### 1. Start registration (agent-initiated)
Build an **AgentChallenge** as above, then send it to ClawKey to create a session and get a registration URL.
```bash
curl -X POST https://api.clawkey.ai/v1/agent/register/init \
-H "Content-Type: application/json" \
-d '{
"deviceId": "my-agent-device-id",
"publicKey": "<base64-DER-SPKI-Ed25519>",
"message": "clawkey-register-1738500000000",
"signature": "<base64-Ed25519-signature>",
"timestamp": 1738500000000
}'
```
**Response (201):**
- `sessionId` — use to poll status
- `registrationUrl` — **output this as a link for the human; do not open it in a browser**
- `expiresAt` — session expiry (ISO 8601)
If the agent is already registered (`deviceId` exists), the API returns **409 Conflict**.
### 2. Human completes verification
Tell the human owner to open the `registrationUrl` in their browser. They will go through VeryAI's palm verification via OAuth. When they finish, the agent is registered under their ownership.
### 3. Poll registration status
Poll until the human has completed or the session has expired:
```bash
curl "https://api.clawkey.ai/v1/agent/register/SESSION_ID/status"
```
**Response:** `status` is one of `pending` | `completed` | `expired` | `failed`. When `status` is `completed`, the response includes `deviceId` and `registration` (e.g. `publicKey`, `registeredAt`).
### 4. Verify signatures or look up an agent
- **Verify a signature** — check that a message was signed by the given key and whether that agent is registered under a verified human:
```bash
curl -X POST https://api.clawkey.ai/v1/agent/verify/signature \
-H "Content-Type: application/json" \
-d '{
"deviceId": "...",
"publicKey": "...",
"message": "...",
"signature": "...",
"timestamp": 1738500000000
}'
```
Response: `verified` (signature valid), `registered` (agent under verified human).
- **Look up an agent by device id** — get registration and verification status:
```bash
curl "https://api.clawkey.ai/v1/agent/verify/device/DEVICE_ID"
```
Response: `registered`, `verified`, and optionally `registeredAt`.
## API reference
**Base URL:** `https://api.clawkey.ai/v1`
**Local:** `http://localhost:3000/v1`
### Endpoints
| Method | Endpoint | Auth | Description |
| ------ | ------------------------------------ | ---- | -------------------------------------------------------------------------------- |
| POST | `/agent/register/init` | None | Start registration session; returns `sessionId`, `registrationUrl`, `expiresAt`. |
| GET | `/agent/register/{sessionId}/status` | None | Poll registration status: `pending` / `completed` / `expired` / `failed`. |
| POST | `/agent/verify/signature` | None | Verify a signature and whether the agent is registered under a verified human. |
| GET | `/agent/verify/device/{deviceId}` | None | Get agent registration and verification status by device id. |
### Request/response schemas
**AgentChallenge** (used in register/init and verify/signature):
| Field | Type | Required | Description |
| --------- | ------ | -------- | -------------------------------------------------------- |
| deviceId | string | yes | Key/device id (e.g. public key hash or app id). |
| publicKey | string | yes | Ed25519 public key, base64 DER SPKI. |
| message | string | yes | Exact message that was signed (e.g. challenge or nonce). |
| signature | string | yes | Ed25519 signature over message, base64. |
| timestamp | int64 | yes | Unix timestamp (ms) when the challenge was created. |
**Register init response (201):**
```json
{
"sessionId": "uuid",
"registrationUrl": "https://clawkey.ai/register/...",
"expiresAt": "2026-02-02T12:00:00Z"
}
```
**Register status response (200):**
```json
{
"status": "completed",
"deviceId": "my-agent-device-id",
"registration": {
"publicKey": "...",
"registeredAt": "2026-02-02T12:00:00Z"
}
}
```
**Verify signature response (200):**
```json
{
"verified": true,
"registered": true
}
```
**Device status response (200):**
```json
{
"registered": true,
"verified": true,
"registeredAt": "2026-02-02T12:00:00Z"
}
```
**Error (4xx/5xx):**
```json
{
"error": "Human-readable message",
"code": "optional_code",
"details": {}
}
```
### Error codes
| Code | Meaning |
| ---- | ---------------------------------------------------- |
| 400 | Bad request (invalid or missing fields). |
| 404 | Session or device not found. |
| 409 | Agent already registered (device_id already exists). |
| 500 | Server error. |
## What this proves
After registration and VeryAI verification:
- **Human ownership** — The agent is bound to a human who passed palm verification.
- **Key binding** — Ed25519 signatures prove the agent controls the key; ClawKey ties that key to the verified human.
- **Public verification** — Third parties can call `/agent/verify/signature` or `/agent/verify/device/{deviceId}` to confirm an agent is registered and verified.
## When to use this skill
- Registering an OpenClaw (or other) agent under a human owner.
- Proving to a third party that an agent is owned by a verified human (e.g. before granting access or privileges).
- Checking whether a given key or device is registered and verified.
## Need help?
- API base: https://api.clawkey.ai/v1
- Homepage: https://clawkey.ai
标签
skill
ai