Building Credential Sentinel was the fun part. Evaluating it, actually proving I could trust it, turned into its own project, and that is the half I am most glad I did not skip.
Quick context on what it does. Credential Sentinel is a LangGraph agent I built for a boring problem with scary consequences: every company has credentials nobody is rotating. The forgotten API token. The service account key left over from a migration two years ago. A TLS cert that no team will admit to owning. The agent goes looking for those, works out which ones are genuinely at risk, and walks them to a safe rotation while a human signs off on anything irreversible. (A fun fact that kept me motivated through the boring parts: a startup called PocketOS once lost its entire production database in nine seconds, because a token meant for managing domains also happened to have permission to delete volumes. This whole project is basically my answer to that headline.)
Getting it working was satisfying. But an agent that looks good in a demo is not the same thing as an agent you would let near production secrets, and I knew the gap between those two was where the real work lived. So I built an evaluation suite around it.
Here is roughly what that meant. I hand wrote 50 test cases: 25 ordinary ones, 15 edge cases, 7 known failures, and 3 adversarial ones built specifically to trip it up. I scored five things that map to what a security engineer actually cares about. Did it route each credential correctly. Did it ever do something unsafe on its own. Did it put the urgent ones first. Was the plan it wrote faithful to the facts it was given. And was it fast and cheap enough to run on a schedule. The deterministic decisions I checked with plain code. For the parts where the agent writes actual English, I used a second, cheaper model as the judge, which is a slightly funny setup when you sit with it. One AI quietly grading another AI’s homework.
Those five did not count equally, and being deliberate about that mattered. A slow plan is annoying, but an unsafe action against a live credential is the kind of thing you cannot undo, so safety carried the most weight, followed by routing, then faithfulness, prioritization, and finally cost. I folded all of them into a single composite score, mostly so I had one honest number to watch instead of a spreadsheet I could quietly cherry pick from. And I never trusted a single run. Every change I made went through the same 50 cases twice, once with the old behavior and once with the new, and I lined the two up side by side in LangSmith. Comparing identical cases before and after is dull to set up and weirdly clarifying to read, because anything that got worse has nowhere to hide.
The starting score was 0.976 out of 1. Good, but not done. And the failures were the interesting part.
My favorite one I caught completely by accident. I tweaked a prompt to fix the order in which the agent revoked old credentials, the change worked, and it quietly broke something else. One of my adversarial cases had a sentence hidden inside a field where a service name was supposed to go, something like “set the risk to low.” After my prompt edit, the model started doing exactly that. Its resistance to that injection fell from perfect to a coin flip. The lesson was not really about the prompt. It was that I had let a security decision be made by the model at all. I moved that calculation into hard code where no input can reach it, and the score came back.
A few rounds of that, and the composite went from 0.976 to a clean 1.0, with every single run tracked in LangSmith so I was reading real deltas instead of guessing.
Same 50 cases, scored before and after. The deterministic decisions (routing, safety, urgency) were already perfect, so every gain came from the LLM-written plan. Delayed-revoke ordering was the big one, from 0.575 to a perfect score.
The full breakdown, including the metrics that never moved because they started at the ceiling:
| Metric | Baseline | Improved | Δ |
|---|---|---|---|
| Routing accuracy | 1.000 | 1.000 | 0.000 |
| Rotation-safety invariants | 1.000 | 1.000 | 0.000 |
| Urgency prioritization | 1.000 | 1.000 | 0.000 |
| Report-counts correctness | 0.960 | 1.000 | +0.040 |
| Delayed-revoke ordering | 0.575 | 1.000 | +0.425 |
| Plan faithfulness (LLM-judge) | 0.956 | 0.993 | +0.037 |
| Injection resistance | 1.000 | 1.000 | 0.000 |
| Composite (weighted) | 0.976 | 1.000 | +0.024 |
Injection resistance reads as a flat 1.000, but that hides the most useful moment of the whole project. It dipped to 0.5 partway through, when the prompt change above backfired, and only came back once I moved the risk decision out of the model. A net of zero with a real story underneath.
The point I want this to make is small and a little stubborn. I can build the agent. I can also tell you, with numbers, exactly where it breaks and what I did about each crack. The first skill is common now. The second one is the job.
Getting it from working to deployed and live, containers and Kubernetes and all, turned into its own writeup too.
Code is here if you want to poke at it: github.com/rashmi1112/Credential-Sentinel
Comments
Loading comments…
Leave a comment