$ designing-safe-tools-for-security-agents

Designing Safe Tools for Security Agents [Lab Part 3]

A security agent sending a proposed action through a restricted tool interface, scope validation, a hostname allowlist, and a local lab service.

This article is the third in a series. The first article introduced collaborative AI agents for reconnaissance. The second article explored the fictional Helixora Therapeutics and Northstar File Relay environment. This time, we will examine the safeguards that keep those agents inside the learner lab.

Giving an AI model a security objective is easy. Giving it the ability to act safely is much harder.

The obvious shortcut is to start a model, attach a shell, and ask it to investigate a target. That produces an impressive demonstration because the model can invoke almost anything installed on the host. It can run scanners, create scripts, open network connections, modify files, and combine tools in ways the developer did not explicitly anticipate. Those are also the reasons I did not build the Helixora lab that way.

An unrestricted shell does not merely give an agent more flexibility. It collapses several important security boundaries into one decision. The model can choose the command, the destination, the protocol, the request volume, the files it reads, the files it modifies, and the data it sends. A prompt may tell it to remain within scope, but the enforcement mechanism is still the model remembering and correctly interpreting that instruction on every turn.

That is not a boundary I want to depend on.

The Helixora lab instead places a small, explicit tool layer between every specialist agent and the environment it assesses. The model can reason about what to do next, but deterministic code decides whether the action is available, whether the destination is allowed, how the request is formed, and what evidence is recorded.

The model proposes an action. The tool layer defines what that action can actually mean.

Everything in this article refers to the isolated learner lab. Its hosts, identities, credentials, files, and data are fictional. The design is deliberately constrained so that learners can inspect the safety boundaries as easily as they inspect the findings.

Safety Is an Architecture Decision

There are two broad ways to tell an agent what it may do. The first is an instruction:

  • Only investigate the authorized lab systems.
  • Do not run destructive commands.
  • Do not access anything outside the defined scope.

Those are useful instructions. The Helixora agents have them. They establish the expected behavior and help the model reason about its role. The second approach is enforcement:

Safe-Tool Architecture

That is the more important layer.

If the model asks for a tool it was not given, the runtime rejects the call. If it supplies a destination that is not in the hostname allowlist, the request is denied before the HTTP client is invoked. If it wants to enumerate paths, it receives a fixed list rather than control of an arbitrary fuzzer. If it wants to create a remediation ticket, a human approval gate remains between the request and the write. The safety property therefore does not depend entirely on a sentence in a system prompt.

Instructions describe the policy. Code enforces the capability boundary.

This separation is useful even when the model behaves perfectly. It makes the design reviewable. A learner can open one file and see the destinations, tools, methods, and write operations that exist. They do not need to infer the practical authority of an agent from several pages of natural-language instructions.

Start with a Simple Threat Model

The lab is not designed around the idea that the model is malicious. It is designed around the reality that model behavior can be imperfect and application content can be untrustworthy.

Several things can go wrong during an agentic assessment:

  • the model can misunderstand the scope;
  • it can construct an unexpected URL;
  • it can call the wrong tool;
  • it can repeat an action unnecessarily;
  • it can treat page content as an instruction;
  • it can confuse an observation with a conclusion;
  • it can attempt a write before a human has approved it;
  • it can stop before recording the evidence it found;
  • it can make a reasonable-sounding inference that the evidence does not support.

The tool architecture cannot prevent every reasoning error. It can, however, prevent many reasoning errors from becoming unconstrained actions.

That is the threat model behind the lab: assume the model may be mistaken, overconfident, distracted by untrusted content, or simply creative in an unexpected way. Then make sure the available tools still keep its actions narrow, reversible where possible, and observable.

The Hostname Allowlist Is the First Boundary

All agent requests to the lab pass through agents/lab_tools.py. At the top of that file is an explicit mapping between fictional hostnames and local addresses:

ALLOWED_HOSTS = {
    "portal.helixora.lab": "http://localhost:8081",
    "api.helixora.lab": "http://localhost:8082",
    "directory.helixora.lab": "http://localhost:8083",
    "staging.helixora.lab": "http://localhost:8084",
    "files.northstar-relay.lab": "http://localhost:8085",
}

The agents work with the fictional names. The tool layer translates those names to the ports published by Docker Compose.

Fictional service Local destination Purpose
portal.helixora.lab localhost:8081 Helixora public site and employee portal
api.helixora.lab localhost:8082 Helixora program API
directory.helixora.lab localhost:8083 Employee and role directory
staging.helixora.lab localhost:8084 Helixora staging mirror
files.northstar-relay.lab localhost:8085 Northstar file-sharing platform

When an agent calls request_lab_page or query_lab_api, the destination is passed to _resolve. That function removes a recognized HTTP scheme, compares the destination with the approved mappings, and returns the corresponding loopback URL.

If there is no match, the function raises LabPermissionError:

Destination outside authorized lab:

The HTTP request is never sent. The denial is also written to telemetry as a tool_denied event. This means an out-of-scope request is not only blocked; it is visible during later review. The important point is where the check occurs. It is not performed by asking the model whether the URL looks appropriate. It occurs in ordinary Python before requests.get receives a destination.

Hostname-Resolution Boundary

Allowlisting Is Better Than Blocklisting

A blocklist attempts to name destinations the agent must not access. That becomes difficult very quickly. There are public addresses, private ranges, metadata services, alternate IP representations, redirects, DNS changes, IPv6 forms, and hostnames that resolve somewhere unexpected. An allowlist starts from the opposite position:

Nothing is reachable unless it is one of these five lab services.

That is a much smaller problem. The list is short enough for a learner to review. Adding a sixth service requires a code change. Discovering a hostname in page content does not automatically grant access to it. An agent can record the new name as evidence, but the tool layer will not contact that host until the operator deliberately places it within scope.

This distinction protects against a subtle agentic failure mode. Reconnaissance naturally produces new destinations. A page may reference an analytics service, a package repository, an email address, or a URL placed there by untrusted content. If discovery automatically became authority, every new reference could expand the assessment. In the lab, it does not.

Discovery can create a question. It cannot enlarge the scope.

URL Validation Needs Careful Parsing

The current lab resolver is intentionally small and readable. That makes it useful for teaching, but it also creates an opportunity to discuss what production-quality validation would need to add. Strong URL enforcement should:

  • parse the URL rather than rely only on string prefixes;
  • compare the normalized hostname exactly;
  • constrain the permitted scheme;
  • constrain the permitted port;
  • reject embedded credentials and ambiguous authority components;
  • resolve and validate the final IP address;
  • disable redirects or validate every redirect target;
  • protect against alternate loopback and IP representations;
  • repeat validation immediately before connection to reduce resolution races.

For example, an HTTP client normally follows redirects unless told otherwise. Validating only the first URL is insufficient if an allowed application can redirect the client to an unapproved destination. A hardened request wrapper should set allow_redirects=False, inspect the Location header, and pass each redirect through the same resolver before continuing.

Similarly, exact parsing avoids treating a hostname merely beginning with an approved string as equivalent to the approved hostname. The current implementation is appropriate for five controlled local applications that the lab owns. It should not be copied unchanged into a system that accepts arbitrary URLs or assesses untrusted infrastructure. This is an important teaching point:

A safeguard should be evaluated against the environment it actually protects, not against the name we give it.

Enumeration Is Deliberately Bounded

Path discovery is another area where a seemingly harmless capability can become open-ended. A normal web content-discovery tool may accept:

  • any wordlist;
  • recursive enumeration;
  • configurable extensions;
  • multiple request methods;
  • high concurrency;
  • response-size filters;
  • arbitrary headers and payloads;
  • an unlimited collection of target hosts.

The Helixora agents receive none of that.

They receive enumerate_common_paths, which operates on one exact allowlisted hostname and a fixed COMMON_PATHS collection stored in the source code. The list contains a small set of routes relevant to the exercise, including:

  • /robots.txt
  • /sitemap.xml
  • /static/app.js
  • /backup/config.json.bak
  • /admin
  • /login
  • /forgot-password
  • /api/v1/openapi.json
  • /api/v1/employees
  • /debug/config

The function performs a simple GET request for each entry, applies a five-second timeout, records the response, and returns only paths whose status is not 404. It does not:

  • accept an uploaded wordlist;
  • generate permutations;
  • recurse into discovered directories;
  • increase concurrency;
  • scan a network range;
  • change the HTTP method;
  • follow links into a new host as a new scope decision;
  • turn one discovery into unlimited further enumeration.

The maximum path-enumeration work is therefore knowable from the source. With 24 fixed entries and five approved services, one complete pass represents at most 120 path requests, excluding explicit follow-up requests selected by the agents. That does not make the traffic invisible. It makes it bounded, explainable, and repeatable.

def enumerate_common_paths(hostname):
    if hostname not in ALLOWED_HOSTS:
        raise LabPermissionError(...)

    for path in COMMON_PATHS:
        response = requests.get(base + path, timeout=5)
        record_response(response)

This is less capable than a conventional content-discovery utility. That is the point. The objective is to teach how an agent uses evidence, not to maximize how many requests it can generate.

The HTTP Methods Are Restricted Too

The generic page and API tools support reads. They do not provide an arbitrary request method.

request_lab_page performs a GET against an approved web host. query_lab_api performs a GET against an approved API endpoint. The model cannot change either function into a PUT, PATCH, or DELETE operation through tool arguments because no method argument exists in the schema.

The few POST operations are named helpers with fixed destinations and fixed purposes:

  • authenticate_portal exchanges an already discovered employee credential at the Helixora login endpoint;
  • get_api_token exchanges an already discovered client credential at the API token endpoint.

There is no generic post_lab_request tool. That difference matters. A generic POST tool asks the model to decide the endpoint, content type, body, and effect. A named authentication helper fixes the endpoint and request shape in code. The model supplies only the values required for that one operation. The application security specialist can inspect Northstar's password-recovery configuration and open the sample reset form, but it is explicitly instructed not to submit a password change. More importantly, it is not given a general mutation tool that would make such a change convenient.

The safest dangerous operation is often the one that does not exist in the agent's tool set.

Agents Never Receive an Unrestricted Shell

The LabAgent runtime does not expose bash, zsh, subprocess, Python evaluation, or an operating-system command tool to the model. It also does not expose a general filesystem interface. The model cannot use its agent tools to:

  • run Nmap or another installed scanner;
  • execute curl against an arbitrary address;
  • inspect environment variables;
  • read SSH keys or browser profiles;
  • search the host filesystem;
  • install a package;
  • create a new script;
  • start another process;
  • alter Docker configuration;
  • open a listening socket;
  • write an arbitrary local file.

The Python application running the agent naturally executes on the learner's computer and uses the network to call the configured model provider. That does not mean the model receives arbitrary access to the Python process. The model sees only the tool definitions supplied to its completion request. When it returns a tool call, the runtime performs a dictionary lookup:

func = self.tools.get(call["name"])

if func is None:
    result = {"error": f"tool {call['name']} not permitted for this agent"}
else:
    result = func(agent=self.name, **call["input"])

That dictionary is the practical capability set for the agent. If the model invents a tool called run_shell, no matching function exists. If it returns Python code in its text, that code remains text. If a page tells it to invoke a hidden command, there is no hidden command available. This is a much clearer boundary than trying to sanitize every shell string a model might construct.

Every Agent Receives a Different Tool Set

The lab does not create one global toolbox and hand it to every specialist. Each agent is constructed with a separate dictionary of permitted functions.

Agent Can interact with applications? Can write assessment state? Important exclusions
Reconnaissance Yes, through bounded lab requests and authentication helpers Findings No tickets, validations, defender assessments, shell, or arbitrary HTTP
Application Security Specialist Yes, for evidence-led checks Linked AppSec assessments No enumeration tool, API-token helper, tickets, or validation results
Analysis Limited API queries for correlation Correlated findings No page enumeration, login helper, tickets, or validation results
Validation Yes, through the same bounded interfaces Validation results No tickets, AppSec assessments, or defender assessments
Defender No application access Defensive visibility assessments Cannot read offensive findings or interact with targets

The defender is the clearest example. It receives only three tools:

  • summarize_security_events
  • read_security_events
  • write_defender_assessment

It cannot request a Helixora page. It cannot read a reconnaissance finding. It cannot authenticate to Northstar. Its conclusions must come from the audit stream available to a defender. The analysis agent is also intentionally limited. It can query the directory API to verify a role, but it does not receive the broader enumeration surface used by reconnaissance. This follows the principle of least privilege at the agent level:

role responsibility -> minimum required tools -> explicit schemas

Role-to-Tool Matrix

The separation also improves the learning experience. When the report says the defender assessed an activity, the learner knows that conclusion did not come from secretly reading the offensive agent's answer.

Tool Schemas Reduce Ambiguity

The tools sent to the model use small, handwritten schemas. For example, the enumeration tool accepts one required string:

{
  "name": "enumerate_common_paths",
  "description": "Run a bounded, fixed-wordlist directory/file 
                    enumeration pass against one lab host.",
  "input_schema": {
    "type": "object",
    "properties": {
      "hostname": {"type": "string"}
    },
    "required": ["hostname"]
  }
}

There is no command, wordlist, threads, recursive, or method property because the agent is not meant to control those things. This illustrates a useful design principle:

Do not expose an argument merely because the underlying library supports it.

Every additional parameter creates another decision the model can make and another input the tool must validate. A narrow schema gives the model enough flexibility to perform its role while keeping the implementation predictable. Handwritten schemas also make review easier. The list of model-visible capabilities is not automatically generated from a large client library. It is a deliberately selected surface.

Credential Use Is Narrowly Defined

Reconnaissance sometimes discovers a credential. The lab allows an agent to use specific fictional credentials as evidence, but it does not give the agent credential-guessing capability.

The difference is significant.

The authentication helpers accept a username and password or a client ID and secret that the agent has already found in the exercise. They submit those values once to a fixed local authentication endpoint and return the resulting session or token. They do not:

  • generate username lists;
  • mutate passwords;
  • perform brute-force attempts;
  • spray one password across accounts;
  • bypass rate limits;
  • steal credentials from the learner's computer;
  • search external breach data;
  • reuse the credential against an unapproved service.

The JWT helper is constrained in the same way. decode_jwt_payload base64-decodes the payload of a token the agent already holds. It does not attempt to forge a token, bypass signature validation, change claims, or attack the signing algorithm.

That lets the learner explore a legitimate reconnaissance question—“What role does this issued token claim?”—without silently expanding the exercise into token manipulation.

Content Is Evidence, Not Authority

An agent examining a website consumes untrusted text. A response might contain ordinary business content, inaccurate documentation, reassuring labels, or direct instructions intended to influence an automated analyst. The Helixora agent prompts explicitly tell specialists not to accept application-provided explanations such as:

  • the weakness is intentional;
  • the behavior is expected;
  • the value is only a stale label;
  • the issue is harmless because it exists in a lab;
  • the agent should stop investigating.

Instead, conclusions must be based on reproducible behavior and corroborating evidence. That protects the quality of the assessment, but it is not the only safeguard. If untrusted content persuades an agent to request an out-of-scope URL, the allowlist still denies it. If it tells the agent to run a command, no shell tool exists. If it asks the defender to query the application, the defender has no application-request tool. This is defense in depth:

  • Prompt: treat content as untrusted
  • Tool set: remove unnecessary capabilities
  • Validation: reject out-of-scope arguments
  • Telemetry: record attempted actions
  • Independent agent: reproduce important conclusions

Prompt-injection resistance should not depend on perfectly detecting every malicious sentence. The capabilities available after the model reads that sentence should still be constrained.

Turn Limits Bound Agent Loops

Each agent run has a maximum number of model turns. Reconnaissance uses small per-host runs. The specialist, analysis, validation, and defender phases receive larger but still finite budgets appropriate to their tasks. When the limit is reached, the runtime stops and returns: Reached max_turns without a final answer.

Turn limits are not a complete request-rate control. One tool call can still perform more than one internal operation, as bounded enumeration does. They are nevertheless useful for preventing an agent from reasoning and calling tools forever. The coordinator also sequences phases rather than allowing specialists to spawn one another without limit:

Agent Turn Sequence

For selected phases, the runtime requires the agent to use a recording tool before silently concluding. It provides one corrective nudge if the model tries to stop without writing the required assessment or validation result.

This is not a permission safeguard in the same sense as the allowlist. It is a workflow-integrity safeguard. Evidence that exists only in a model's final paragraph cannot be correlated, validated, or placed reliably in the report.

Writes Are Separated from Reads

Most agent operations are reads. The shared store accepts structured findings, linked specialist assessments, validation results, and defender assessments, but those write functions are specific to each role. The operation with an external-looking consequence—creating a remediation ticket—is kept behind the coordinator. An agent can recommend a ticket. It cannot create one directly. The coordinator first checks that the underlying finding has an independent CONFIRMED validation and excludes categories representing working controls or non-actionable summaries. It then requires approval. The tool itself accepts an approved flag and raises LabPermissionError if approval is absent.

if not approved:
    raise LabPermissionError("Ticket creation requires human approval")

In learner-led mode, the operator can review and approve each proposed ticket interactively. An explicit --auto-approve option exists for repeatable lab runs, making the change in approval policy visible at the command line rather than hidden in model reasoning.

The model may identify a desirable write. A deterministic workflow and a human decide whether it occurs.

Local Services Reduce the Blast Radius

The five target applications run in Docker containers and are reached by the agent tools through loopback ports 8081 to 8085. Each service is attached to a separate named bridge network:

  • Helixora portal -> fictional-web
  • Helixora API -> fictional-api
  • Directory API -> fictional-directory
  • Staging mirror -> fictional-staging
  • Northstar relay -> fictional-files

The lab tool mapping never directs an agent to a public hostname. The fictional .lab names are labels used by the assessment; the request wrapper translates them to localhost. There is an important Docker detail here. The current Compose file uses short-form mappings such as:

ports:
  - "8081:8080"

Docker commonly publishes that port on all host interfaces, not only 127.0.0.1. The agent still reaches it through loopback, but that mapping alone should not be described as a host-level localhost-only guarantee. For strict loopback-only publication, bind the host address explicitly:

services:
  portal:
    ports:
      - "127.0.0.1:8081:8080"

  api:
    ports:
      - "127.0.0.1:8082:8080"

  directory:
    ports:
      - "127.0.0.1:8083:8080"

  staging:
    ports:
      - "127.0.0.1:8084:8080"

  files:
    ports:
      - "127.0.0.1:8085:8080"

The Compose networks are also currently configured with internal: false. Setting them to internal: true provides a stronger container-network boundary where compatible with the model-provider and host communication design.

These are different safeguards and should not be confused:

Boundary What it controls
Agent hostname allowlist Which destinations the model can reach through its tools
Loopback port binding Which host interfaces can accept connections to the containers
Internal Docker network Whether containers have external network connectivity through that network
Host firewall Which other devices can reach published host ports

For the learner lab, explicit loopback bindings are the clearest default. They make the infrastructure match the conceptual boundary already enforced by the agent tools.

The Model Provider Is Outside the Target Path

The lab supports multiple model providers. A learner can use Anthropic, OpenAI, or Google through the provider adapter. Changing the model provider does not change an agent's tool dictionary. The provider receives the conversation and the same restricted tool schemas. It can request one of those tools, but it cannot grant itself another function. There is still an outbound connection from the host process to the selected model API. That connection should be understood separately from target access:

  • Agent runtime -> configured model provider
  • Agent tool -> allowlisted local lab service

The first supports model inference. The second performs assessment actions. The model does not receive a generic network client merely because the runtime can call its API.

All data sent to a hosted model should still be treated according to the provider and account's data-handling requirements. The Helixora environment uses fictional content specifically so that the learner exercise does not require real credentials, real personal information, or production telemetry to enter model context.

Every Action Leaves Evidence

The tool layer records telemetry for calls, denials, authentication outcomes, findings, validations, and ticket decisions. HTTP responses also produce normalized security events containing details such as:

  • source service;
  • method;
  • path;
  • status code;
  • actor where known;
  • whether the request was authenticated;
  • normalized signal;
  • visibility level;
  • whether an alert was generated;
  • whether the application allowed or blocked the action.

This provides two useful records.

  • The first is agent telemetry, which explains what the orchestration system attempted to do.
  • The second is the defensive audit stream, which represents what the assessed environment exposed to the defender.
  • The defender receives only the second stream. That separation makes it possible to compare action with visibility without giving the defender the answer in advance.
  • Telemetry does not prevent an unsafe action by itself. It provides accountability, supports debugging, and helps learners understand why the system moved from one step to the next.

A controlled agent should be bounded before the action and observable after it.

Independent Validation Limits Reasoning Risk

The validation agent independently repeats security-relevant checks before the coordinator treats them as confirmed. It does not simply read an analysis conclusion and agree with it. Its prompt requires it to use its own bounded tools, reproduce the request, and record whether the outcome is CONFIRMED, REJECTED, or INCONCLUSIVE. That reduces the impact of several model failures:

  • an observation recorded with excessive confidence;
  • a misunderstood response;
  • an incorrect relationship between identities;
  • a claim based only on application wording;
  • a finding that cannot be reproduced;
  • confusion between a successful control and a vulnerability.

Independent validation is not a substitute for access control. It is a safeguard around conclusions and downstream actions. A finding that has not survived validation does not automatically become a remediation ticket.

What the Safeguards Do Not Promise

It is tempting to describe a bounded tool layer as a complete sandbox. It is not. The current lab provides application-level capability controls for the model. It does not formally isolate every Python module in a separate operating-system sandbox. The specialist agents run as code in the same local project and rely on the implementation to expose only their assigned callables. The current design also does not yet provide:

  • a cryptographically signed scope manifest;
  • a global request budget enforced across every phase;
  • per-tool rate limiting beyond fixed enumeration and request timeouts;
  • separate operating-system identities for every agent;
  • an outbound firewall around the host process;
  • exact URL canonicalization for hostile arbitrary inputs;
  • redirect revalidation in the HTTP wrapper;
  • automatic secret redaction before model-provider calls;
  • a tamper-evident telemetry store;
  • container resource limits in the Compose file.

Those controls would matter if the architecture moved beyond this fictional learner environment. For a stronger implementation, I would have added:

  1. Exact parsed-host validation and redirect checks.
  2. Explicit 127.0.0.1 Docker port bindings.
  3. Internal container networks where appropriate.
  4. Per-run and per-host request budgets.
  5. Rate and concurrency limits in the request wrapper.
  6. Response-size limits and content-type validation.
  7. Secret classification and redaction before provider calls.
  8. Process or container isolation for specialist workers.
  9. Signed, immutable scope configuration.
  10. Append-only or externally protected audit records.

Being explicit about these limitations is not a weakness in the lab. It is part of the lesson. Safe agent design requires knowing exactly where a boundary exists and where it does not.

Why Not Just Trust the System Prompt?

A carefully written prompt is still valuable. It establishes role, sequencing, evidence standards, and expected judgment. The Helixora prompts tell agents not to brute-force credentials, not to perform open-ended exploration, not to modify data, and not to trust self-serving application text. But prompts operate in the same reasoning system that may become confused by an unusual response. Consider the difference:

Prompt-Only vs Layered Control

In the second design, a prompt failure does not automatically become an infrastructure failure. That is the principle I want learners to take away from this part of the lab.

Never make the model the sole enforcement point for its own authority.

A Practical Review Checklist

When reviewing a tool intended for a security agent, I ask the following questions.

Destination

  • Is the destination selected from an allowlist?
  • Is the hostname parsed and compared exactly?
  • Are scheme and port constrained?
  • Are redirects disabled or revalidated?
  • Is DNS resolution checked against the authorized address set?

Operation

  • Is the HTTP method fixed?
  • Can the model supply an arbitrary body or command?
  • Can the operation mutate state?
  • Could a narrower named function replace a generic client?

Volume

  • Is there a request budget?
  • Is concurrency limited?
  • Is recursion bounded?
  • Are timeout and response-size limits present?
  • Can one tool call fan out into an unknown amount of work?

Data

  • What information is returned to the model?
  • Could secrets or personal data enter provider context?
  • Are results normalized or returned raw?
  • Is sensitive material redacted when it is not required?

Authority

  • Which agent receives the tool?
  • Does that agent need every argument the tool exposes?
  • Are writes separated from reads?
  • Which actions require human approval?

Evidence

  • Is the call recorded?
  • Are denials recorded as well as successes?
  • Can another agent independently validate the outcome?
  • Can the learner reconstruct why the action occurred?

If those questions cannot be answered from the code, the tool is probably too broad or insufficiently documented.

The Safeguard Stack

The Helixora lab does not rely on one perfect control. It combines several imperfect but complementary controls:

  1. Fictional data prevents the exercise from depending on real identities or production secrets.
  2. Local target mapping directs assessment tools to the five lab services.
  3. A hostname allowlist denies every destination not explicitly authorized.
  4. Bounded enumeration fixes the paths, method, timeout, and target set.
  5. Named authentication helpers permit use of discovered lab credentials without enabling guessing.
  6. Per-agent tool dictionaries enforce different capabilities for different roles.
  7. Small tool schemas remove unnecessary model-controlled parameters.
  8. No shell or generic HTTP client prevents arbitrary command and network construction.
  9. Turn limits and coordinator sequencing bound workflow execution.
  10. Human approval for tickets separates recommendations from writes.
  11. Telemetry and security events record actions and denials.
  12. Independent validation checks important conclusions before downstream action.
  13. A separate defender view evaluates what the target environment could actually observe.

Thirteen layers surrounding a security agent, from fictional data and local services through tool restrictions, human approval, telemetry, validation, and defender review.

None of these controls should be used as an excuse to weaken another. A local service still needs an allowlist. An allowlisted tool still needs a bounded method. A restricted read still needs telemetry. A recorded finding still needs validation. A proposed write still needs approval.

That is what defense in depth looks like for an agent workflow.

Where We Go Next

We now have three important pieces of the experiment.

  • The first is the idea of specialist agents collaborating during reconnaissance.
  • The second is the fictional Helixora and Northstar environment they investigate.
  • The third is the tool boundary that determines what those agents are actually allowed to do.

In the next article, we will examine the shared evidence model and coordinator workflow. We will follow a discovery as it becomes a finding, an application-security assessment, a correlation, an independent validation result, a defensive visibility assessment, and finally a section in the HTML report. That journey is where agent orchestration becomes visible.

The model may decide which approved question to ask next, but it does not define its own authority. The scope, tools, operations, limits, writes, and evidence requirements remain explicit in code.

Safe security agents are not models that have been asked to behave carefully. They are systems in which careful behavior is supported—and unsafe behavior is constrained—by architecture.

That is the boundary the Helixora lab is designed to expose for analysis and validation.