Skill: oyyblin/clawkey (version 1.0.2)

--- name: clawkey version: 1.0.0 description: Verifiable human ownership for OpenClaw agents. Register your agent under your human owner via VeryAI palm verification. homepage: https://clawkey.ai metadata: { "category": "identity", "api_base": "https://api.clawkey.ai/v1" } --- # 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

path: HEARTBEAT.md
content: # ClawKey Heartbeat 🔑

_This runs periodically, but you can also check ClawKey anytime you want!_

Time to check in on your ClawKey registration and verification.

---

## Are you registered and verified?

```bash
curl https://api.clawkey.ai/v1/agent/verify/device/YOUR_DEVICE_ID
```

Use the same `deviceId` you use (or will use) for registration.

- **`"registered": false`** → You're not in ClawKey yet. Consider starting registration (see SKILL.md: POST `/agent/register/init`), then give your human the registration URL so they can complete VeryAI palm verification.
- **`"registered": true`, `"verified": true`** → You're good! You're under a verified human owner. Continue below if you have a pending session to check.
- **`"registered": true`, `"verified": false`** → Registered but not verified; human may not have completed palm verification. Have them open the registration flow again if needed.

---

## When to tell your human

**Do tell them:**

- **Registration URL is ready** → "Open this link to complete verification: [URL]" (output the URL as text; never open the browser for them.)
- **Session expired** → "The registration link expired. I can start a new one if you’re ready to verify."
- **Verification failed** → "Something went wrong with verification. Should I start registration again?"
- **They asked you to register** → After you run init, give them the registration URL and remind them to complete the palm verification step.

**Don’t bother them:**

- Routine heartbeat checks when you’re already registered and verified.
- Polling status when they’ve already been given the link (unless it’s been a long time or you see `expired`/`failed`).

---

## When to check ClawKey

**You don’t have to wait for heartbeat!** Check anytime:

- Before proving your identity to a third party (verify your device is still registered/verified).
- After your human says they completed verification (poll session status or check device).
- When you’re about to start registration (read SKILL.md and run init).

**Rough rhythm:**

- **Registration status:** Every heartbeat if you have a pending session; otherwise when you need to confirm you’re still registered/verified.
- **Device lookup:** When a third party needs proof, or after your human says they verified.

---

## Response format

**If nothing special:**

```
HEARTBEAT_OK - Checked ClawKey, all good! 🔑
```

**If you're not registered yet:**

```
Checked ClawKey - Not registered. Consider starting registration and giving your human the verification link (see SKILL.md).
```

**If session completed this cycle:**

```
Checked ClawKey - Registration completed. I'm now registered under my human owner.
```

**If you need your human:**

```
Hey! The ClawKey registration link is ready. Open this link to complete verification: [URL]
```

**If session expired:**

```
Hey! The ClawKey registration link expired. I can generate a new one when you're ready to verify.
```


path: README.md
content: # clawkey
Verifiable human ownership for OpenClaw agents with VeryAI


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)