openbotauthGet a cryptographic identity for your AI agent. Generate Ed25519 keys, sign your work, prove who you are — across any platform.
Install via ClawdBot CLI:
clawdbot install hammadtq/openbotauthCryptographic identity for AI agents. Register once, then sign HTTP requests (RFC 9421) anywhere. Optional browser integrations via per-request signing proxy.
User wants to: browse websites with signed identity, authenticate a browser session, sign HTTP requests as a bot, set up OpenBotAuth headers, prove human-vs-bot session origin, manage agent keys, sign scraping sessions, register with OBA registry, set up enterprise SSO for agents.
Bash
This skill is self-contained — no npm packages required. Core mode uses Node.js (v18+) + curl; proxy mode additionally needs openssl.
Core Mode (portable, recommended):
POST /agentsBrowser Mode (optional, runtime-dependent):
Keys are stored at ~/.config/openbotauth/key.json in OBA's canonical format:
{
"kid": "<thumbprint-based-id>",
"x": "<base64url-raw-public-key>",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\n...",
"privateKeyPem": "-----BEGIN PRIVATE KEY-----\n...",
"createdAt": "..."
}
The OBA token lives at ~/.config/openbotauth/token (chmod 600).
Agent registration info (agent_id, JWKS URL) should be saved in agent memory/notes after Step 3.
The bearer token is for registration only:
POST /agents (and key rotation)~/.config/openbotauth/token after registration completesMinimum scopes: agents:write + profile:read
keys:write if you need /keys endpointNever use global headers with OBA token:
set headers command applies headers globallyopen --headers)cat ~/.config/openbotauth/key.json 2>/dev/null && echo "---KEY EXISTS---" || echo "---NO KEY FOUND---"
If a key exists: read it to extract kid, x, and privateKeyPem. Check if the agent is already registered (look for agent_id in memory/notes). If registered, skip to Step 4 (signing).
If no key exists: proceed to Step 2.
Run this locally. Nothing leaves the machine.
node -e "
const crypto = require('node:crypto');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519');
const publicKeyPem = publicKey.export({ type: 'spki', format: 'pem' }).toString();
const privateKeyPem = privateKey.export({ type: 'pkcs8', format: 'pem' }).toString();
// Derive kid from JWK thumbprint (matches OBA's format)
const spki = publicKey.export({ type: 'spki', format: 'der' });
if (spki.length !== 44) throw new Error('Unexpected SPKI length: ' + spki.length);
const rawPub = spki.subarray(12, 44);
const x = rawPub.toString('base64url');
const thumbprint = JSON.stringify({ kty: 'OKP', crv: 'Ed25519', x });
const hash = crypto.createHash('sha256').update(thumbprint).digest();
const kid = hash.toString('base64url').slice(0, 16);
const dir = path.join(os.homedir(), '.config', 'openbotauth');
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
fs.writeFileSync(path.join(dir, 'key.json'), JSON.stringify({
kid, x, publicKeyPem, privateKeyPem,
createdAt: new Date().toISOString()
}, null, 2), { mode: 0o600 });
console.log('Key generated!');
console.log('kid:', kid);
console.log('x:', x);
"
Save the kid and x values — needed for registration.
This is a one-time setup that gives your agent a public JWKS endpoint for signature verification.
Ask the user:
I need an OpenBotAuth token to register my cryptographic identity. Takes 30 seconds:
>
1. Go to https://openbotauth.org/token
2. Click "Login with GitHub"
3. Copy the token and paste it back to me
>
The token looks like oba_ followed by 64 hex characters.
When they provide it, save it:
node -e "
const fs = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const dir = path.join(os.homedir(), '.config', 'openbotauth');
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
const token = process.argv[1].trim();
fs.writeFileSync(path.join(dir, 'token'), token, { mode: 0o600 });
console.log('Token saved.');
" "THE_TOKEN_HERE"
node -e "
const fs = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const dir = path.join(os.homedir(), '.config', 'openbotauth');
const key = JSON.parse(fs.readFileSync(path.join(dir, 'key.json'), 'utf-8'));
const tokenPath = path.join(dir, 'token');
const token = fs.readFileSync(tokenPath, 'utf-8').trim();
const AGENT_NAME = process.argv[1] || 'my-agent';
const API = 'https://api.openbotauth.org';
fetch(API + '/agents', {
method: 'POST',
redirect: 'error', // Never follow redirects with bearer token
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: AGENT_NAME,
agent_type: 'agent',
public_key: {
kty: 'OKP',
crv: 'Ed25519',
kid: key.kid,
x: key.x,
use: 'sig',
alg: 'EdDSA'
}
})
})
.then(r => { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); })
.then(async d => {
console.log('Agent registered!');
console.log('Agent ID:', d.id);
// Fetch session to get username for JWKS URL
const session = await fetch(API + '/auth/session', {
redirect: 'error', // Never follow redirects with bearer token
headers: { 'Authorization': 'Bearer ' + token }
}).then(r => { if (!r.ok) throw new Error('Session HTTP ' + r.status); return r.json(); });
const username = session.profile?.username || session.user?.github_username;
if (!username) throw new Error('Could not resolve username from /auth/session');
const jwksUrl = API + '/jwks/' + username + '.json';
// Write config.json for the signing proxy
fs.writeFileSync(path.join(dir, 'config.json'), JSON.stringify({
agent_id: d.id,
username: username,
jwksUrl: jwksUrl
}, null, 2), { mode: 0o600 });
console.log('Config written to ~/.config/openbotauth/config.json');
// Delete token — no longer needed after registration
fs.unlinkSync(tokenPath);
console.log('Token deleted (no longer needed)');
console.log('');
console.log('JWKS URL:', jwksUrl);
console.log('');
console.log('Save this to memory:');
console.log(JSON.stringify({
openbotauth: {
agent_id: d.id,
kid: key.kid,
username: username,
jwks_url: jwksUrl
}
}, null, 2));
})
.catch(e => console.error('Registration failed:', e.message));
" "AGENT_NAME_HERE"
curl https://api.openbotauth.org/jwks/YOUR_USERNAME.json
You should see your public key in the keys array. This is the URL that verifiers will use to check your signatures.
Save the agent_id, username, and JWKS URL to memory/notes — you'll need the JWKS URL for the Signature-Agent header in every signed request.
| Do | Don't |
|----|-------|
| curl -H "Authorization: Bearer ..." https://api.openbotauth.org/agents | Set bearer token as global browser header |
| Delete token after registration | Keep token in browsing session |
| Use origin-scoped headers for signing | Use set headers with bearer tokens |
| Store token at ~/.config/openbotauth/token (chmod 600) | Paste token into chat logs |
Generate RFC 9421 signed headers for a target URL. The output is a JSON object for agent-browser open --headers or set headers --json (OpenClaw).
Required inputs:
TARGET_URL — the URL being browsedMETHOD — HTTP method (GET, POST, etc.)JWKS_URL — your JWKS endpoint from Step 3 (the Signature-Agent value)node -e "
const { createPrivateKey, sign, randomUUID } = require('crypto');
const { readFileSync } = require('fs');
const { join } = require('path');
const { homedir } = require('os');
const METHOD = (process.argv[1] || 'GET').toUpperCase();
const TARGET_URL = process.argv[2];
const JWKS_URL = process.argv[3] || '';
if (!TARGET_URL) { console.error('Usage: node sign.js METHOD URL JWKS_URL'); process.exit(1); }
const key = JSON.parse(readFileSync(join(homedir(), '.config', 'openbotauth', 'key.json'), 'utf-8'));
const url = new URL(TARGET_URL);
const created = Math.floor(Date.now() / 1000);
const expires = created + 300;
const nonce = randomUUID();
// RFC 9421 signature base
const lines = [
'\"@method\": ' + METHOD,
'\"@authority\": ' + url.host,
'\"@path\": ' + url.pathname + url.search
];
const sigInput = '(\"@method\" \"@authority\" \"@path\");created=' + created + ';expires=' + expires + ';nonce=\"' + nonce + '\";keyid=\"' + key.kid + '\";alg=\"ed25519\"';
lines.push('\"@signature-params\": ' + sigInput);
const base = lines.join('\n');
const pk = createPrivateKey(key.privateKeyPem);
const sig = sign(null, Buffer.from(base), pk).toString('base64');
const headers = {
'Signature': 'sig1=:' + sig + ':',
'Signature-Input': 'sig1=' + sigInput
};
if (JWKS_URL) {
headers['Signature-Agent'] = JWKS_URL;
}
console.log(JSON.stringify(headers));
" "METHOD" "TARGET_URL" "JWKS_URL"
Replace the arguments:
METHOD — e.g., GETTARGET_URL — e.g., https://example.com/pageJWKS_URL — e.g., https://api.openbotauth.org/jwks/your-username.jsonFor strict verifiers: If a site rejects signatures from this inline signer, use @openbotauth/bot-cli (recommended) or the openbotauth-demos/packages/signing-ts reference signer.
For single signed navigation (demo / Radar proof):
agent-browser open <url> --headers '<OUTPUT_FROM_STEP_4>'
This uses origin-scoped headers (safer than global).
For real browsing (subresources/XHR): Use the signing proxy (Step A-C below).
OpenClaw browser:
set headers --json '<OUTPUT_FROM_STEP_4>'
With named session:
agent-browser --session myagent open <url> --headers '<OUTPUT_FROM_STEP_4>'
Important: re-sign before each navigation. Because RFC 9421 signatures are bound to @method, @authority, and @path, you must regenerate headers (Step 4) before navigating to a different URL. For continuous browsing, use the proxy instead.
node -e "
const { readFileSync, existsSync } = require('fs');
const { join } = require('path');
const { homedir } = require('os');
const f = join(homedir(), '.config', 'openbotauth', 'key.json');
if (!existsSync(f)) { console.log('No identity found. Run Step 2 first.'); process.exit(0); }
const k = JSON.parse(readFileSync(f, 'utf-8'));
console.log('kid: ' + k.kid);
console.log('Public (x): ' + k.x);
console.log('Created: ' + k.createdAt);
"
Status: Not yet implemented. This describes the planned direction.
For organizations using Okta, WorkOS, or Descope: OBA will support binding agent keys to enterprise subjects issued by your IdP. OBA is not replacing your IdP directory — it attaches verifiable agent keys and audit trails to identities you already manage.
Planned flow:
This complements (not competes with) IdP-native agent features — you get portable keys + web verification surface.
Every signed request produces these RFC 9421-compliant headers:
| Header | Purpose |
|--------|---------|
| Signature | sig1=: |
| Signature-Input | Covered components (@method @authority @path), created, expires, nonce, keyid, alg |
| Signature-Agent | JWKS URL for public key resolution (from OBA Registry) |
The Signature-Input encodes everything a verifier needs: which components were signed, when, by whom (keyid), and when it expires.
When running inside OpenClaw, you can include the session key in the nonce or as a custom parameter to bind the signature to the originating chat:
agent:main:main # Main chat session
agent:main:discord:channel:123456789 # Discord channel
agent:main:subagent:<uuid> # Spawned sub-agent
This lets publishers trace whether a request came from the main agent or a sub-agent.
Sub-agent key derivation (HKDF from parent key) is planned but not yet implemented in a cryptographically sound way. For now, sub-agents should:
A proper delegation/attestation protocol is being designed.
RFC 9421 signatures are per-request — they are bound to the specific method, authority, and path. Setting headers once (Steps 4-5) only works for the initial page load. Sub-resources, XHRs, and redirects will carry stale signatures and get blocked.
Solution: Start a local signing proxy. It intercepts every HTTP/HTTPS request and adds a fresh signature automatically. No external packages needed — uses only Node.js built-ins and openssl.
cat > /tmp/openbotauth-proxy.mjs << 'PROXY_EOF'
import { createServer as createHttpServer, request as httpRequest } from "node:http";
import { request as httpsRequest } from "node:https";
import { createServer as createTlsServer } from "node:tls";
import { connect, isIP } from "node:net";
import { createPrivateKey, sign as cryptoSign, randomUUID, createHash } from "node:crypto";
import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync } from "node:fs";
import { join } from "node:path";
import { homedir } from "node:os";
import { execFileSync } from "node:child_process";
const OBA_DIR = join(homedir(), ".config", "openbotauth");
const KEY_FILE = join(OBA_DIR, "key.json");
const CONFIG_FILE = join(OBA_DIR, "config.json");
const CA_DIR = join(OBA_DIR, "ca");
const CA_KEY = join(CA_DIR, "ca.key");
const CA_CRT = join(CA_DIR, "ca.crt");
// Load credentials
if (!existsSync(KEY_FILE)) { console.error("No key found. Run keygen first."); process.exit(1); }
const obaKey = JSON.parse(readFileSync(KEY_FILE, "utf-8"));
let jwksUrl = null;
if (existsSync(CONFIG_FILE)) { const c = JSON.parse(readFileSync(CONFIG_FILE, "utf-8")); jwksUrl = c.jwksUrl || null; }
// Strict hostname validation (blocks shell injection & path traversal)
const HOSTNAME_RE = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
function isValidHostname(h) {
return typeof h === "string" && h.length > 0 && h.length <= 253 && (HOSTNAME_RE.test(h) || isIP(h) > 0);
}
// Ensure CA exists
mkdirSync(CA_DIR, { recursive: true, mode: 0o700 });
if (!existsSync(CA_KEY) || !existsSync(CA_CRT)) {
console.log("Generating proxy CA certificate (one-time)...");
execFileSync("openssl", ["req", "-x509", "-new", "-nodes", "-newkey", "ec", "-pkeyopt", "ec_paramgen_curve:prime256v1", "-keyout", CA_KEY, "-out", CA_CRT, "-days", "3650", "-subj", "/CN=OpenBotAuth Proxy CA/O=OpenBotAuth"], { stdio: "pipe" });
execFileSync("chmod", ["600", CA_KEY], { stdio: "pipe" });
}
// Per-domain cert cache
const certCache = new Map();
function getDomainCert(hostname) {
if (!isValidHostname(hostname)) throw new Error("Invalid hostname: " + hostname.slice(0, 50));
if (certCache.has(hostname)) return certCache.get(hostname);
// Use hash for filenames to prevent path traversal
const hHash = createHash("sha256").update(hostname).digest("hex").slice(0, 16);
const tk = join(CA_DIR, `_t_${hHash}.key`), tc = join(CA_DIR, `_t_${hHash}.csr`);
const to = join(CA_DIR, `_t_${hHash}.crt`), te = join(CA_DIR, `_t_${hHash}.ext`);
try {
execFileSync("openssl", ["ecparam", "-genkey", "-name", "prime256v1", "-noout", "-out", tk], { stdio: "pipe" });
execFileSync("openssl", ["req", "-new", "-key", tk, "-out", tc, "-subj", `/CN=${hostname}`], { stdio: "pipe" });
writeFileSync(te, `subjectAltName=DNS:${hostname}\nbasicConstraints=CA:FALSE\nkeyUsage=digitalSignature,keyEncipherment\nextendedKeyUsage=serverAuth`);
execFileSync("openssl", ["x509", "-req", "-sha256", "-in", tc, "-CA", CA_CRT, "-CAkey", CA_KEY, "-CAcreateserial", "-out", to, "-days", "365", "-extfile", te], { stdio: "pipe" });
const r = { key: readFileSync(tk, "utf-8"), cert: readFileSync(to, "utf-8") };
certCache.set(hostname, r);
return r;
} finally { for (const f of [tk, tc, to, te]) try { unlinkSync(f); } catch {} }
}
// RFC 9421 signing
function signReq(method, authority, path) {
const created = Math.floor(Date.now() / 1000), expires = created + 300, nonce = randomUUID();
const lines = [`"@method": ${method.toUpperCase()}`, `"@authority": ${authority}`, `"@path": ${path}`];
const sigInput = `("@method" "@authority" "@path");created=${created};expires=${expires};nonce="${nonce}";keyid="${obaKey.kid}";alg="ed25519"`;
lines.push(`"@signature-params": ${sigInput}`);
const sig = cryptoSign(null, Buffer.from(lines.join("\n")), createPrivateKey(obaKey.privateKeyPem)).toString("base64");
const h = { signature: `sig1=:${sig}:`, "signature-input": `sig1=${sigInput}` };
if (jwksUrl) h["signature-agent"] = jwksUrl;
return h;
}
const verbose = process.argv.includes("--verbose") || process.argv.includes("-v");
const port = parseInt(process.argv.find((a,i) => process.argv[i-1] === "--port")) || 8421;
let rc = 0;
function log(id, msg) { if (verbose) console.log(`[${id}] ${msg}`); }
const server = createHttpServer((cReq, cRes) => {
const id = ++rc, url = new URL(cReq.url), auth = url.host, p = url.pathname + url.search;
const sig = signReq(cReq.method, auth, p);
log(id, `HTTP ${cReq.method} ${auth}${p} → signed`);
const h = { ...cReq.headers }; delete h["proxy-connection"]; delete h["proxy-authorization"];
Object.assign(h, sig); h.host = auth;
const fn = url.protocol === "https:" ? httpsRequest : httpRequest;
const pr = fn({ hostname: url.hostname, port: url.port || (url.protocol === "https:" ? 443 : 80), path: p, method: cReq.method, headers: h }, (r) => { cRes.writeHead(r.statusCode, r.headers); r.pipe(cRes); });
pr.on("error", (e) => { log(id, `Error: ${e.message}`); cRes.writeHead(502); cRes.end("Proxy error"); });
cReq.pipe(pr);
});
server.on("connect", (req, cSock, head) => {
const id = ++rc, [host, ps] = req.url.split(":"), tp = parseInt(ps) || 443;
// Validate host and port before processing
if (!isValidHostname(host) || tp < 1 || tp > 65535) {
log(id, `CONNECT rejected: invalid ${host}:${tp}`);
cSock.write("HTTP/1.1 400 Bad Request\r\n\r\n"); cSock.end(); return;
}
log(id, `CONNECT ${host}:${tp} → MITM`);
cSock.write("HTTP/1.1 200 Connection Established\r\nProxy-Agent: openbotauth-proxy\r\n\r\n");
const dc = getDomainCert(host);
const tls = createTlsServer({ key: dc.key, cert: dc.cert }, (ts) => {
let data = Buffer.alloc(0);
ts.on("data", (chunk) => {
data = Buffer.concat([data, chunk]);
const he = data.indexOf("\r\n\r\n");
if (he === -1) return;
const hs = data.subarray(0, he).toString(), body = data.subarray(he + 4);
const ls = hs.split("\r\n"), [method, path] = ls[0].split(" ");
const rh = {};
for (let i = 1; i < ls.length; i++) { const c = ls[i].indexOf(":"); if (c > 0) rh[ls[i].substring(0, c).trim().toLowerCase()] = ls[i].substring(c + 1).trim(); }
const cl = parseInt(rh["content-length"]) || 0, fp = path || "/";
const sig = signReq(method, host + (tp !== 443 ? `:${tp}` : ""), fp);
log(id, `HTTPS ${method} ${host}${fp} → signed`);
Object.assign(rh, sig);
const pr = httpsRequest({ hostname: host, port: tp, path: fp, method, headers: rh, rejectUnauthorized: true }, (r) => {
let resp = `HTTP/1.1 ${r.statusCode} ${r.statusMessage}\r\n`;
const rw = r.rawHeaders; for (let i = 0; i < rw.length; i += 2) resp += `${rw[i]}: ${rw[i+1]}\r\n`;
resp += "\r\n"; ts.write(resp); r.pipe(ts);
});
pr.on("error", (e) => { log(id, `Error: ${e.message}`); ts.end("HTTP/1.1 502 Bad Gateway\r\nContent-Length: 0\r\n\r\n"); });
if (body.length > 0) pr.write(body);
if (cl <= body.length) { pr.end(); } else {
let recv = body.length;
const bh = (d) => { recv += d.length; pr.write(d); if (recv >= cl) { pr.end(); ts.removeListener("data", bh); } };
ts.on("data", bh);
}
});
});
tls.listen(0, "127.0.0.1", () => {
const lc = connect(tls.address().port, "127.0.0.1", () => { lc.write(head); lc.pipe(cSock); cSock.pipe(lc); });
lc.on("error", () => cSock.end()); cSock.on("error", () => lc.end());
cSock.on("close", () => { tls.close(); lc.end(); });
});
});
server.listen(port, "127.0.0.1", () => {
console.log(`openbotauth signing proxy on http://127.0.0.1:${port}`);
console.log(` kid: ${obaKey.kid}`);
if (jwksUrl) console.log(` Signature-Agent: ${jwksUrl}`);
console.log("Every request gets a fresh RFC 9421 signature.");
});
PROXY_EOF
echo "Proxy written to /tmp/openbotauth-proxy.mjs"
node /tmp/openbotauth-proxy.mjs --verbose
This starts the signing proxy on 127.0.0.1:8421. Every HTTP and HTTPS request flowing through it gets a fresh RFC 9421 Ed25519 signature.
In another terminal (or from agent-browser):
# For demos (ignore cert warnings):
agent-browser --proxy http://127.0.0.1:8421 --ignore-https-errors open https://example.com
# For production: install ~/.config/openbotauth/ca/ca.crt as trusted CA
TLS Note: The proxy MITMs HTTPS by generating per-domain certs signed by a local CA. Either:
--ignore-https-errors for demos/testing~/.config/openbotauth/ca/ca.crt as a trusted CA for clean operationThe proxy:
Signature-Agent header (JWKS URL) on every request127.0.0.1:8421 by default (configurable with --port)Security warning: ~/.config/openbotauth/ca/ca.key is a local MITM root key. Treat it as sensitive as a private key — if stolen, an attacker can intercept traffic on that machine.
Limitations:
subjectAltName=IP: instead of DNS: (current code uses DNS, which strict clients may reject)When to use Steps 4-5 instead: Simple single-page-load scenarios where you control every navigation and can re-sign before each one.
~/.config/openbotauth/key.json with 0600 permissions — never expose them~/.config/openbotauth/token is also sensitive — never log or share itSignature-Agent must point to a publicly reachable JWKS URL for verification to workcrypto module — no npm dependencies requiredapi.openbotauth.org~/.config/openbotauth/token after registration. You won't need it for signing.set headers with bearer tokens in agent-browser. Use open --headers for origin-scoped injection.~/.config/openbotauth/
├── key.json # kid, x, publicKeyPem, privateKeyPem (chmod 600)
├── key.pub.json # Public JWK for sharing (chmod 644)
├── config.json # Agent ID, JWKS URL, registration info
├── token # oba_xxx bearer token (chmod 600)
└── ca/ # Proxy CA certificate (auto-generated)
├── ca.key # CA private key
└── ca.crt # CA certificate
| Runtime | Support | Notes |
|---------|---------|-------|
| Claude Code / Cursor / Codex | ✅ Full | Recommended path - CLI registration |
| agent-browser | ✅ Full | Use scoped headers, not global |
| OpenClaw Browser Relay | ✅ After registration | Register via CLI first |
| CUA / Browser Control | ⚠️ Caution | Treat control plane as hostile |
| skills.sh | ✅ Full | curl-based registration is safe |
For browser runtimes: Complete registration in CLI mode. The signing proxy only needs the private key (local) and JWKS URL (public). No bearer token needed during browsing.
For production integrations, prefer the official packages:
@openbotauth/verifier-client — verify signatures@openbotauth/registry-signer — key generation and JWK utilities@openbotauth/bot-cli — CLI for signing requests@openbotauth/proxy — signing proxyFor strict RFC 9421 signing, use the reference signer from openbotauth-demos (packages/signing-ts).
Generated Mar 1, 2026
Companies performing large-scale web scraping need to distinguish their automated agents from malicious bots. OpenBotAuth allows them to register cryptographic identities and sign all HTTP requests, enabling websites to verify legitimate business scraping while blocking unauthorized access. This is particularly valuable for market research firms and price comparison services that require consistent, authenticated access to e-commerce sites.
Organizations deploying multiple AI agents across departments can use OpenBotAuth to establish centralized identity management. Each agent registers once with the enterprise's OpenBotAuth instance, then signs all internal API requests, allowing IT teams to audit and control agent access to sensitive systems. This replaces ad-hoc API keys with cryptographically verifiable identities for HR automation, finance reporting, and customer service bots.
Customer support teams using AI agents to browse customer accounts need to prove the session originates from an authorized support bot rather than a human operator. OpenBotAuth's browser integration allows signing each request, enabling systems to verify the agent's identity while maintaining customer privacy. This is essential for banking, healthcare, and SaaS companies where support agents require temporary access to user accounts.
Social media platforms and online communities using AI agents for content moderation need to distinguish their official moderation bots from third-party scrapers. OpenBotAuth provides cryptographic signatures that platforms can verify at the edge, ensuring only authorized moderation actions are processed. This prevents impersonation attacks while allowing legitimate moderation bots to operate at scale across millions of requests daily.
Companies with microservices architectures can use OpenBotAuth to authenticate inter-service communication where services are implemented as AI agents. Each service registers its identity, and all internal API calls include verifiable signatures, replacing traditional API keys with more secure, auditable authentication. This is particularly useful for financial institutions and logistics companies where service-to-service communication must be tamper-proof.
Offer managed OpenBotAuth instances for large organizations, providing custom domains, enhanced security features, and compliance reporting. Charge annual subscription fees based on the number of registered agents and request volume, with premium tiers offering advanced key rotation, audit logging, and integration support. Target regulated industries like finance and healthcare that require strict identity governance.
Create and sell specialized SDKs, browser extensions, and CLI tools that simplify OpenBotAuth integration for developers. Offer free open-source core libraries with premium commercial licenses for advanced features like automated key rotation, multi-cloud support, and enterprise-grade monitoring. Generate revenue through license fees, support contracts, and custom integration services for large deployments.
Build a marketplace where websites can subscribe to verified agent lists and signature verification services. Websites pay to access real-time verification of registered agents, while agent operators pay for premium verification badges and reputation scoring. Create network effects where more websites using the service attracts more agents to register, generating transaction fees from both sides.
💬 Integration Tip
Always generate keys locally first before registration, and immediately delete bearer tokens after setup to prevent security leaks in browser sessions.
Set up and use 1Password CLI (op). Use when installing the CLI, enabling desktop app integration, signing in (single or multi-account), or reading/injecting/running secrets via op.
Security-first skill vetting for AI agents. Use before installing any skill from ClawdHub, GitHub, or other sources. Checks for red flags, permission scope, and suspicious patterns.
Perform a comprehensive read-only security audit of Clawdbot's own configuration. This is a knowledge-based skill that teaches Clawdbot to identify hardening opportunities across the system. Use when user asks to "run security check", "audit clawdbot", "check security hardening", or "what vulnerabilities does my Clawdbot have". This skill uses Clawdbot's internal capabilities and file system access to inspect configuration, detect misconfigurations, and recommend remediations. It is designed to be extensible - new checks can be added by updating this skill's knowledge.
Use when reviewing code for security vulnerabilities, implementing authentication flows, auditing OWASP Top 10, configuring CORS/CSP headers, handling secrets, input validation, SQL injection prevention, XSS protection, or any security-related code review.
Security check for ClawHub skills powered by Koi. Query the Clawdex API before installing any skill to verify it's safe.
Scan Clawdbot and MCP skills for malware, spyware, crypto-miners, and malicious code patterns before you install them. Security audit tool that detects data exfiltration, system modification attempts, backdoors, and obfuscation techniques.