Skip to content

Trust and permission

Once rules exist, the default is deny: anything not permitted is refused, and every capability is something you granted on purpose.

Until the first rule exists, nothing is enforced. A freshly activated agent is permissive by design, so that installing or upgrading one can never lock an operator out of their own machine. Writing the first rule is how you opt in. The installer does it for you: it writes a starting policy that admits only you.

The boot line tells you which state you are in:

[licence] personal licence - N policy rule(s) in force

0 policy rule(s) means nothing is being enforced. Any other number means deny-by-default is live.

Once it is on, the arrangement is the opposite of an API key, where the key means “everything this key can reach” and scoping it down is extra work nobody does.

{ "owner": "[email protected]" }

Without owner in hexaeight-agent.json, no policy is enforced at all — every rule present, none applied. The boot line tells you which state you are in:

[licence] personal licence - 10 policy rule(s) in force - agent may be exposed normally

0 policy rule(s) means the door is open. Check it after every change.

<principal> , <object> , <direction> , allow|deny
partis
principalwho is acting — a person’s email, an agent’s name, or *
objectwhat they are reaching — an agent name, op:*, editor:*, or *
directioninbound (reaching this agent) or outbound (this agent reaching out)

Deny beats allow, so a deny holds even where a wildcard would admit.

rulewithout it
<agent>,op:*,outboundsign-in works and skill runs fail with bind said 404
<agent>,*,inboundthe router’s own replies come back as that identity and are refused
*,<agent>,outboundengines cannot reach the router — the sender is engine:<name>, so this must be *

Never write a blanket *,*,inbound,deny. It outranks everything else you wrote, including the rules that let you in.

An external turn is authorised three times, by three components, each with its own rule list:

#whowhere the rules are
1the serving agentits own folder
2the router, relay gatethe router’s sealed policy
3the router, model gatethe same file

A rule in one is invisible to the other two. The router’s file is sealed under the router’s key, so the agent CLI cannot write it — and the router reads it once at startup. Restart after changes.

An agent acting on behalf of someone is a separate, explicit grant:

Terminal window
SUB=$(printf '[email protected]' | sha512sum | cut -d' ' -f1)
./hexaeight-agent-linux-x64 vouch allow --caller <agent-name> --subject "$SUB"

The subject is the hash, not the address. This is what makes a turn bind to a person rather than to the agent, which two of the three gates require.

Identity and policy decide who. Two more mechanisms decide what.

The jail. Every engine turn runs with the identity folder masked:

[hexaeight-agent] engine jail: ON (bwrap) masking [/home/you/heia-agent]

A turn cannot read env-file even though it is running as the agent.

The mechanism differs by platform — bwrap on Linux, and on Windows because the agent runs under WSL; macOS uses its own sandbox. Check yours reports as working:

Terminal window
hexaeight-activate sandbox

The command set. Shell access is denied unless a mission explicitly carries a cmdset:

[exec-policy] mission 'My_Workflow': enforce ON, NO cmdset — shell denied (memory/skills/native only)

A rule that is present but does not match reads as protection and is not.

Terminal window
hexaeight-activate list-policy --subject [email protected]

That prints what the enforcer decides, not what is written down. It is the only check worth trusting. The baseline output also shows a deliberate failure:

inbound [email protected] -> <agent> DENY ok

A test that expects a refusal, and gets one.

  • No API keys to issue, rotate, escrow or leak — identity is cryptographic and machine-bound
  • Default-deny, so the blast radius of a new agent is nothing until you widen it
  • Revocation is a policy edit, not a key rotation across every consumer
  • Binaries gate on published hashes, fetched at runtime — a bad build is withdrawn centrally
  • Prompt injection is bounded by policy, not by prompting — a caller of a sealed mission cannot choose the procedure, and the fence limits what the procedure may touch