Docs
The site, the API, and the run record
What a run writes down, the dashboard and the API, reaching them from another machine, an agent's memory, and where state lives.
What a run writes down
Every run, scheduled or not, records each step: what it was handed, what it returned, how long it took and what it cost. A step that asked a model is a line with a price on it, so a job that grew a second model call shows a second line and a bigger number. A run that is waiting shows who was asked and until when.
A trial run is marked a trial, and lists every message and email it would have sent and did not.
A prompt job or a reply that chloe stopped in the middle offers to carry on
from there (POST /api/runs/<id>/carry-on): the model is handed what it had
done, and told which call never finished. A reply in a conversation does it by
itself when chloe starts again, once, with the tools it had: its answer goes in
the conversation, and to whoever asked when their channel can reach them.
The dashboard
chloe serves its own dashboard at 127.0.0.1:3067 (serve.port in settings),
with nothing to install. Starting it prints a link that opens the page signed
in.
| Address | What it shows |
|---|---|
/ |
every agent, at a glance |
/agents/<id> |
one agent: what it is set up to do, its jobs, channels and connections |
/agents/<id>/chat |
talking to it |
/agents/<id>/log |
its runs; ?run=<id> opens one |
/agents/<id>/files/<path> |
its own files |
/agents/<id>/memory |
its memory |
/log |
every agent's runs, searched together |
/account |
your password, a name to sign in with, and signing out |
/configuration |
every setting and key as a form: Google, models, mail and alerts, web search, .env, and chloe.config.ts as text |
/appearance |
how the page looks, kept in this browser |
/tokens |
making and revoking tokens |
/people |
inviting people, changing or removing what they were given, and a link to set a new password for one who forgot theirs |
All of them but an agent's are under Dashboard, at the right of the bar.
Who may reach it
| Who | How | What they may do |
|---|---|---|
| You | a link from npx chloe link, or the password if you set one |
everything |
| Somebody you invited | their email and their own password, from the link you sent them | only the agents they were let in to, and on each only chat, read or run as given. See One person, and the people you invite. |
| Another system | a token, made at /tokens or with npx chloe tokens make |
read the API, and chat to and run the jobs of agents with an HTTP channel. Never write a file, read a memory or touch the tokens. |
| Anybody | GET /api, whether there is a password, signing in, signing out, reading or taking an invitation or a password link whose link they hold, and asking for a password link by mail |
A fresh copy has no password and opens with a link: npx chloe prints one as
it starts while there is no password, and npx chloe link prints one whenever
asked. A link signs one browser in for a week, works once, and only within the
hour. Making one reads data/login.json, so it takes a shell on the machine.
A password is for signing in without a link. Set the first one on the page,
under My account, or with npx chloe account. My account changes it, given
the current one, and a new one signs every other browser out. You may add a
name there too, and then signing in asks for the name and the password.
Forgotten either, npx chloe account sets a new password and prints the name. A token is shown once and kept
only as a hash. A token made with --agent <id> reaches that agent and nothing
else.
GET /api lists every route with a line on what it does and who may call it:
a page in a browser, JSON for anything else.
token=$(curl -s -X POST localhost:3067/api/login \
-H 'content-type: application/json' \
-d '{"password":"yours", "name":"yours, if you chose one"}' | jq -r .token)
curl -s localhost:3067/api/agents -H "authorization: Bearer $token"From another machine
chloe listens on loopback only unless you say otherwise, and sends nothing anywhere you did not name: no copy of your runs, agents or memories goes to any other dashboard. To reach the dashboard from somewhere else:
npx chloe --remote, for a server you reach over SSH. chloe listens on every address and serves the page over HTTPS, with a certificate it makes itself and keeps in the state folder, and prints a link at the address your SSH session came in on. The browser says the connection is not private once, because nobody else signed that certificate: the fingerprint it shows is the one chloe printed. Plain HTTP answers only on the machine itself. This puts the page on the internet, guarded by the link and the password, and a firewall that closes the port keeps it from opening.- An SSH tunnel.
ssh -L 3067:127.0.0.1:3067 you@yourbox, then127.0.0.1:3067in your browser. Nothing is opened. - A private network of your own, such as Tailscale or WireGuard, with a web server on the box that only it reaches.
- Your own web server on a public name, passing everything on to chloe's
port. This puts the run history, the agents' files and their memories one
password away from the internet, so use a long password. The server in front
must pass the visitor's address on, as
cf-connecting-ipor by adding tox-forwarded-for, and nothing but it may reach the port, or the lockout for wrong passwords is worth nothing. A memory file's pass works only from the address that asked for it, which is read the same way.
For Caddy, the whole of it is:
agents.example.com {
reverse_proxy 127.0.0.1:3067 {
flush_interval -1
}
}flush_interval -1 lets a reply stream while the agent is still working.
Whichever way you reach it, put that address in the config as serve: { url: "https://agents.example.com" }, so an agent can send you a link to a file in its
memory rather than a path.
An agent's memory
Every agent has a memory folder, data/memory/<id>/ unless its memory says
otherwise (see An agent). data/memory is one git
repository, a folder per agent: what a run changes in its own folder is
committed when the run ends, under the agent's name, and the run lists the
commit.
The dashboard shows a memory the way an editor shows a folder: the tree, open files as tabs, and beside it what changed, with commit, push and pull. Every file has a History view with each commit's changes and an Undo. Undo is refused when the file has changed since, because putting it back would lose the later change. An agent's page lists its changes newest first, with the ones since you last looked marked.
A memory file other than plain text is shown in a sandboxed frame. It may be HTML an agent wrote from an email or a web page, so it runs in a sandbox: its own script runs, but it cannot reach the dashboard around it, call the API as you, or send anything out.
Every file served is logged first, in data/memory-audit/<agent>.jsonl, and
a read that could not be logged is refused. Listing a folder is logged too.
Reading the same file from a shell on the machine is not, because a shell is
already the whole machine. Only you can reach a memory; a token cannot.
State
Everything chloe keeps is in data/, beside chloe.config.ts. Copying data/
and cloning your repo is the whole of moving to a new machine.
data/agents.db conversations and the run history
data/login.json the password and what signs the sessions and links, mode 600
data/tokens.json the tokens, as hashes, mode 600
data/seen-addresses.json where sign-ins came from, for the new-address alert
data/memory-audit/ every memory file served, one log per agent
data/memory/<id>/ what that agent keeps, with its historyCHLOE_STATE and CHLOE_MEMORY in .env move them, for when your agents live
inside a repo of your own. Git should ignore data/, and git clean -x would
delete it: do not run that in a repo that holds it.