Home / Self-host

Run the whole platform in your own VPC.

One compose command brings up the conversation-QA workspace on loopback; the default stack opens no outbound connection.

$ docker compose up -d
your cloud · your VPC
YOUR VPC loopback only · 127.0.0.1:8321 hotato serve read-only · 5 views bearer-token auth SQLite registry evidence store · sha256 /data volume local judge · Ollama optional · private network no published port your browser SSH tunnel / reverse proxy you control internet unreached no outbound
From the compose file: workspace, registry, and evidence store inside your boundary; the opt-in judge stays on the private network.

Small on purpose, and up in one command.

Three parts, and only the first is required; the core is standard-library Python.

hotato
The pip-installable package; the core carries zero runtime dependencies.
hotato serve
The read-only, token-authenticated workspace: five views over your registry and evidence store.
Ollama
A local model judge for the rubric lane, opt-in behind a compose profile.
repository root
# build, then bring the workspace up on loopback
docker compose build
docker compose up -d
# -> http://127.0.0.1:8321

# print the token the server generated
docker compose exec hotato-workspace cat /data/serve/default/token
Reach it through an SSH tunnel or reverse proxy you control; the CLI ingests calls into the same /data.

A model judge that stays on the private network.

The rubric lane scores a transcript against criteria with a local model.

enable the judge
$ docker compose --profile judge up -d
pull a model once
$ docker compose exec ollama ollama pull qwen2.5vl:3b

Ollama publishes no port and is reachable only on the private compose network. The model pull is the one network fetch; inference runs offline. The endpoint gate keys on hostname alone, so this non-loopback endpoint takes one acknowledgment.

Carry it anywhere. Back it up in one command.

The workspace runs without a network, and everything it needs lives in one volume you own.

save · move · load
# on a connected machine
docker compose build
docker save hotato-selfhost:local | gzip > hotato-selfhost.tar.gz

# move the tarball to the air-gapped host, then:
gunzip -c hotato-selfhost.tar.gz | docker load && docker compose up -d
The judge model moves the same way: pull into a named volume, restore it on the target.

The /data volume holds the registry, evidence store, token, and audit log. Tar it and restore it on a host you run; a rebuild upgrades the image and leaves registry and evidence untouched. The store is content-addressed: a restored artifact is the same bytes that produced the verdict.

# back up the /data volume
docker run --rm -v hotato_hotato-data:/data -v "$PWD":/backup alpine \
  tar czf /backup/hotato-data-backup.tgz -C /data .

One script proves it: ./deploy/verify-zero-egress.sh runs the same image with the network removed; the workspace still serves a view.

Only loopback
Confirms 127.0.0.1:8321 is the one published binding.
Network removed
Runs the image on an internal Docker network; a view still returns success.
Nothing external
Lists established connections in the container; none leave the host.

Your calls. Your infrastructure. Zero egress.

The workspace, registry, and evidence store, up in one command inside your VPC.

$ docker compose up -d

Read the token, then open 127.0.0.1:8321. Full posture →