Quick start — a framework for outside callers
From nothing to another system calling your framework, in eight steps. The framework does the work; HexaEight decides who may ask, what model it uses, which command may run, and exactly what it can reach.
The one idea to hold on to: a runner reaches only the memories you share into it. Your documents are shared as local memories; your APIs are shared as served memories. There is no other door — no workspace memories, no files, no shell.
Before you start
Section titled “Before you start”- A working agent (the install), with agent r30, harness engine r41 and
Activate 1.0.68 or later:
hexaeight-activate verify-env. - A router route whose model makes real tool calls. Measured: one route’s model answered “what is the weather” by inventing a tool result; another called the tool every time. You will check this in step 7.
- Python 3.10–3.13, or uv (it fetches one).
The examples use CrewAI; langgraph, pydanticai and agno work the same way. Folder names follow
the install: ~/hbia-agent is the licence folder (your identity files),
~/heia-agent is your agent’s folder — use yours if they differ.
1 · Try it in your own workspace
Section titled “1 · Try it in your own workspace”cd ~/heia-agent # your agent's folderhexaeight-activate enable crewaiIn the workspace choose runmission → CrewAI_v1_Runner and say hello. The answer is headed
Answered by CrewAI. This is the same mission the runner will serve — see
What a mission is.
2 · Create the runner
Section titled “2 · Create the runner”A runner is a second agent, sealed to one mission, that serves outside callers:
hexaeight-activate add-runner --from ~/hbia-agent --dir ~/runner-crewai \ --model "<route|model>"--from supplies the identity only (hardlinked, never copied). The runner gets its own ports, policy,
store and working folders.
3 · Put the framework into the runner
Section titled “3 · Put the framework into the runner”cd ~/runner-crewaihexaeight-activate enable crewaiRun inside the runner’s folder, enable installs the mission into the runner’s own store and
seals its one command there (this folder is a runner: its own harness root).
4 · Give it information — documents, as memories
Section titled “4 · Give it information — documents, as memories”The runner sees only its mission until you share something:
hexaeight-activate runner-memory --listhexaeight-activate runner-memory --share Product-Handbook # linked: no copyhexaeight-activate runner-memory --share Product-Handbook --copy # or a self-contained copyA local memory is one you built in the workspace from your documents (Documents it can search). Linked, the runner searches the same index and sees updates; copied, it keeps its own.
5 · Give it APIs — also as memories
Section titled “5 · Give it APIs — also as memories”An API reaches a runner the same way: as a served memory — a name that stands for a sealed route on another agent.
- Seal the API on the agent that runs it — An API you already run:
Terminal window cd ~/api-agent && hexaeight-activate add-api --name weather --port 38490 - Point a memory at it.
add-apiprints the pointer’s exact fields. Add it as a served memory in the workspace — that writes~/.hexaeight-harness/memories/weather/remote.json— or save those fields yourself to that path (what a pointer holds). - Share it with the runner — this copies that pointer into the runner’s store:
Terminal window cd ~/runner-crewai && hexaeight-activate runner-memory --share weather - Let the runner call that agent, and that agent accept the runner — one rule on each side
(the two rules);
add-runner --peer <api-agent>ormodify-agent --dir ~/runner-crewai --peer <api-agent>writes the runner’s side.
Every call then goes through service_call, over the turn’s ticket, under the runner’s policy. The
framework never sees a host, a port or a key.
Other agents need no memory of their own. The runner has connect-to-agent built in —
memory_search("connect-to-agent", "<agent name> <question>"). Because a runner always answers someone
else, it will not ask a third agent for them unless you switch relay on
(No relay by default).
6 · Start it
Section titled “6 · Start it”cd ~/runner-crewai && hexaeight-activate restart agentThis stops and starts only the agent in this folder, and gives the runner its own store. Its
agent.log must show:
[hexaeight-agent] engine 'missionrun-external' runs ONE mission (sealed): CrewAI_v1_Runner[licence] … policy rule(s) in force …[mcp-external] OPENED BY CONFIG (external.allowTools): service_call,who_am_i7 · Let a caller in, and call it
Section titled “7 · Let a caller in, and call it”A caller is a backend acting for a person. Three stores must name that person as
sha512(email) — the runner’s vouch and inbound rule, and the router’s policy. The exact commands
are in Other software calling it.
The call, from the caller’s own folder (any folder holding an identity):
cd ~/caller # the caller's identity folderset -a; . ./env-file; set +a # the caller's identityexport HEIA_DIR=~/caller-heia # its OWN store — never the runner's, or the door answers your question back to youexport HEIA_ASK_TIMEOUT_SECONDS=0 # wait for the work; the runner bounds its own turn
printf '{"ask":"What is the weather in Chennai?","on_behalf_of":"%s","peer":"<runner-agent-name>","peer_url":"http://127.0.0.1:<runner-port>","capability":"crewai"}\n' "$SUB" \ | ./hexaeight-agent-linux-x64 ask-daemonpeer is the runner’s agent name (its identity), peer_url its port from step 2, and capability
a label for this kind of request. Only the output line starting with { is the result; the answer is
the result field of its last frame.
Check it really used the tool — each caller session has its own folder under
~/runner-crewai-frontdoor/work/, and the framework’s log there has one line per tool call:
[tool] search:weather('Chennai') -> '[weatherapi.com] Current weather in Chennai … 29.9°C …'An answer with no matching [tool] line was not fetched — change the runner’s model (step 2).
8 · Make it yours
Section titled “8 · Make it yours”The runner is runner.py inside the mission: a HexaEight half (identical in every framework) and a
framework half, run(question, session_id). Duplicate the mission, change the framework half, and
seal your version — see Your framework already works.
When something is off
Section titled “When something is off”| you see | it means |
|---|---|
an answer that names a source, but no [tool] line in the log | the model invented it — use a model that makes real tool calls |
nothing found in '<name>' | that memory is not shared into the runner — runner-memory --share <name> |
'<tool>' is not available on the external server | the runner’s external tools do not include it — modify-agent --allow-tools service_call,who_am_i |
Connection refused | the runner is still starting — give it ~25 s after restart agent |
| a caller can list every memory on the machine | a runner made before Activate 1.0.67 — re-create it with add-runner |