Interviewed by: Mehadi Hasan
Developer: Marco Bellingeri
Role: Cloud & AI Security Engineer | Python, AWS, AI Agents, and DevSecOps
Location: Italy
Website: marcobellingeri.dev
A security test can pass while the vulnerability it was designed to catch remains exploitable. For Marco Bellingeri, discovering that failure in his own AI system became one of the most important lessons of his engineering journey.
Bellingeri works at the intersection of cloud infrastructure, artificial intelligence, and application security. His projects explore practical challenges in building AI-powered systems, from protecting language models against prompt injection to independently monitoring the actions of coding agents. Rather than treating AI security as a collection of restrictions placed around a model, he approaches it as an engineering problem involving permissions, evidence, observability, and verification.
His background spans systems programming, networking, Linux, cybersecurity, infrastructure monitoring, and production Python applications. That experience shaped a perspective that now runs through his work: a system should not be trusted simply because its implementation looks correct or its monitoring dashboard is green.
Through projects such as MonferrinoAI, Agentic-OS, and LLM-Council, Bellingeri explores a central question for modern AI engineering: How do we prove that an AI system behaves as intended, rather than merely assuming that it does?
From Systems Engineering to AI Security
Bellingeri did not begin his career with artificial intelligence. His path developed gradually through systems, programming, networking, Linux, and cybersecurity. He also spent several years teaching computer science and security-related subjects, an experience that helped him understand how different components interact within a larger system.
Infrastructure and observability became important parts of that foundation. His earlier work involved Linux environments, virtual machines, VPNs, Prometheus, Grafana, and cloud monitoring. From there, cloud engineering became a natural progression.
He later moved deeper into Python-based software engineering, contributing to production systems that included 24/7 monitoring software for Rome Metro Line C, enterprise network diagnostics, and a larger backend platform built around Python and AWS.
That backend platform involved serverless components, asynchronous processing, databases, queues, and interconnected services. Working with these systems introduced challenges that extended beyond writing code: handling failures, managing permissions, tracking state, and understanding what happens when individual components behave unexpectedly.
Artificial intelligence added another layer of complexity.
When Bellingeri began building applications with large language models (LLMs), retrieval-augmented generation (RAG), and AI agents, he became interested not only in their capabilities but also in what happened when they were treated as components of real software systems.
Models introduce uncertainty. They process untrusted inputs, retrieve external information, interact with tools, and sometimes produce incorrect results with considerable confidence. Those characteristics make security inseparable from the broader engineering challenge.
Today, Bellingeri approaches an AI system with two questions:
What can it do, and what evidence do I have that it behaves the way I think it does?
That distinction between capability and evidence provides a foundation for understanding his projects.
MonferrinoAI: Making RAG Useful Beyond Retrieval
One of Bellingeri's projects, MonferrinoAI, began with a practical business problem: how can a local business website remain current and visible over time, particularly for search engine optimization (SEO), answer engine optimization (AEO), competitor awareness, and local Google search?
A website can be redesigned and launched successfully but gradually become outdated as competitors publish new material and search behavior changes. Bellingeri wanted to explore a system that could maintain an evolving knowledge base about a business website and its competitive environment, identify content gaps, and help determine what should be developed next.
MonferrinoAI uses scheduled GitHub Actions workflows to automate much of that preparation.
Firecrawl collects web content from the business website and competitors. Voyage AI generates embeddings, while Supabase, PostgreSQL, and pgvector provide the storage and retrieval foundation.
The system maintains information at multiple levels, including competitor snapshots, vectors representing complete pages, and more granular vectors representing individual website sections.
Each week, it selects a candidate from an SEO backlog, queries the RAG system to understand what the website already covers, and produces a structured briefing.
An important architectural decision is that MonferrinoAI does not autonomously write and publish content. It gathers information, refreshes the knowledge base, identifies opportunities, and prepares context. The actual writing remains a human responsibility, as does the decision to merge or publish the resulting work.
This human-in-the-loop approach reflects a broader principle in Bellingeri's engineering philosophy: automation can assist with analysis and preparation without automatically receiving authority over the final action.
Building MonferrinoAI also challenged a common assumption about RAG systems: that successful retrieval necessarily produces useful results.
Bellingeri tested retrieval against the actual database and found that a keyword-only query could rank the wrong page above the page that was genuinely relevant. Including additional context, such as the keyword cluster and target page, improved retrieval behavior.
The problem was not that the database returned invalid results. It returned results that were less useful for the decision the system needed to make.
That experience changed how he thought about retrieval. Instead of treating it as a simple step in which relevant-looking material is passed to a model, he began focusing more explicitly on evidence, relevance, provenance, and the limits of what the retrieved information could justify.
Freshness presented another challenge. Even if every subsequent component worked correctly, recommendations based on outdated website or competitor information could still lead to poor decisions.
Security introduced a related concern. Web pages are untrusted inputs, and their contents can include text that resembles instructions. Such text should not acquire authority over the AI system merely because it was retrieved successfully.
In MonferrinoAI, the information passed from RAG into the final briefing is deliberately constrained. Rather than passing retrieved page bodies directly into that handoff, the system exposes information such as URLs, headings, and similarity distances.
The objective is to limit how much untrusted content crosses into the next stage.
For Bellingeri, these are not separate problems to solve independently. Information quality, reliability, and security are connected because each depends on knowing where information originated, how relevant it is, and what the system is permitted to do with it.
Agentic-OS: Observing What AI Agents Actually Do
While MonferrinoAI focuses on retrieving and preparing information, Agentic-OS addresses another challenge: understanding what happens when AI agents interact with development environments.
Bellingeri noticed a gap between what a coding agent reports doing and what actually occurred in the environment. An agent might describe an action, but its own account is not necessarily sufficient evidence that the action happened as reported.
This matters because coding agents can do considerably more than generate text. They can call tools, modify files, execute commands, consume resources, and make decisions across multiple steps.
Traditional observability already helps engineers understand complex software. Agentic systems make independent observation even more important because the system being monitored can also produce the narrative used to explain its behavior.
Agentic-OS was built to explore this problem.
The platform collects telemetry from Claude Code through an OpenTelemetry Collector, using Prometheus and Grafana for metrics and a private Loki pipeline for logs. It also includes a small status API, while its public-facing surface exposes only a sanitized subset of aggregate information.
The architecture separates internal operational visibility from the information made publicly available.
Observability is also a privacy boundary
Building the platform revealed another issue: telemetry can contain sensitive information that engineers might not expect to appear in monitoring data.
Bellingeri found that the real Claude Code client could send identity attributes, including an email address. This meant that the observability pipeline itself needed to be treated as a privacy boundary.
Simply assuming that sensitive attributes would never arrive was not sufficient.
His approach uses allow-lists and tests against the actual telemetry structure so that unknown attributes are dropped rather than stored by default.
This is an important distinction. Monitoring systems are often designed to collect enough information to explain failures, but collecting more information can also introduce privacy risks. Observability therefore needs its own controls and verification.
For Bellingeri, monitoring an AI agent means looking beyond its final answer. Engineers need to understand which actions occurred, which tools were involved, what failed, how long operations took, and whether the system has independent evidence that reported actions actually happened.
As AI agents become more capable, observability becomes part of the trust model rather than merely a debugging convenience.
LLM-Council: Why Multiple Models Do Not Guarantee the Truth
Bellingeri's LLM-Council project explores a different problem: how to evaluate AI-generated answers without treating a single model as an unquestionable authority.
Language models can produce useful reasoning, but they can also be confidently wrong, follow a user's framing too closely, or reinforce assumptions that deserve to be challenged.
LLM-Council responds by asking three different models to generate answers independently. Their responses are anonymized, and the models blindly rank one another's answers. A separate chairman model then produces a final synthesis while surfacing meaningful disagreements.
The approach introduces additional perspectives rather than relying on a single response.
However, Bellingeri is careful about what this process can establish.
Multiple models can share the same blind spots. Agreement does not automatically make an answer correct, and a consensus can still be wrong.
The value lies in obtaining additional evidence through independent attempts, disagreement, and potentially different failure modes. The evaluation process itself must also be tested.
To assess the system, Bellingeri uses unit tests, adversarial cases, live end-to-end checks, and mutation testing. These methods help examine not only the answers but also whether the mechanisms intended to evaluate them can detect failures.
That last concern leads to one of the most revealing experiences in his work: a security test that passed while the attack it was meant to prevent still worked.
The Prompt-Injection Test That Passed While the Attack Worked
The incident occurred inside LLM-Council, where output from one model stage becomes input to another. Bellingeri needed to separate untrusted model-generated content from the instructions surrounding it.
His initial implementation used static delimiters to mark the boundaries of untrusted content. He wrote tests that checked marker placement and string structure.
Those tests passed.
But they were checking whether the Python string had been assembled correctly, not whether the next model would interpret the boundaries safely.
A hostile model output could reproduce the static closing delimiter, making the downstream model interpret the untrusted block as though it had ended. The tests remained green because the structure matched their expectations, even though the intended security property was not enforced.
The distinction was fundamental: a test can verify that a mechanism exists without proving that the mechanism provides the protection engineers expect.
Testing the property, not the implementation
Bellingeri changed the delimiter mechanism to use a fresh random nonce for each prompt. This made the authentic closing marker unpredictable to an untrusted response. He also identified another boundary requiring similar protection: model-generated rankings entering the chairman stage were untrusted as well.
He then tested the protections by deliberately breaking them.
He replaced the random nonce with a static value and expected the relevant tests to fail. He also removed the ranking boundaries and expected their tests to detect the change.
This approach provided stronger evidence that the tests were observing the security properties they were intended to protect.
There is an important limitation, however. A random nonce helps protect the structural boundary against delimiter forgery; it does not make the content inside that boundary trustworthy or solve prompt injection as a whole.
Bellingeri's lesson extends beyond this particular implementation. A test's name describes a claim about the system, but its assertions determine what evidence the test actually provides.
Before trusting a security test, he now wants to see that it can detect a known-bad state.
The practical question is simple: if the protection stops working, what should cause the test to fail?
Penetration Testing Your Own Systems
Bellingeri applies a similar approach when evaluating the security of systems he has built himself.
In his work on Agentic-OS, he treated the project as a security engagement, combining a read-only review of code and configuration with dynamic testing of the full stack in a controlled local environment.
The goal was not simply to discover unusual vulnerabilities. It was to identify cases where a security control existed and appeared reasonable but provided less protection than its documentation or his assumptions suggested.
Agentic-OS was particularly useful for this exercise because the observability pipeline has its own security requirements, including authentication, privacy filtering, storage boundaries, separation of public and private telemetry, and monitoring of the monitoring system.
The exercise reinforced the limits of reviewing configuration files in isolation. Some security properties can only be established by exercising the actual running component or testing against the real shape of its data.
He also distinguishes between a potential concern and a demonstrated security failure.
When prioritizing an issue, he considers the complete path from an initial condition to an exploitable action, a resulting state change, and the actual impact. A weakness that demonstrably crosses an authorization boundary or exposes sensitive information deserves a different priority from a configuration improvement without a realistic path to harm.
This evidence-based approach helps avoid two mistakes: overlooking a meaningful vulnerability because the configuration appears correct, and overstating a potential weakness before demonstrating its consequences.
The question remains consistent throughout his work: which security property failed, and what evidence proves that it failed?
A Security Model Built Around Boundaries and Least Privilege
These experiences inform how Bellingeri approaches security for RAG applications and AI agents.
His central principle is that content is not authority.
A retrieved document may provide useful evidence without being allowed to control the system. The same principle applies to web pages, memory entries, tool outputs, and responses generated by other models.
To maintain this separation, he distinguishes among system instructions, retrieved data, model reasoning or recommendations, and authorized actions. Prompt injection becomes more dangerous when those boundaries collapse and untrusted content can influence decisions beyond its intended role.
He applies least-privilege thinking to agents in much the same way he would to conventional software services.
An agent that needs to read a resource does not automatically need permission to modify it. An agent that can recommend an action does not necessarily need authority to execute it. Higher-impact actions should have stronger policy boundaries and, where appropriate, human approval.
He also prefers architectures in which a single model does not simultaneously act as the sensor, interpreter, executor, and final authority on whether an action was correct.
Instead, he favors a flow in which untrusted input is processed through reasoning, policy and authorization, tool execution, and independent observation.
Security cannot depend solely on instructing a model to avoid dangerous behavior. The surrounding system must enforce the boundaries that matter.
Making security compatible with development speed
Bellingeri does not advocate adding maximum friction to every operation. He sees two unhelpful extremes: treating security as something to add at the end, and imposing so much manual overhead that developers begin working around the controls.
His preference is to move security into architecture, safe defaults, and automation.
Dependency checks, secret scanning, static analysis, workflow security, and build validation can run automatically in continuous integration (CI). This reduces the need for developers to remember and apply every control manually.
The level of control should also reflect the capability and potential impact of an operation. A read-only retrieval task and an agent capable of modifying files or executing commands should not necessarily follow the same approval process.
The objective is to make security proportional to capability, uncertainty, and impact, so that it becomes part of the development workflow rather than an obstacle added from outside.
Production Readiness Means Testing What Actually Ships
Bellingeri's approach to verification extends beyond AI systems. His production website combines security controls, accessibility, automated testing, and performance optimization.
One example involves his use of a strict, hash-based Content Security Policy (CSP) without unsafe-inline. During implementation, syntax highlighting introduced inline styles that were incompatible with the intended policy.
The straightforward solution would have been to weaken the policy. Instead, Bellingeri removed the incompatible behavior and added tests against the built output.
Those CI checks verify the HTML that will actually reach production, including inline scripts, hashes, style attributes, and other relevant properties that may only become apparent after the build process.
The decision reflects a broader engineering principle: verifying source code alone is not always enough. Production behavior depends on the generated output, dependencies, browser behavior, network conditions, unexpected inputs, and operational boundaries.
His website also uses a broader set of engineering practices, including automated testing, dependency checks, secret scanning, static analysis, accessibility validation, performance optimization, supply-chain controls, and observability.
The common thread is meaningful verification. A control that remains green because it never observes the property that matters can create a false sense of security.
Why Public Technical Writing Improves Engineering
Bellingeri initially shared projects because he enjoyed building software and documenting what he learned. Over time, publishing technical work changed how he approached engineering.
When code stays private, it can be easy to assume that an implementation works for the reasons the developer believes it does. Publishing a technical claim introduces a more demanding question: how can that claim be demonstrated?
For Bellingeri, that pressure is useful. Public technical writing and open-source work have encouraged greater attention to assumptions, reproducibility, scope, and evidence.
He also sees value in documenting failures rather than publishing only successful implementations.
His prompt-injection article came from a test that was wrong. He could have corrected the test privately and moved on, but explaining why the protection appeared correct while failing to provide the intended security offered a more useful engineering lesson.
Public work also creates opportunities for peer review. Other developers can challenge assumptions, propose better experiments, and identify failure modes that the original author may have missed.
That feedback can influence how a system evolves.
For Bellingeri, sharing technical findings is therefore not separate from building software. It is another way to examine whether the claims made about that software can withstand scrutiny.
What Comes Next: Identity, Trust, and Agent Security
Bellingeri is particularly interested in the security of increasingly capable AI agents.
As AI systems move beyond generating text to using tools, maintaining memory, calling APIs, modifying files, executing code, and coordinating with other agents, their trust requirements become more complicated.
One question concerns the relationship between an agent's identity and its changing configuration.
If an agent has a durable identity but its model, system prompt, toolset, permissions, or runtime configuration changes, should the trust previously associated with that identity automatically carry over?
Bellingeri does not think it should.
He wants to explore systems in which trust remains scoped to the configuration and conditions under which the supporting evidence was earned. A change in capabilities or runtime conditions should not automatically inherit every previous assurance.
The Model Context Protocol (MCP) is another area of interest. It can make it easier to connect models to capabilities, but each additional capability introduces another trust boundary that needs to be considered.
He is also interested in adversarial testing and observability for agent systems. Ideally, he wants to reconstruct the full path from a claim to a decision, a tool invocation, the actual action, the resulting state, and the evidence that confirms what happened.
The agent's own explanation should not be the only source of truth.
The Engineering Lesson: Confidence Is Not Evidence
Looking back at his projects, Bellingeri finds it difficult to select just one. MonferrinoAI, Agentic-OS, and LLM-Council each represent a different stage of his exploration of AI, cloud systems, and security.
But the lesson he values most comes from LLM-Council and the prompt-injection test that passed while the underlying security property remained broken.
The significance was not simply that he found a weakness. It was that the implementation and its test suite had both appeared correct to the person who designed them.
That experience forced him to challenge his own assumptions and reconsider what a successful test actually proved.
His advice to developers building AI applications is to treat an LLM as one component in a larger system, surrounded by familiar engineering principles: input validation, least privilege, observability, failure handling, testing, and separation of responsibilities.
He also encourages developers to treat externally sourced information as untrusted, avoid giving models more authority than necessary, and test security properties rather than only happy paths.
If a system claims to prevent an attack, deliberately weaken the protection and check whether the tests notice. If an agent is supposed to be unable to access a resource, test the boundary directly.
This approach brings together the ideas behind his projects. A green test does not automatically prove that a system is secure. Multiple models agreeing does not guarantee that an answer is correct. A model reporting that it performed an action does not prove that the action occurred.
The responsibility of an engineer is to understand exactly what a control claims to guarantee, identify the evidence supporting that claim, and verify that the mechanism used to evaluate it can itself detect failure.
That is the thinking behind Bellingeri's personal engineering motto:
“I build it. I break it. I secure it.”
About Marco Bellingeri
Marco Bellingeri is a Cloud & AI Security Engineer based in Italy, working across Python, AWS, DevSecOps, LLM engineering, RAG, AI agents, and cloud infrastructure. His work focuses on building and evaluating systems where AI capabilities meet practical security and operational requirements.
His projects include MonferrinoAI, which explores AI-assisted website knowledge management and content planning; Agentic-OS, which focuses on observability for AI-assisted development; and LLM-Council, which explores multi-model response evaluation and adversarial testing.
He is particularly interested in AI agent security, MCP security, prompt injection, LLM observability, and testing that demonstrates whether security properties actually hold.
Developer Stories is a CoderLegion interview series highlighting the people, ideas, and engineering decisions behind software development.