惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
J
Java Code Geeks
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Last Week in AI
Last Week in AI
雷峰网
雷峰网
博客园_首页
小众软件
小众软件
美团技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
腾讯CDC
P
Proofpoint News Feed
MongoDB | Blog
MongoDB | Blog
Google DeepMind News
Google DeepMind News
MyScale Blog
MyScale Blog
U
Unit 42
The Cloudflare Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Microsoft Security Blog
Microsoft Security Blog
大猫的无限游戏
大猫的无限游戏
Engineering at Meta
Engineering at Meta
N
Netflix TechBlog - Medium
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 叶小钗

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
An agent called my payment API 50,000 times in 90 seconds...
All For Scie · 2026-04-23 · via DEV Community

It was 2:47 AM on a Tuesday.

My phone lit up with 47 alerts in under a minute.

"Payment endpoint: rate limit exceeded"
"Payment endpoint: 429 errors"
"Payment endpoint: CPU 98%"

I opened the logs. What I saw made my stomach drop.

Agent payments-batch-23a7 had called the /transfer endpoint 50,342 times in 90 seconds.

Each call succeeded.

Each call moved money.

And the API key? It worked perfectly. Authenticated every single request.


How we got here

Three months earlier, we had built a multi-agent payment system.

  • Agent A (orchestrator) received a customer request
  • Agent B (risk check) validated the transaction
  • Agent C (payment executor) called Stripe
  • Agent D (notification) sent confirmations

We secured it the way everyone does: API keys.

Each agent had a key. Each service validated the key. Simple. Familiar. We shipped fast.

We thought we were done.


The root cause

The 2:47 AM incident wasn't a hack. No external attacker.

It was a bug.

Agent B (risk check) entered an error loop. Every time it failed to validate, it retried. Every retry created a new payment request. The orchestrator saw each request as legitimate — because the API key was valid.

The key told us who was calling. It told us nothing about how many times or under what conditions.

Our rate limits were at the human level: 1000 requests per minute per key. Agent B's error loop generated 50k requests in 90 seconds — well under the per-minute limit because the loop was distributed across multiple instances.

We had no per-agent counters. No per-action limits. No circuit breakers at the agent level.


What we tried first

Fix #1: Stricter rate limits

We dropped the limit to 100 requests per minute per key.

Three hours later, a legitimate batch job failed. Customers complained. We reverted.

Fix #2: Manual approval for payments

Every transfer needed a human to click "approve" in a dashboard.

Agents are supposed to be autonomous. This defeated the entire point. Agents waited minutes for human clicks. Throughput collapsed.

Fix #3: Hardcoded agent IDs

We embedded agent IDs into the payment service logic.

Works until you add a new agent type. Then you modify code. Then you test. Then you deploy. Then you pray.

We added four new agent types in two weeks. The hardcoded approach became unmaintainable overnight.


What actually worked

We realized we needed four things that API keys don't provide:

  1. Per-agent counters — Agent B can call transfer 100 times. Then it's blocked.
  2. Per-action limits — Risk check can call "validate" 10k times but "transfer" only 100 times.
  3. Time-bound permissions — A batch agent only works between 2-4 AM. Outside that window, calls are rejected.
  4. Delegation tracing — When Agent C calls Stripe, we need to know the full chain (A → B → C), not just C.

We built all four into a system we called Codios.


How Codios changed our 2:47 AM problem

Here's what happens now when an agent calls our payment endpoint:

Before (API keys):

  • Check key → valid → execute → money moves → audit log shows "Agent C called /transfer"

After (Codios):

  • Agent carries a signed capability contract
  • The contract says: "Agent B can call /transfer 100 times, expires in 1 hour"
  • The payment service verifies the signature offline (~0ms)
  • Checks the counter — if 100 reached, reject
  • Checks expiry — if outside window, reject
  • Consumes a nonce to prevent replay
  • Writes to audit log with full delegation chain
  • Then executes the transfer

When Agent B's error loop happened again three weeks later:

Call #101 hit the contract limit. Rejected. No money moved. My phone didn't ring at 2:47 AM.


What we learned

API keys are not enough for agents.

Not because API keys are bad. Because they solve the wrong problem. Authentication is table stakes. Authorization — with scope, limits, and time — is what agents actually need.

Build for failure loops, not just happy paths.

We designed security for the "agent works correctly" case. We forgot the "agent breaks and calls the same endpoint 50k times" case. That's where all the risk lives.

Delegation chains need full visibility.

When something fails three agents deep, you need to know the whole path. Partial logs are worse than no logs — they send you down the wrong debugging path.


Where we are now

Codios runs in production across our payment, risk, and notification agents.

  • Average enforcement overhead: 1.8ms
  • False positives from rate limits: 0 since deployment
  • Unauthorized calls blocked: 127,000+ (mostly from error loops like the one above)
  • 2:47 AM phone calls: 0

If you're building agent systems

You don't have to build this yourself. It took us three months and two production incidents to get it right.

Codios is open for teams who want to skip the pain.

codios.midlantics.com

Or just reply here. Happy to share more war stories about what broke — and what finally worked.