This tutorial walks you through integrating Kakunin runtime binding into an existing agent. By the end, every action your agent takes will be cryptographically signed and scope-enforced.
npm install @kakunin/sdk
pip install kakunin-sdk
import { KakuninClient } from '@kakunin/sdk';
const kakunin = new KakuninClient({
apiKey: process.env.KAKUNIN_API_KEY,
baseUrl: 'https://api.kakunin.ai',
});
from kakunin import KakuninClient
kakunin = KakuninClient(
api_key=os.getenv('KAKUNIN_API_KEY'),
base_url='https://api.kakunin.ai'
)
Create or retrieve an agent and bind it to your process.
async function setupAgent() {
// Option 1: Create a new agent
const agent = await kakunin.agents.create({
name: 'my_trading_bot',
metadata: { version: 'v1.0.0' },
});
// Option 2: Retrieve existing agent
// const agent = await kakunin.agents.get('my_trading_bot');
// Issue or retrieve certificate
const cert = await kakunin.agents.getCertificate(agent.id, {
validityDays: 365,
scope: {
maxTransactionSize: 50000,
allowedMarkets: ['EUR_USD', 'GBP_USD'],
allowedRegions: ['eu-west-1'],
},
});
// Store certificate and KMS ARN in memory
// Never store private key — it stays in KMS
return {
agentId: agent.id,
certificatePem: cert.certificate_pem,
kmsKeyArn: cert.kms_key_arn,
serialNumber: cert.serial_number,
};
}
const identity = await setupAgent();
console.log(`Agent bound: ${identity.agentId}`);
async def setup_agent():
# Create or retrieve agent
agent = await kakunin.agents.create(
name='my_trading_bot',
metadata={'version': 'v1.0.0'}
)
# Issue certificate
cert = await kakunin.agents.get_certificate(
agent['id'],
validity_days=365,
scope={
'max_transaction_size': 50000,
'allowed_markets': ['EUR_USD', 'GBP_USD'],
'allowed_regions': ['eu-west-1'],
}
)
return {
'agent_id': agent['id'],
'certificate_pem': cert['certificate_pem'],
'kms_key_arn': cert['kms_key_arn'],
'serial_number': cert['serial_number'],
}
identity = await setup_agent()
Before your agent executes any significant action (trade, write data, etc.), sign it:
async function executeTrade(tradeRequest: {
market: string;
side: 'BUY' | 'SELL';
size: number;
}) {
// 1. Serialize request deterministically
const payload = JSON.stringify(tradeRequest,
Object.keys(tradeRequest).sort()
);
// 2. Sign with agent's identity
const signedAction = await kakunin.actions.sign({
agentId: identity.agentId,
payload,
metadata: {
actionType: 'trade.execute',
timestamp: Date.now(),
},
});
console.log(`Action signed: ${signedAction.signature.substring(0, 20)}...`);
// 3. Submit to exchange with proof
const response = await fetch('https://api.exchange.com/v1/trades', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Agent-Certificate': identity.certificatePem,
'X-Agent-Signature': signedAction.signature,
'X-Agent-Timestamp': signedAction.timestamp.toString(),
'Authorization': `Bearer ${process.env.EXCHANGE_API_KEY}`,
},
body: payload,
});
if (!response.ok) {
throw new Error(`Trade failed: ${await response.text()}`);
}
return response.json();
}
// Execute trade
const trade = {
market: 'EUR_USD',
side: 'BUY',
size: 45000,
};
const result = await executeTrade(trade);
console.log(`Trade executed: ${result.orderId}`);
async def execute_trade(trade_request: dict):
# Serialize deterministically
payload = json.dumps(trade_request, sort_keys=True)
# Sign action
signed_action = await kakunin.actions.sign(
agent_id=identity['agent_id'],
payload=payload,
metadata={
'action_type': 'trade.execute',
'timestamp': int(time.time() * 1000),
}
)
# Submit with proof
async with aiohttp.ClientSession() as session:
async with session.post(
'https://api.exchange.com/v1/trades',
headers={
'X-Agent-Certificate': identity['certificate_pem'],
'X-Agent-Signature': signed_action['signature'],
'X-Agent-Timestamp': str(signed_action['timestamp']),
},
data=payload,
) as response:
if response.status != 200:
raise Exception(f"Trade failed: {await response.text()}")
return await response.json()
trade = {
'market': 'EUR_USD',
'side': 'BUY',
'size': 45000,
}
result = await execute_trade(trade)
Your agent should check revocation status periodically. Kakunin handles this automatically if you use the signing service, but you can also check manually:
async function checkRevocationStatus(): Promise<boolean> {
const status = await kakunin.certificates.getStatus(
identity.serialNumber
);
if (status.revoked) {
console.error(`Agent revoked: ${status.revocation_reason}`);
process.exit(1); // Stop execution immediately
}
return !status.revoked;
}
// Check on startup
await checkRevocationStatus();
// Check periodically (e.g., every 5 minutes)
setInterval(checkRevocationStatus, 5 * 60 * 1000);
async def check_revocation_status() -> bool:
status = await kakunin.certificates.get_status(
identity['serial_number']
)
if status.get('revoked'):
logger.error(f"Agent revoked: {status['revocation_reason']}")
sys.exit(1)
return not status.get('revoked', False)
await check_revocation_status()
asyncio.create_task(periodic_check(check_revocation_status, 5 * 60))
Log all actions with their signatures for audit trails:
async function logAction(
action: string,
request: any,
signature: string,
result: any
) {
const auditEntry = {
timestamp: new Date().toISOString(),
agent_id: identity.agentId,
agent_cert_serial: identity.serialNumber,
action_type: action,
request_payload: request,
signature: signature.substring(0, 50) + '...',
result_status: result.status || 'success',
};
// Log to stdout (structured for Datadog/Better Stack)
console.log(JSON.stringify(auditEntry));
// Optional: Send to compliance audit system
await complianceAuditLog.insert(auditEntry);
}
Test locally before deploying:
async function testIntegration() {
// 1. Setup
const identity = await setupAgent();
console.log('✓ Agent identity bound');
// 2. Sign action
const testPayload = JSON.stringify({ test: true });
const signed = await kakunin.actions.sign({
agentId: identity.agentId,
payload: testPayload,
});
console.log('✓ Action signed');
// 3. Check revocation
const notRevoked = await checkRevocationStatus();
console.log(`✓ Revocation check passed: ${notRevoked}`);
// 4. Test with mock exchange
const mockResponse = await testMockExchange(
testPayload,
identity.certificatePem,
signed.signature
);
console.log(`✓ Mock exchange accepted signature: ${mockResponse.ok}`);
console.log('\n✅ Runtime binding integration successful');
}
await testIntegration();
Create .env:
# Kakunin API
KAKUNIN_API_KEY=sk_live_xxxxxxxxxxxxxxxx
# Exchange API (if needed)
EXCHANGE_API_KEY=your_exchange_api_key
# Logging
LOG_LEVEL=info
AUDIT_LOG_ENDPOINT=https://audit.yourcompany.com/api/log
Now that your agent is cryptographically bound:
identity.certificatePemJSON.stringify with sorted keys)KAKUNIN_API_KEY is valid and not expired