Skip to content

Other software calling it

Everything so far assumed the caller is you, or another agent you run. The front door is for the case where it is not: another company’s backend, a customer’s application, a partner system.

The shape that makes this safe:

the caller their software open a session ask a question runner agent /external/auth /external/incoming bound to a person ONE sealed procedure a caller chooses the question · never the method

A caller can ask a question. A caller cannot choose the mission, inject priming, or reach anything outside the fence.

ask-daemonthe agent binary’s client mode — it makes one encrypted call to another agent and prints the reply
peer / peer_urlwho you are calling, by identity name, and where it listens
capabilitywhich sealed capability on that agent you are asking for
on_behalf_ofthe person this turn is for, as sha512(their email) — never the address
HEIA_DIRan agent’s own store. The caller and the door must not share one (see gate 3)

Use a separate agent for the front door. It gets its own ports, its own policy store, and one sealed engine — and it can run alongside your workspace agent.

Terminal window
hexaeight-activate add-external-agent \
--from /home/you/my-agent \
--dir /home/you/my-runner \
--mission My_Workflow \
--license personal \
--base-port 8930

--from supplies the identity only — hexaeight.mac and env-file are hardlinked, never copied. Ports are chosen (or checked, with --base-port) before anything is created, so a port clash leaves nothing behind. Pass --model "<route|model>" to choose the runner’s model; without it, the source agent’s mission route is used.

Start and restart it from its own folder:

Terminal window
cd /home/you/my-runner && hexaeight-activate restart agent

restart agent stops and starts only the agent in that folder — the output names every other agent it left alone — and gives a runner its own store (HEIA_DIR=<runner>-frontdoor/heia) automatically.

A runner has its own harness root, <runner>-frontdoor/harness-root, and the mission it runs sees only that — its own mission, and nothing else on the machine, until you export something into it:

Terminal window
cd /home/you/my-runner
hexaeight-activate runner-memory --list
hexaeight-activate runner-memory --share weather # served: the pointer is exported
hexaeight-activate runner-memory --share My_Documents # local: linked, no copy
hexaeight-activate runner-memory --share My_Documents --copy # or a self-contained copy
hexaeight-activate runner-memory --unshare weather

A served memory is still reached through service_call under the runner’s own policy. Each caller session also gets its own working folder, <runner>-frontdoor/work/<session>/.

Measured before this existed: an outside caller asked a runner to list memories and got every memory on the machine. A runner created before Activate 1.0.67 still behaves that way — re-create it with add-runner, then share only what it needs.

Six things stand between a freshly provisioned runner and its first successful call. Each fails differently, and none of them says “policy”.

These are setup steps, not bugs — but provisioning could do several of them for you, and the intent is that it will. Until then, work through them in order; each one’s symptom is given so you can tell which you have hit.

1. The vouch subject is the hash, not the email

Section titled “1. The vouch subject is the hash, not the email”

An external turn binds to sha512(email), not the address.

Terminal window
cd /home/you/my-runner
set -a; . ./env-file; set +a
SUB=$(printf '[email protected]' | sha512sum | cut -d' ' -f1)
./hexaeight-agent-linux-x64 vouch allow --caller <your-agent-name> --subject "$SUB"

Symptom without it: not permitted to act on behalf of that subject.

Terminal window
./hexaeight-agent-linux-x64 hexaeight-agent.json \
--add-rule "${SUB},<your-agent-name>,inbound,allow"

Symptom without it, and this is the one nothing prepares you for — the router approves and the agent then refuses:

[engineproxy] REFUSED to register router session … no matching allow rule (default-deny)
[engineproxy] router authorized … but the agent policy refused to register it

3. The caller and the door need separate stores

Section titled “3. The caller and the door need separate stores”

Both resolve through HEIA_DIR. Sharing one means the caller’s “expecting a reply” record and the door’s inbox are the same file, and the door answers your own question back to you:

{"ok":true,"accepted":"response"}

restart agent gives the door its own store (above). Give the caller a different one:

Terminal window
export HEIA_DIR=/home/you/caller-heia # making the call

4. The runner’s harness root starts with nothing in it

Section titled “4. The runner’s harness root starts with nothing in it”

The wrapper runs the engine with --root <runner>-frontdoor/harness-root, created empty. Put the mission into it and export the memories it uses, or the mission has nothing to navigate — the turn returns a permissions frame in about a second and writes nothing.

  • A published framework mission: hexaeight-activate enable <framework> run in the runner’s folder installs it into the runner’s root and seals its command there.
  • Your own mission: agskill-import --in <bundle.zip> --root <runner>-frontdoor/harness-root.
  • Its memories: hexaeight-activate runner-memory --share <name> for each one.

5. Re-sealing drops the wrapper unless you say otherwise

Section titled “5. Re-sealing drops the wrapper unless you say otherwise”

engine --add without --file unbinds the wrapper silently. Same symptom as above.

A binary older than the sealed-mission feature ignores the mission and reports -> process rather than runs ONE mission (sealed): <name>. No error, no log line explaining it.

Is the caller another agent? See Two agents talking to each other for the rules on both agents and their routers.

Terminal window
cd /path/to/caller-identity
set -a; . ./env-file; set +a
export HEIA_DIR=/home/you/caller-heia
export HEIA_ASK_TIMEOUT_SECONDS=0 # 0 = wait for the work; the peer bounds its own turn
SUB=$(printf '[email protected]' | sha512sum | cut -d' ' -f1)
printf '{"ask":"<question>","on_behalf_of":"%s","peer":"<agent>",
"peer_url":"http://127.0.0.1:8930","capability":"<cap>"}\n' "$SUB" \
| ./hexaeight-agent-linux-x64 ask-daemon

Only a line starting with { is a result — the tool narrates to the same stdout.

{"sent":true,"reason":"sent","peer":"<agent>","session":"s-2ec7a313…","reply":"…"}

sent:true means it was delivered and answered. The reply holds the turn’s frames; the last one is the result:

{"type":"result","subtype":"success","is_error":false,
"result":"<the answer>","num_turns":11,"duration_ms":152906,
"usage":{"input_tokens":117855,"output_tokens":1475}}

Read subtype first: success means the procedure ran to an answer. And check the door’s own log recorded the turn — if the wrapper never ran, nothing is written there:

2026-09-25T22:21:08+00:00 mission=My_Workflow in=431 chars out=701 chars

A failure is just as legible:

{"sent":true,"reply":"{\"ok\":false,\"error\":\"not permitted to act on behalf of that subject\"}"}

which is gate 1 above — the vouch names the address instead of sha512(address).

This is where a day goes if it is not written down. Each has its own rule list, and a rule in one is invisible to the other two.

#who decidesrules live in
1the serving agentthe runner folder, via --add-rule
2the router, relay gatethe router’s own sealed policy
3the router, model gatethe same file

The router’s policy is sealed under the router’s key — the agent’s CLI cannot write it, and the router does not hot-reload. Restart it after a change.

Sessions are cached by (peer, on_behalf_of). A session opened before you fixed a rule — or before you re-sealed the mission — keeps being reused and keeps failing:

'<subject>' may not post to session 'ext-s-…'

Retire the cached entry under <HEIA_DIR>/peer-sessions/ and the next call mints a fresh one. It does not self-heal.

[hexaeight-agent] engine 'missionrun-external' runs ONE mission (sealed): My_Workflow
[exec-policy] mission 'My_Workflow': enforce ON, NO cmdset — shell denied
[external] enabled — authenticated:missionrun-external cleartext:OFF
[byoa] vouch policy loaded: 2 rule(s)

Four lines. If the first is missing the caller can choose the mission. If the second says anything other than shell denied the mission can run commands. Read them after every restart.