How we secure Figma's internal systems with agents Figma's security engineering team built an AI agent on AWS Bedrock Knowledge Bases and Amazon Kendra that triages alerts, conducts forensic investigations, queries the security data lake, writes code fixes, and remembers what it learns, cutting alert time-to-resolution by 71% and changing how on-call engineers work. How we secure Figma’s internal systems with agents Our security team built an AI agent that triages alerts, conducts forensic investigations, queries our security data lake, writes code to fix issues—and remembers what it learns. Here's how we cut alert time-to-resolution by 71% and fundamentally changed how our on-call engineers work. Share How we secure Figma’s internal systems with agents Illustrations by Jimmy Simpson When it comes to protecting our internal platforms, the threats we defend against on Figma’s security engineering team are shifting constantly. Cloud infrastructure changes frequently, developers adopt new tools, and what people install and run on their laptops is different every quarter like when your Designer Advocates team starts vibe coding their own apps and automations https://www.figma.com/blog/4-ways-were-using-our-mcp-server-at-figma/ 1-create-and-refresh-decks-in-figma-slides . 4 ways we’re using our MCP server at Figma The Figma MCP server reaches further across the platform than it ever has. From updating a living deck to shipping a design to production—here's what that looks like in practice. Our SIEM, Panther, helps us keep ahead of all these threats by running a wide range of checks across our cloud infrastructure, endpoints, SaaS apps, and identity systems. When it detects a potential issue, it posts alerts into Slack and creates an Asana ticket for on-call engineers to pick up. Historically, being on call involved a massive amount of manual work. Gathering context was the main challenge. We’d have to figure out if the alert resembled something we’d just seen last week, if there was a pending PR that might address the issue, if Slack threads mentioned something related, and so on. Like many teams, we saw LLM model capabilities rapidly accelerate over the past year. We recently shared how Figma stays ahead of vulnerabilities with agents https://www.figma.com/blog/how-figma-stays-ahead-of-vulnerabilities-with-agents/ in our codebase, which caught issues in configuration changes to sensitive tools like Okta and AWS thanks to previous investments in storing their configuration as code. But to meaningfully reduce toil and improve how we protected Figma’s internal systems, we needed to go beyond the codebase and build a new system that could help us handle the wide scope of issues our SIEM finds. How Figma stays ahead of vulnerabilities with agents For the past year, agents at Figma have guarded code as it's written, reviewed every pull request, and audited a decade-old monorepo, all on one policy. We started with a narrow goal of building a retrieval layer that could surface prior on-call engineer reasoning when a new alert fired. That project eventually grew into a full agentic system that investigates alerts, queries audit logs, writes code changes, opens PRs, and gets better over time through its own memory. It’s completely changed how our security team works. The RAG layer: Giving alerts a memory the-rag-layer-giving-alerts-a-memory The first thing we built was a retrieval-augmented classification system on top of AWS Bedrock Knowledge Bases and Amazon Kendra. Again, our initial goal was just to surface historical context about what had happened the last time the same alert fired, and maybe suppress duplicate alerts. When a Panther alert fires, our Lambda handler converts it into a standardized document and indexes it into Kendra. We pull structured fields out of the raw alert payload: IPs from p any ip addresses , actors from p any usernames and various provider-specific user fields, AWS account IDs from ARNs. These become searchable Kendra document attributes alongside the alert title, severity, tags, and timestamps: js const attrs: DocumentAttribute = { Key: 'alert id', Value: { StringValue: doc.alert id } }, { Key: 'alert type', Value: { StringValue: doc.alert type } }, { Key: 'severity', Value: { StringValue: doc.severity } }, { Key: 'status', Value: { StringValue: doc.status } }, { Key: 'created at', Value: { DateValue: doc.created at } }, { Key: 'has investigation context', Value: { LongValue: doc.has investigation context } }, When the next similar alert fires, we query Bedrock for semantic matches using the alert title which is typically the name of the detection followed by the actor username as the primary vector: function buildQueryFromAlert alert: Alert : string { return ${alert.data.title}\n\nhas investigation context=1 } Another thing that improved the recommendations was biasing toward recent results. A similar alert from two days ago is way more useful than an exact duplicate alert from six months ago, because how we triage alerts is ever-evolving, too. We bias toward retrieving alerts that have investigation context , which are comments left by an on-call engineer in Slack about what they found. A closed alert with no comments tells you almost nothing. We capture investigation context without changing our engineers’ existing workflows. When someone leaves a note in a Slack alert thread and we have a similar procedure for Asana tickets , we index that comment back into Kendra as investigation context on the original alert: export async function addInvestigationContext alertId: string, contextText: string : Promise