AI Security
Structured Logging Blueprint for AI‑Powered Tools that Invoke External APIs
TL;DR: Capture every AI‑initiated external request in a structured, immutable log that records who, what, when, and why. Store logs in a tamper‑evident location, apply retention policies, and enable automated alerts for anomalous patterns. This blueprint shows how to do it with minimal code, using Cloudflare Workers AI as an example.
Why Structured Logging Matters for AI‑Driven API Calls
AI agents often act as glue between internal data and third‑party services (e.g., CRM, payment gateways, SaaS analytics). Each call can expose sensitive identifiers or trigger costly actions. Without a reliable log you lose:
- Forensic visibility for incident response (NIST AI RMF – Respond function).
- Compliance evidence for GDPR, SOC 2, or HIPAA audits.
- Operational insight to detect credential misuse or prompt injection attacks (OWASP GenAI Top‑10).
What Data Should Be Captured
Record the following fields for every outbound request initiated by an AI workflow:
{
"timestamp": "2024-09-01T12:34:56Z",
"request_id": "c3f9a1b2-...",
"agent_name": "lead‑gen‑assistant",
"user_context": "[email protected]",
"api_endpoint": "https://api.stripe.com/v1/charges",
"http_method": "POST",
"request_payload_hash": "sha256:abcd1234...",
"response_status": 200,
"response_body_hash": "sha256:efgh5678...",
"duration_ms": 342,
"risk_score": 12,
"notes": "Triggered by user prompt ‘create invoice’"
}
Key considerations:
- Never log raw secrets (API keys, passwords). Store only a hash or a reference to a vault entry.
- Hash payloads to preserve privacy while still enabling deduplication.
- Include a risk_score derived from prompt‑analysis (e.g., presence of suspicious tokens).
Choosing a Log Format and Transport
JSON lines (one JSON object per line) is the de‑facto standard for structured logs. It works with most SIEMs and cloud log services.
Transport Options
- HTTP POST to a log ingestion endpoint (e.g., Cloudflare Logpush, Elastic Cloud).
- Write‑once storage like Amazon S3 with Object Lock or Cloudflare R2 immutable buckets.
- Message queues (Kafka, Cloudflare Workers KV) for real‑time processing before archival.
Secure Storage and Tamper‑Evidence
Store logs in a location that provides:
- Immutable write‑once semantics (S3 Object Lock, R2 with
immutable=true). - Encryption at rest (default for most cloud storage).
- Access controls limited to audit teams and automated alerting functions.
Example: Cloudflare Workers AI can forward logs to a Cloudflare R2 bucket with immutability enabled.
Retention, Rotation, and Deletion
Follow the principle of least‑privilege data retention:
- Keep detailed logs for 90 days for active monitoring.
- Archive compressed JSON‑L files for up to 2 years for compliance.
- Purge logs older than the regulatory window using lifecycle rules.
Never delete logs manually; rely on automated policies to avoid accidental loss.
Automated Alerting for Anomalous API Usage
Feed the structured logs into a simple rule engine (e.g., Cloudflare Workers KV + Cron) that raises alerts when:
- More than 10 high‑risk calls (
risk_score > 50) occur within a minute. - Requests to a new endpoint appear for the first time.
- Response status codes indicate failures > 5 xx.
Send alerts to Slack, email, or PagerDuty via webhook.
Sample Implementation with Cloudflare Workers AI
The snippet below shows a minimal Worker that wraps an AI call, logs the request, and forwards the log to an R2 bucket.
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const start = Date.now()
const requestId = crypto.randomUUID()
const body = await request.json()
// Call the AI model (simplified)
const aiResponse = await fetch('https://api.cloudflare.com/client/v4/accounts/.../ai/run', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body.prompt)
})
const duration = Date.now() - start
const logEntry = {
timestamp: new Date().toISOString(),
request_id: requestId,
agent_name: 'sales‑assistant',
user_context: body.user,
api_endpoint: 'https://api.stripe.com/v1/charges',
http_method: 'POST',
request_payload_hash: await crypto.subtle.digest('SHA-256', new TextEncoder().encode(body.prompt)).then(h => Buffer.from(h).toString('hex')),
response_status: aiResponse.status,
response_body_hash: await aiResponse.clone().text().then(t => crypto.subtle.digest('SHA-256', new TextEncoder().encode(t)).then(h => Buffer.from(h).toString('hex'))),
duration_ms: duration,
risk_score: assessRisk(body.prompt), // custom function
notes: 'AI‑generated invoice request'
}
// Store log in R2 (bucket named "ai-logs")
await AI_LOGS.put(`${requestId}.jsonl`, JSON.stringify(logEntry) + "\n", {
httpMetadata: { contentType: 'application/json' },
customMetadata: { immutable: 'true' }
})
return new Response(JSON.stringify({ success: true }), { status: 200 })
}
function assessRisk(prompt) {
// Very naive risk estimator – replace with your own model
return /\bdelete\b|\bdrop\b/i.test(prompt) ? 80 : 5
}
Replace AI_LOGS with your R2 binding. The log is written once and cannot be overwritten, satisfying tamper‑evidence requirements.
Key Takeaways
- Log every AI‑initiated external call with a structured JSON payload.
- Never store raw secrets; hash payloads instead.
- Use immutable, encrypted storage and enforce retention policies.
- Automate anomaly detection to catch prompt‑injection or credential abuse early.
- Leverage cloud‑native services (Workers AI, R2, Logpush) to keep implementation lightweight.
Need a practical AI security review?
AISecAll reviews prompts, tool permissions, document flows, and agent behavior so small teams can use AI without guessing where the risk sits.