AI Agents  

Building Secure Hosted AI Agents with Isolated Runtime Environments

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:

  1. Read project files.

  2. Inspect source code.

  3. Execute commands.

  4. Install dependencies.

  5. Run tests.

  6. Generate output files.

  7. 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:

  • Arbitrary command execution

  • Malicious file access

  • Credential theft

  • Network abuse

  • Resource exhaustion

  • Dependency attacks

  • Prompt injection

  • Data exfiltration

  • Cross-tenant access

  • Persistent malicious artifacts

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:

  • Containers

  • Virtual machines

  • MicroVMs

  • Sandboxed runtimes

  • Dedicated worker processes

  • Restricted operating-system environments

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:

  • Malicious dependencies

  • Unexpected network access

  • Arbitrary installation scripts

  • Large downloads

  • Resource consumption

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:

  • Malware persistence

  • Workspace contamination

  • Cross-task state leakage

  • Credential leftovers

  • Unexpected configuration changes

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:

  1. Treat model output as untrusted input.

  2. Keep orchestration and execution in separate trust boundaries.

  3. Use ephemeral runtimes for sensitive tasks.

  4. Restrict filesystem access to a dedicated workspace.

  5. Apply explicit CPU, memory, disk, and timeout limits.

  6. Restrict outbound network access.

  7. Use allowlisted tools and commands.

  8. Never expose production credentials directly to the runtime.

  9. Prefer short-lived, least-privileged credentials.

  10. Enforce tenant isolation.

  11. Sanitize runtime output before displaying or storing it.

  12. Record security-relevant operations in audit logs.

  13. Test malicious execution scenarios.

  14. Destroy temporary runtime state after completion.

  15. 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.