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:
A caller can ask a question. A caller cannot choose the mission, inject priming, or reach anything outside the fence.
The words this page uses
Section titled “The words this page uses”ask-daemon | the agent binary’s client mode — it makes one encrypted call to another agent and prints the reply |
peer / peer_url | who you are calling, by identity name, and where it listens |
capability | which sealed capability on that agent you are asking for |
on_behalf_of | the person this turn is for, as sha512(their email) — never the address |
HEIA_DIR | an agent’s own store. The caller and the door must not share one (see gate 3) |
Provision the runner
Section titled “Provision the runner”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.
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:
cd /home/you/my-runner && hexaeight-activate restart agentrestart 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.
What the runner can see
Section titled “What the runner can see”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:
cd /home/you/my-runnerhexaeight-activate runner-memory --listhexaeight-activate runner-memory --share weather # served: the pointer is exportedhexaeight-activate runner-memory --share My_Documents # local: linked, no copyhexaeight-activate runner-memory --share My_Documents --copy # or a self-contained copyhexaeight-activate runner-memory --unshare weatherA 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.
What provisioning does not do for you
Section titled “What provisioning does not do for you”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.
cd /home/you/my-runnerset -a; . ./env-file; set +a./hexaeight-agent-linux-x64 vouch allow --caller <your-agent-name> --subject "$SUB"Symptom without it: not permitted to act on behalf of that subject.
2. The same subject needs an inbound rule
Section titled “2. The same subject needs an inbound rule”./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 it3. 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:
export HEIA_DIR=/home/you/caller-heia # making the call4. 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.
6. The agent binary must be new enough
Section titled “6. The agent binary must be new enough”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.
Make the call
Section titled “Make the call”cd /path/to/caller-identityset -a; . ./env-file; set +aexport HEIA_DIR=/home/you/caller-heiaexport HEIA_ASK_TIMEOUT_SECONDS=0 # 0 = wait for the work; the peer bounds its own turn
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-daemonOnly a line starting with { is a result — the tool narrates to the same stdout.
What success looks like
Section titled “What success looks like”{"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 charsA 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).
Three policy stores authorise one turn
Section titled “Three policy stores authorise one turn”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 decides | rules live in |
|---|---|---|
| 1 | the serving agent | the runner folder, via --add-rule |
| 2 | the router, relay gate | the router’s own sealed policy |
| 3 | the router, model gate | the 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.
A cached session outlives a policy change
Section titled “A cached session outlives a policy 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.
Verifying the door is really closed
Section titled “Verifying the door is really closed”[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.