Internal agents earn trust quickly. Their users are colleagues, the data stays inside the company, and the first weeks go well. Then the agent gains a tool that reads the ledger, and the situation changes:

  • A teammate outside finance asks it a question.
  • Someone pastes in a supplier email that contains instructions of its own.
  • A scheduled task starts running at six in the morning with nobody watching.

The usual first defense is a sentence in the system prompt: "Only share ledger data with the finance team."

That sentence is a request. It is not a control.

This article explains why, and what a control looks like instead. The short version: whether a person may use a tool should be decided in code, outside the model, at the moment the tool is called, and every decision should leave a record.

Five layers at a glance

No single control is enough, so layer them. Here are the five layers this article covers, and how Hubzoid handles each one.

LayerThe question it answersHow Hubzoid handles it
ScopeWhat can the agent touch at all?Give each Hub and helper agent only the tools its work needs
GrantsWho may use this sensitive tool?A named permission, checked in code when the tool is called
ChannelDoes this channel prove who is asking?Sensitive tools only where each request carries one verified identity
ReviewWho decides before it matters?Designed into the work, because 1.0.1 has no approval step
RecordWhat happened, and why?Every allow and deny decision recorded, or the call does not run

A prompt is a request, not a lock

A system prompt sits in the same stream of text as everything else the model reads:

  • the person's message
  • a forwarded email
  • a document from a shared drive
  • an alert from another system

If any of those contains instructions, the model has to referee between them, and it will not do so reliably every time. A prompt also leaves nothing behind. When an agent does something it should not, the prompt cannot tell you who asked, what was attempted or why it was allowed. Behavior you only requested is behavior you do not control.

Controls that hold run outside the model and write down what they decided. Here is what happens in Hubzoid when an agent asks to use a sensitive tool:

  1. Allowed channel?
  2. Granted person?
  3. Decision recorded
  4. Tool runs
A sensitive tool call in Hubzoid. If any check fails, the call is refused.

The safest tool is one the agent does not have

The strongest guardrail is a capability the agent never had. An agent that can read an order's status but has no tool to change it cannot be talked into changing it.

In Hubzoid, scope starts with the Hub itself, the shared set of instructions, knowledge and tools a team's agent works from. Give a team its own Hub with only the tools its work needs, rather than one agent that can reach everything. Within a Hub, you can narrow further:

  • A helper agent can be limited to a named list of tools.
  • The built-in web tools can be limited to a list of known sites, or removed entirely, which closes an easy route for data to leave.
  • A scheduled task can change only the files it declared in advance.
  • In a scheduled run, the agent can pull the latest source files but cannot record its changes in the Hub's version history or publish them. Hubzoid does that step itself, and only for the declared files.

Sensitive tools run only for people who were granted them

Some tools should exist but be limited to certain people: the ledger lookup, the payroll report, the customer credit check. In Hubzoid you place such a tool in a separate, restricted part of the Hub, and it gets its own named permission. An administrator grants that permission to named people in the Console, reviews the change and saves it.

The check happens at the moment the tool is called. It runs in Hubzoid's own code, before the tool does anything, and it looks only at who is asking and what they were granted. What the prompt says, or what a pasted email says, does not enter into it. A person without the grant is refused, however the request was worded.

The check at call time is the wall. It holds even when injected text names the tool directly.

On the default runtime, a tool the person has not been granted is also hidden from the model, so it is never offered. Hiding is the tidy experience. Two more details keep the wall intact:

  • Credentials stay out of reach. The passwords and keys these tools use are stored where the agent's own file reading cannot go. The model sees what the tool returns, never the credential.
  • Unattended work has its own identity. A scheduled task or workflow acts as itself, not as whoever set it up. Grant it only the permissions its job needs, as you would a person. Without a grant, its calls are refused and the refusal is recorded.

A grant answers whether someone may use the ledger tool at all. Which rows the tool returns is decided by the tool and the system behind it, so filter there too.

The channel matters as much as the person

The same person can reach an agent from different places, and not every place proves who they are. A Slack channel thread mixes several authors into one conversation, and the answer runs under whoever mentioned the agent. Allowing sensitive tools there would let one person's permission act on another person's request.

So Hubzoid checks the channel before it checks the grant. By default, sensitive tools can be reached only from channels where each request carries one verified identity:

  • the web chat
  • the API
  • assistants connected with a person's own key
  • scheduled work running as itself

Slack, WhatsApp and Telegram are not on the list. An administrator can add Slack direct messages, which have a single author, after turning on the setting that matches each Slack user to a verified email. Slack channel threads should never be added.

Let the agent prepare and a person decide

Hubzoid 1.0.1 does not pause a tool call and wait for someone to approve it. So where a decision deserves a person, design the work so the agent prepares and a person acts:

  • Have scheduled runs write drafts and reports for someone to act on, rather than calling a tool that changes a system of record.
  • While a scheduled task is new, let its edits wait for a person to read before they are saved into the Hub's history.
  • Keep written checks for the rules that matter. A check can fail if the agent calls a tool it should not have called, and the checks can be run before every change so a failure holds that change back.
  • Change access in the Console, which shows every edit for review before it is saved and records it afterward.

Review is a team practice as much as a feature. Decide in advance which outputs a person signs off, and write that into the task.

Every decision leaves a record

Every allow and deny decision on a sensitive tool is written down where it is made. Each record holds:

  • the time and the Hub
  • the person and the channel
  • the tool, the decision and the reason

If the record cannot be written, the call does not run. So every sensitive call that ran has a record, and when something goes wrong you can answer who asked, what was attempted and why it was allowed or refused. A prompt could never tell you that.

The Console's Activity page shows these tool decisions alongside access changes: who granted what, to whom and when. Each scheduled run keeps a history of what it did, and pausing, resuming or cancelling scheduled work is recorded with the name of the person who did it.

What the prompt is still for

None of this makes the system prompt useless. It is where you describe the job, the format and the judgment you expect.

The prompt shapes behavior inside the boundaries. It should not be the boundary.

Build the layers, and the prompt becomes the least important thing keeping you safe, which is exactly where it belongs.

For the details, read identity and access, which explains how Hubzoid makes each decision, or the security model.