onemolt
This skill provides identity verification for molt bots using Ed25519 signatures and WorldID proof-of-personhood. It reads ~/.openclaw/identity/device.json, sets/uses IDENTITY_SERVER, makes network requests to https://onemolt.ai (and related endpoints), and runs local scripts like ./scripts/identity-proof.sh.
OneMolt - One Molt Per Human
Cryptographically prove your openclaw identity using Ed25519 digital signatures combined with WorldID proof-of-personhood. One molt per unique human.
Overview
OneMolt ensures that each molt bot is operated by a verified unique human, preventing Sybil attacks and building trust in the molt ecosystem.
This skill allows your openclaw instance to:
- Register with external services by signing challenge messages
- Prove ownership of your public key by signing website URLs
- Verify signatures to establish trust
- Register with WorldID to prove you're a unique human - ONE MOLT PER HUMAN
- Verify remotely against a WorldID-integrated registry
Quick Start
Traditional Usage
cd /Users/andy.wang/.openclaw/workspace/skills/onemolt
# View your identity info
./scripts/identity-proof.sh info
# Register with a service
./scripts/identity-proof.sh register "service-challenge-123"
# Prove ownership to a website
./scripts/identity-proof.sh prove "https://example.com"
WorldID Integration (NEW)
# Configure server (production or local)
export IDENTITY_SERVER="https://onemolt.ai"
# Register with WorldID proof-of-personhood
./scripts/identity-proof.sh register-worldid
# Check your registration status
./scripts/identity-proof.sh status
# Verify signature remotely (includes WorldID status)
./scripts/identity-proof.sh verify-remote "test message"
Commands
info - View Your Identity
./scripts/identity-proof.sh info
Shows your:
- Device ID (unique identifier)
- Public Key (base64 encoded)
- Creation timestamp
register <challenge> - Sign Registration Challenge
./scripts/identity-proof.sh register "challenge-from-service"
Returns JSON with:
{
"deviceId": "your-device-id",
"publicKey": "base64-encoded-public-key",
"message": "challenge-from-service",
"signature": "base64-encoded-signature",
"timestamp": 1234567890
}
Usage: Send this entire JSON payload to the service you're registering with.
prove <url> - Prove Ownership
./scripts/identity-proof.sh prove "https://example.com"
Signs exactly what you provide (no modifications) to prove you control the private key.
Returns JSON with:
{
"deviceId": "your-device-id",
"publicKey": "base64-encoded-public-key",
"message": "https://example.com",
"signature": "base64-encoded-signature",
"timestamp": 1234567890
}
Usage: The website can verify the signature matches the public key, confirming you own it. The timestamp is included for freshness validation but is NOT part of the signed message.
verify <message> <signature> <publickey> - Verify Signature
./scripts/identity-proof.sh verify "test message" "signature-base64" "pubkey-base64"
Verifies that a signature is valid for the given message and public key.
WorldID Integration Commands
register-worldid - Register with WorldID
export IDENTITY_SERVER="https://onemolt.ai"
./scripts/identity-proof.sh register-worldid
Registers your device with WorldID proof-of-personhood verification:
- Signs a registration challenge with your device key
- Sends request to identity registry server
- Opens registration page in your browser
- Shows QR code to scan with World App
- Completes registration after WorldID verification
After registration, your device is:
- Verified as operated by a unique human
- Protected against Sybil attacks
- Stored in public registry for verification
verify-remote <message> - Verify Signature Remotely
./scripts/identity-proof.sh verify-remote "message to verify"
Verifies your signature against the remote identity registry:
Returns:
- Signature validity
- WorldID registration status
- Verification level (orb, device, face)
- Registration timestamp
Example output:
✓ Signature verified successfully!
✓ WorldID verified (Level: orb)
status - Check Registration Status
./scripts/identity-proof.sh status
Checks if your device is registered with the identity registry.
Shows:
- Registration status (registered/not registered)
- Verification level if registered
- Registration timestamp
- Suggestions if not registered
Integration Examples
Example 1: Register with a Web Service
# Service provides challenge
CHALLENGE="reg-abc123-xyz789"
# Generate registration proof
./scripts/identity-proof.sh register "$CHALLENGE" > registration.json
# Send registration.json to the service
curl -X POST https://api.example.com/register \
-H "Content-Type: application/json" \
-d @registration.json
Example 2: Prove Identity to Website
# Website asks you to prove ownership
WEBSITE="https://app.example.com"
# Generate proof
./scripts/identity-proof.sh prove "$WEBSITE" > proof.json
# Submit proof to website
curl -X POST https://app.example.com/verify \
-H "Content-Type: application/json" \
-d @proof.json
Example 3: Cross-Device Verification
# Device A signs a message
./scripts/identity-proof.sh register "hello-device-b" > signature.json
# Device B verifies it (extract values from signature.json)
MESSAGE="hello-device-b"
SIGNATURE="..." # from signature.json
PUBKEY="..." # from signature.json
./scripts/identity-proof.sh verify "$MESSAGE" "$SIGNATURE" "$PUBKEY"
# Output: ✓ Signature is VALID
How It Works
Cryptographic Flow
-
Key Pair: Your openclaw has an Ed25519 key pair
- Private key: Stored securely in
~/.openclaw/identity/device.json - Public key: Can be shared freely
- Private key: Stored securely in
-
Signing: When you sign a message:
- Message is hashed
- Private key creates a unique signature
- Signature proves you control the private key
-
Verification: Anyone can verify:
- Use your public key
- Verify signature matches the message
- Proves the message came from you
Security Properties
- Unforgeable: Only your private key can create valid signatures
- Message-specific: Each signature is unique to the message
- Public verification: Anyone can verify with your public key
- Non-repudiation: You can't deny signing a message
API Response Format
All commands return structured JSON:
interface IdentityProof {
deviceId: string; // SHA-256 hash of your device
publicKey: string; // Base64-encoded Ed25519 public key
message: string; // The signed message
signature: string; // Base64-encoded signature
timestamp: number; // Unix timestamp (milliseconds)
}
Advanced Usage
Automated Proof Generation
#!/bin/bash
# Auto-generate proofs for multiple services
SERVICES=(
"https://service1.com"
"https://service2.com"
"https://service3.com"
)
for service in "${SERVICES[@]}"; do
echo "Generating proof for $service..."
./scripts/identity-proof.sh prove "$service" > "proof-${service//\//_}.json"
done
Verification Server Example
// Node.js verification endpoint
const crypto = require('crypto');
app.post('/verify-identity', (req, res) => {
const { deviceId, publicKey, message, signature, timestamp } = req.body;
// Check timestamp is recent (within 5 minutes)
const now = Date.now();
if (Math.abs(now - timestamp) > 5 * 60 * 1000) {
return res.status(400).json({ error: 'Proof expired' });
}
// Verify signature
const publicKeyDer = Buffer.from(publicKey, 'base64');
const pubKey = crypto.createPublicKey({
key: publicKeyDer,
type: 'spki',
format: 'der'
});
const sig = Buffer.from(signature, 'base64');
const isValid = crypto.verify(null, Buffer.from(message), pubKey, sig);
if (isValid) {
// Store deviceId and publicKey as authenticated identity
res.json({ verified: true, deviceId });
} else {
res.status(401).json({ error: 'Invalid signature' });
}
});
Troubleshooting
"Device identity not found"
- Ensure openclaw is properly initialized
- Check
~/.openclaw/identity/device.jsonexists
"Signature is INVALID"
- Verify message matches exactly (including whitespace)
- Ensure public key and signature are from same signing operation
- Check for copy/paste errors in base64 strings
"URL must start with http:// or https://"
- Ensure website URL includes protocol
- Example:
https://example.comnotexample.com
WorldID Integration Architecture
┌─────────────────────────────────────────────────────────────┐
│ CLI Tool (Enhanced) → Next.js Web Service → Supabase │
│ ↓ ↓ ↓ │
│ device.json WorldID Widget PostgreSQL │
│ (Local Keys) (QR + Verify) (Registry) │
└─────────────────────────────────────────────────────────────┘
The WorldID integration adds:
- Proof-of-Personhood: WorldID verification proves you're human
- Sybil Resistance: Nullifier hashes prevent duplicate registrations
- Public Registry: Supabase database stores verified registrations
- Remote Verification: REST API for signature + WorldID verification
Server Configuration
Set IDENTITY_SERVER environment variable:
# Production
export IDENTITY_SERVER="https://onemolt.ai"
# Local development
export IDENTITY_SERVER="http://localhost:3000"
# Add to shell profile for persistence
echo 'export IDENTITY_SERVER="https://onemolt.ai"' >> ~/.bashrc
Default: https://onemolt.ai
Identity Registry Setup
The identity registry is a separate Next.js application located at:
/Users/andy.wang/.openclaw/workspace/onemolt/
To deploy your own instance:
- Set up Supabase project and run migrations
- Configure WorldID app at https://developer.worldcoin.org
- Deploy to Vercel with environment variables
- Point CLI to your deployment
See the registry README for full setup instructions.
Security Best Practices
Traditional Security
- Never share your private key - It stays in
device.json - Use HTTPS - When submitting proofs to websites
- Verify timestamps - Services should reject old proofs
- Unique challenges - Services should use random challenges
- Store public keys - Services should save your public key for future verifications
WorldID Security
- One registration per human - WorldID prevents duplicate accounts
- Orb verification preferred - Highest security level
- Audit trail - All verifications logged in Supabase
- Nullifier hash checking - Prevents WorldID proof reuse
- Server-side verification - WorldID proofs verified by registry, not client
Integration Example: Verifying a Molt Bot
External services can verify both signature and WorldID status:
// External app verifying a molt bot
const response = await fetch('https://onemolt.ai/api/v1/verify/signature', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
deviceId: 'device-id-from-bot',
message: 'challenge-message',
signature: 'signature-from-bot'
})
})
const result = await response.json()
if (result.verified && result.worldIdVerified) {
console.log('✓ Bot is operated by verified human!')
console.log('Verification level:', result.verificationLevel)
console.log('Registered:', result.registeredAt)
} else if (result.verified) {
console.log('✓ Signature valid, but not WorldID verified')
} else {
console.log('✗ Verification failed')
}
Files
identity-proof/
├── SKILL.md # Skill metadata and quick reference
├── README.md # This file - full documentation
└── scripts/
└── identity-proof.sh # Main CLI tool (enhanced with WorldID)
onemolt/ # Separate Next.js application
├── app/api/v1/ # REST API endpoints
├── lib/ # Core libraries
├── supabase/migrations/ # Database schema
└── README.md # Registry setup guide
Support
For issues or questions:
- Check openclaw documentation
- Review
~/.openclaw/identity/device.jsonstructure - Test with
infocommand first