Full spec for the highest identity tier of Bot Conduct Standard. Integration time: 10–15 minutes.
Level 3 identity binds every production request your bot makes to a cryptographic proof of origin. Sites integrating BCS verify the signature server-side before accepting the request with elevated trust.
Three properties matter:
BCS uses Ed25519 (Edwards-curve Digital Signature Algorithm, RFC 8032). 32-byte public key, 64-byte signatures, constant-time verification, quantum-unsafe (acknowledged, not a 2026 concern).
from nacl.signing import SigningKey
from nacl.encoding import HexEncoder
signing_key = SigningKey.generate()
private_key_hex = signing_key.encode(encoder=HexEncoder).decode()
public_key_hex = signing_key.verify_key.encode(encoder=HexEncoder).decode()
print("PRIVATE (keep secret):", private_key_hex)
print("PUBLIC (upload this):", public_key_hex)
const nacl = require('tweetnacl');
const kp = nacl.sign.keyPair();
const privateKey = Buffer.from(kp.secretKey).toString('hex');
const publicKey = Buffer.from(kp.publicKey).toString('hex');
import "crypto/ed25519"
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
// store hex-encoded
First, obtain operator_id and api_key via:
POST /api/operator/register with {email, operator_name} — triggers verification emailPOST /api/operator/verify with {email, code} — returns operator_id and api_keyThen upload your public key:
curl -X POST https://botconduct.org/api/operator/upload-key \
-H "Content-Type: application/json" \
-d '{
"operator_id": "op-xxxxxxxxxxxx",
"api_key": "bcs-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"public_key": "<64-char hex public key>"
}'
Response:
{
"status": "key_registered",
"identity_level": 3,
"key_fingerprint": "<first 16 chars of sha256(public_key_bytes)>",
"message": "Public key registered."
}
For every outbound request your bot makes, compute the canonical message and sign it.
BCS-v1\n
<HTTP_METHOD>\n
<TARGET_URL_exact_string>\n
<ISO8601_TIMESTAMP_UTC>\n
<NONCE_UUID_v4>\n
<SHA256(request_body)_hex or empty string>
Note: newline delimiter is LF (\n), not CRLF. The URL must be the exact string sent (including query string as given, not re-canonicalized).
from nacl.signing import SigningKey
import hashlib, uuid, datetime
def sign_request(method, url, body_bytes, private_key_hex, operator_id):
ts = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
nonce = str(uuid.uuid4())
body_sha = hashlib.sha256(body_bytes).hexdigest() if body_bytes else ""
message = f"BCS-v1\n{method}\n{url}\n{ts}\n{nonce}\n{body_sha}"
sk = SigningKey(bytes.fromhex(private_key_hex))
sig_bytes = sk.sign(message.encode()).signature
sig_hex = sig_bytes.hex()
return {
"X-BCS-Operator": operator_id,
"X-BCS-Timestamp": ts,
"X-BCS-Nonce": nonce,
"X-BCS-Signature": sig_hex,
}
Include these four on every request your bot makes in production:
X-BCS-Operator: op-xxxxxxxxxxxx
X-BCS-Timestamp: 2026-04-16T15:30:00Z
X-BCS-Nonce: 3f7b8c2e-9a1d-4b6e-8f5a-1c2d3e4f5a6b
X-BCS-Signature: <128-char hex Ed25519 signature>
Level 3 certification makes your agent recognizable at every BCS-integrated site without bilateral setup. Sites trust your signature because our engine validates it — you don't negotiate access with each site individually.
For a site integrating BCS, the common pattern is a two-layer policy: Fast Pass for verified high-score operators, and fall-through to their existing security stack for everyone else:
# In the site's middleware — conceptual
verdict = bcs.score(request)
if verdict["score"] >= 90 and verdict["certified"]:
allow() # Fast Pass: skip further checks for certified good actors
else:
# fall through to existing Cloudflare / DataDome / rules
existing_security_stack.process(request)
This is why Level 3 matters more than it looks. The cryptographic signature is what lets sites trust the score without calling our API for every single check — they can cache the signature and the per-operator public key, reducing API load and letting Fast Pass work even at CDN edge.
When a site integrating BCS receives your request, they call our verdict API. The engine:
operator_id (indexed lookup).|now − timestamp| ≤ 30 seconds.identity_level: 3 when verification succeeds.Upload a new public key using the same /api/operator/upload-key endpoint. The previous key enters a 7-day grace period and is then discarded. During the grace period, both keys verify.
POST /api/operator/revoke-key
{
"operator_id": "op-xxxxxxxxxxxx",
"api_key": "bcs-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"reason": "key_compromised | routine_rotation | other",
"replacement_public_key": "<optional new key hex>"
}
Revocation propagates to all BCS-integrated sites via the /api/revocations endpoint. Sites polling every 5 minutes (default) pick up the change; Enterprise sites with webhook push get it immediately.
Level 3 certificates are valid 90 days from issuance. You can renew by running the active test again or by demonstrating sustained acceptable passive behavior. Renewed cert retains the same operator_id and public key (unless you rotate).
GET /api/operator/<id>/stats periodically to detect anomalies (signatures from unexpected IPs, unusual volume).| Language | Library |
|---|---|
| Python | pynacl (pip install pynacl) |
| Node.js | tweetnacl (npm install tweetnacl) |
| Go | crypto/ed25519 (stdlib) |
| Rust | ed25519-dalek |
| Ruby | rbnacl |
| Java / JVM | com.google.crypto.tink or BouncyCastle |
| PHP | sodium_* functions (PHP 7.2+) |
Level 3 certification is free during early access.
Get started →Technical questions: hello@botconduct.org
X: @botconduct
Reference integration: importsignals.com/security
Spec version 1.0 — last updated 2026-04-16.
Comments welcome. Security issues: email hello@botconduct.org with subject "security".