AI agents are moving from simple chat interfaces to systems that can execute code, call APIs, process files, browse information, and perform multi-step tasks.
That flexibility creates a security problem.
A normal application usually controls exactly what code runs and which resources it can access. An AI agent is different because its behavior can be influenced by model output, external content, user instructions, and dynamically selected tools.
If an agent can execute arbitrary code inside the same environment as the main application, a small mistake can become a serious security incident.
A safer architecture separates the agent's execution environment from the host application.
This article explains how to design a secure hosted AI agent architecture using isolated runtime environments, with practical .NET examples and production-oriented security considerations.
Introduction
Consider an AI coding agent that receives this request:
Analyze the uploaded project,
run the tests,
fix failing tests,
and generate a report.
The agent may need to:
Read project files.
Inspect source code.
Execute commands.
Install dependencies.
Run tests.
Generate output files.
Return a summary.
Giving the agent unrestricted access to the application server is dangerous.
Instead, use an architecture such as:
User
|
v
Agent Application
|
Tool / Task Planner
|
v
Runtime Controller
|
+-----------+-----------+
| |
v v
Isolated Runtime A Isolated Runtime B
| |
Files Processes
Network Commands
Packages Temporary Data
| |
+-----------+-----------+
|
v
Sanitized Results
The central idea is simple:
The agent should not automatically share the trust boundary of the host application.
Why Agent Isolation Matters
Traditional applications usually execute predetermined code paths.
An agent can generate or select actions dynamically.
For example:
User
|
v
LLM
|
+--> Read file
|
+--> Execute command
|
+--> Call API
|
+--> Create file
|
+--> Run program
Every additional capability increases the attack surface.
Potential risks include:
Isolation does not eliminate these risks, but it provides an additional security boundary.
What Is an Isolated Runtime?
An isolated runtime is a controlled execution environment where agent-generated operations can run without having unrestricted access to the host system.
Depending on the security requirements, this could be based on:
The important characteristic is not the technology name.
It is the security boundary around execution.
Separate Control Plane and Execution Plane
A production architecture should separate agent orchestration from code execution.
Control Plane
-------------------------
Agent API
Authentication
Authorization
Task Management
Policy Engine
Audit Logs
Runtime Controller
|
v
Execution Plane
-------------------------
Isolated Runtime
Temporary Files
Processes
Network
Tools
The control plane decides what is allowed.
The execution plane performs what has been authorized.
This separation makes security policies easier to enforce.
A Basic .NET Architecture
A .NET application can represent an agent task with a simple model.
public sealed record AgentTask(
string TaskId,
string TenantId,
string Instructions,
IReadOnlyList<string> AllowedTools);
The application should not send arbitrary instructions directly to a runtime.
Instead, first validate the task.
public interface IAgentPolicy
{
Task<PolicyDecision> EvaluateAsync(
AgentTask task,
CancellationToken cancellationToken);
}
The policy layer can decide whether execution is permitted.
public sealed record PolicyDecision(
bool Allowed,
string? Reason);
Then:
var decision = await policy.EvaluateAsync(
task,
cancellationToken);
if (!decision.Allowed)
{
throw new InvalidOperationException(
decision.Reason ?? "Execution denied.");
}
This creates a clear authorization boundary before runtime execution.
Never Trust Model Output
One of the most important principles for agent security is:
LLM output is data, not authorization.
Suppose the model generates:
{
"tool": "execute_command",
"command": "dotnet test"
}
The application should not assume that because the model selected the tool, execution is allowed.
Instead:
LLM Decision
|
v
Policy Validation
|
v
Tool Authorization
|
v
Runtime Restrictions
|
v
Execution
The model can request an operation.
The application decides whether the operation is permitted.
Define Explicit Tool Permissions
Avoid giving every agent access to every tool.
For example:
public enum AgentTool
{
ReadFile,
WriteFile,
RunTests,
ExecuteProcess,
HttpRequest
}
A policy can define which tools are available.
var allowedTools = new HashSet<AgentTool>
{
AgentTool.ReadFile,
AgentTool.RunTests
};
If the agent requests:
ExecuteProcess
the policy layer can reject it.
This is more secure than relying on prompt instructions such as:
Do not execute dangerous commands.
Prompts are not security boundaries.
Runtime Resource Limits
An isolated environment should have explicit resource limits.
At minimum, consider:
CPU
Memory
Disk
Process Count
Execution Time
Network Connections
File Size
Output Size
For example:
Maximum execution time: 2 minutes
Maximum memory: controlled limit
Maximum workspace size: controlled limit
Maximum output: controlled limit
The exact values should be based on the workload rather than arbitrary defaults.
Resource limits protect both security and reliability.
Execution Timeout
Every agent operation should have a timeout.
using var timeout =
new CancellationTokenSource(
TimeSpan.FromMinutes(2));
await runtime.ExecuteAsync(
task,
timeout.Token);
Without a timeout, an agent could accidentally or intentionally start a process that never terminates.
Examples include:
Infinite loops
Long-running builds
Waiting processes
Interactive commands
Recursive scripts
Timeouts provide an important containment mechanism.
Filesystem Isolation
The agent should not automatically access the host filesystem.
Instead, provide a dedicated workspace:
Runtime
|
+-- /workspace
| +-- source
| +-- output
| +-- temp
|
+-- /runtime
|
+-- restricted system files
The workspace should be temporary whenever possible.
For example:
var workspace =
Path.Combine(
Path.GetTempPath(),
$"agent-{task.TaskId}");
Directory.CreateDirectory(workspace);
After execution:
Directory.Delete(
workspace,
recursive: true);
Production systems should also handle cleanup when execution fails.
Path Traversal Protection
File tools need additional protection.
An agent might attempt to access:
../../secrets.txt
or:
/etc/passwd
The application should resolve the path and verify that it remains inside the allowed workspace.
var fullPath =
Path.GetFullPath(
Path.Combine(workspace, requestedPath));
var root =
Path.GetFullPath(workspace);
if (!fullPath.StartsWith(
root,
StringComparison.OrdinalIgnoreCase))
{
throw new UnauthorizedAccessException(
"Path is outside the workspace.");
}
The exact implementation should also account for platform-specific path behavior and symbolic links.
Filesystem authorization should happen before opening the file.
Network Isolation
Network access is one of the most frequently overlooked agent risks.
Imagine an agent can execute:
curl https://internal-service
or connect to a private database.
A compromised agent could potentially use that capability for data exfiltration or internal service discovery.
Use an explicit network policy.
For example:
Internet
|
X
Agent Runtime
Approved API
|
v
Controlled Gateway
Rather than giving the runtime unrestricted outbound access, route approved network operations through controlled services.
Allowlisting External Services
If an agent needs an API, define exactly which destinations are allowed.
Conceptually:
var allowedHosts = new HashSet<string>(
StringComparer.OrdinalIgnoreCase)
{
"api.example.internal"
};
Before making a request:
if (!allowedHosts.Contains(
requestUri.Host))
{
throw new SecurityException(
"Destination is not allowed.");
}
Production implementations should validate DNS resolution, redirects, IP ranges, and other SSRF-related conditions rather than relying only on a hostname string.
Protect Credentials
Never place production secrets inside the agent workspace.
Avoid:
.env
appsettings.json
credentials.json
private keys
database passwords
cloud credentials
The agent should receive only the minimum credential required for the specific operation.
A stronger architecture is:
Agent
|
v
Tool Request
|
v
Credential-Aware Gateway
|
v
External Service
The runtime never receives the long-lived credential directly.
Temporary Credentials
When credentials are unavoidable, prefer short-lived credentials with restricted permissions.
For example:
Credential
|
+-- Limited scope
+-- Limited lifetime
+-- Limited resource access
This reduces the impact if the runtime is compromised.
The principle is the same as least privilege:
Give the agent the smallest capability necessary to complete the task.
Multi-Tenant Isolation
Enterprise agent systems frequently serve multiple customers.
A dangerous architecture looks like:
Tenant A
Tenant B
Tenant C
|
v
Shared Runtime
A stronger design associates every execution with a tenant identity.
public sealed record RuntimeRequest(
string TenantId,
string TaskId,
string WorkspaceId);
The runtime controller should validate that the workspace belongs to the requested tenant.
Do not rely only on a value supplied by the model.
Tenant identity should originate from authenticated application context.
Prevent Cross-Tenant File Access
Suppose:
Tenant A -> workspace-a
Tenant B -> workspace-b
The runtime for Tenant A should never receive access to:
workspace-b
Use separate workspace boundaries and enforce authorization at the runtime controller.
This should be tested explicitly.
Container Isolation Is Not the Entire Security Model
Containers can provide useful isolation, but a container should not automatically be treated as an unrestricted security boundary.
A production design should combine multiple controls:
Authentication
+
Authorization
+
Tool Restrictions
+
Filesystem Isolation
+
Network Restrictions
+
Resource Limits
+
Runtime Isolation
+
Monitoring
Security should be layered.
Command Execution Policy
If the agent can execute commands, use an allowlist where possible.
For example:
var allowedCommands =
new HashSet<string>(
StringComparer.OrdinalIgnoreCase)
{
"dotnet",
"git"
};
However, validating only the executable name is not enough.
For example:
dotnet test
and:
dotnet tool install ...
have very different security implications.
Define command-level policies.
dotnet test
dotnet build
may be allowed, while:
dotnet tool install
could require additional authorization.
Package Installation Risk
Agent-generated environments often need dependencies.
That creates another attack surface.
A package installation can introduce:
Where possible, use prebuilt runtime images containing approved dependencies.
Base Runtime Image
|
+-- .NET SDK
+-- Approved tools
+-- Approved libraries
|
v
Agent Workspace
This is generally easier to control than allowing unrestricted package installation.
Output Sanitization
Agent output should be treated as untrusted data too.
Suppose the runtime produces:
<script>...</script>
or a generated file contains unexpected content.
The host application should validate and sanitize content before displaying it or passing it to another system.
This is particularly important when agent output is rendered as HTML or stored for later processing.
Audit Logging
Every sensitive operation should be auditable.
Capture information such as:
Task ID
Tenant ID
Agent ID
Tool
Timestamp
Runtime ID
Requested Operation
Policy Decision
Execution Result
Duration
Exit Code
Resource Usage
Example:
logger.LogInformation(
"Agent task {TaskId} executed tool {Tool} " +
"for tenant {TenantId}",
task.TaskId,
tool,
task.TenantId);
Avoid logging secrets, tokens, credentials, or sensitive document contents.
Trace the Complete Agent Execution
Distributed tracing can connect:
User Request
|
v
Agent Run
|
v
Tool Call
|
v
Runtime
|
v
External Service
A trace identifier allows engineers to understand where time was spent and which operation occurred before a failure.
This is valuable for both performance analysis and security investigations.
Failure Handling
Runtime execution should be treated as an untrusted operation that can fail.
try
{
return await runtime.ExecuteAsync(
request,
cancellationToken);
}
catch (OperationCanceledException)
{
logger.LogWarning(
"Agent task {TaskId} timed out.",
request.TaskId);
throw;
}
catch (Exception ex)
{
logger.LogError(
ex,
"Agent task {TaskId} failed.",
request.TaskId);
throw;
}
Do not expose internal exception details directly to users.
For example, avoid returning:
Connection string:
Server=...
Password=...
as part of an agent error message.
Secure Runtime Lifecycle
A useful lifecycle is:
Create
|
v
Configure
|
v
Validate
|
v
Execute
|
v
Collect Results
|
v
Sanitize
|
v
Destroy
The runtime should ideally be disposable.
Persistent execution environments increase the risk of:
For sensitive workloads, ephemeral execution environments provide stronger isolation.
Security Testing
Security testing should include malicious agent scenarios.
For example:
Attempt to read host files
Attempt path traversal
Attempt network access
Attempt credential access
Attempt resource exhaustion
Attempt cross-tenant access
Attempt unauthorized tool execution
Attempt package installation
Attempt persistent file creation
A test should verify both:
Expected behavior
+
Security control enforcement
For example:
[Fact]
public async Task Cannot_Read_File_Outside_Workspace()
{
await Assert.ThrowsAsync<UnauthorizedAccessException>(
() => fileTool.ReadAsync(
"../../secret.txt"));
}
Security controls should be tested as application behavior rather than assumed to work.
Common Mistakes
Giving the Agent Host Access
The agent should not run with the same filesystem and permissions as the host application.
Treating Prompts as Security Controls
A prompt saying "do not access secrets" does not enforce anything technically.
Allowing Unrestricted Network Access
Outbound network access can become an exfiltration path.
Sharing Workspaces
Temporary execution environments should not reuse untrusted state between tenants or unrelated tasks.
Ignoring Resource Limits
Without CPU, memory, disk, and time limits, agents can consume excessive resources.
Logging Sensitive Data
Audit logs should help investigations without becoming another source of credential leakage.
Trusting Tool Arguments
Every tool request should be validated by application-side policy.
Advantages
Stronger Containment
Runtime isolation limits the impact of unsafe code or unexpected agent behavior.
Better Multi-Tenant Separation
Separate execution environments make tenant boundaries easier to enforce.
Controlled Resource Usage
Resource limits prevent a single task from consuming unlimited infrastructure.
Easier Cleanup
Ephemeral runtimes can be destroyed after task completion.
Improved Auditability
Runtime creation, tool execution, and policy decisions can be logged independently.
Disadvantages
Additional Infrastructure
Isolated runtimes require orchestration and lifecycle management.
Startup Overhead
Creating a new runtime can add latency compared with executing directly inside the application process.
More Complex Networking
Controlled network access requires additional configuration.
Operational Cost
Dedicated execution environments consume additional infrastructure resources.
More Security Configuration
Isolation is valuable only when filesystem, network, identity, and resource controls are configured correctly.
Recommended Production Architecture
A practical secure architecture can look like this:
User
|
v
Authentication
|
v
Agent API
|
v
Policy / Authorization
|
v
Agent Orchestrator
|
v
Runtime Controller
|
+------------+------------+
| |
v v
Ephemeral Runtime A Ephemeral Runtime B
| |
Tool Execution Tool Execution
File Workspace File Workspace
Resource Limits Resource Limits
| |
+------------+------------+
|
v
Result Sanitizer
|
v
Agent API
|
v
User
This architecture provides multiple independent control points.
The LLM determines what it would like to do.
The application determines what it is allowed to do.
The isolated runtime determines where the operation can execute.
Best Practices
For production hosted AI agents:
Treat model output as untrusted input.
Keep orchestration and execution in separate trust boundaries.
Use ephemeral runtimes for sensitive tasks.
Restrict filesystem access to a dedicated workspace.
Apply explicit CPU, memory, disk, and timeout limits.
Restrict outbound network access.
Use allowlisted tools and commands.
Never expose production credentials directly to the runtime.
Prefer short-lived, least-privileged credentials.
Enforce tenant isolation.
Sanitize runtime output before displaying or storing it.
Record security-relevant operations in audit logs.
Test malicious execution scenarios.
Destroy temporary runtime state after completion.
Monitor both security and operational metrics.
Conclusion
Hosted AI agents require a different security model from traditional application workloads because their behavior can be dynamically influenced by model output, external content, and tool selection.
The safest approach is not to assume that the model will behave correctly. Instead, design the surrounding system so that unsafe behavior is contained.
An isolated runtime provides an important execution boundary, but it should be combined with authorization, tool restrictions, filesystem isolation, network controls, resource limits, credential protection, tenant separation, auditing, and security testing.
The key architectural principle is straightforward:
Let the agent decide what it wants to do, but never let the agent decide what it is allowed to do.
That decision belongs to the application and its security policies.