gantree

Console — the yard board

The product is the crane: ai-gantry. This page is the operator walk for this repo — the board that appears when you run more than one. Pitch and why the harness is worth operating live in the root readme. Install: install.md. Headless host + attach: headless.md. Login, profile, settings: operators.md. Door: security.md.

Chat stays Telegram (or Discord / Slack). Nothing here sits in a chat turn. Gantree reads Docker and files after the fact. It writes the same files the harness already understands.

browser  →  gantree (localhost | Tailscale | tunnel)  →  Docker + files
                                                         gantry  gantry  gantry

Agents open zero inbound ports. Bind 127.0.0.1 by default. If you expose the console, you expose it to yourself.


First boot

Need Node 22. npm start (or compose) on the Docker host.

  1. Open http://127.0.0.1:3000 — first boot is /setup (one operator).
  2. After that, /login.
  3. Sessions live in yard gantree.db — not a crane’s data/gantry.db.

Forgot the passphrase: stop, delete that sqlite file, start, run setup again. No email reset. Click your name for Profile; the cog is Settings. Roles, add/remove, and the stuck table: operators.md. What the door checks: security.md.

Screenshot yard (no daemon)

The board looks empty or unknown when Docker is missing — common on SteamOS / a laptop without the socket. Seed a photographable house, then paint it:

npm run seed
# .env (loopback only — never compose):
# GANTREE_DEV=1
# GANTREE_DEV_OPERATOR=bob
# GANTREE_DEV_PASSPHRASE=bob-dev-ok
# GANTREE_SHOT=1
npm run dev
node scripts/shot.mjs http://127.0.0.1:3000 yard host crane crane-metrics metrics profile settings

That writes five named cranes (kit, ada, jules, moss, piper), five operators with photos and Telegram ids, and host/token series in gantree.db. GANTREE_SHOT does not start containers. A real daemon on Arch/SteamOS is often $XDG_RUNTIME_DIR/docker.sock — set DOCKER_SOCKET and unset GANTREE_SHOT.

First-boot setup: create the operator who owns the box   Log in with name and passphrase   Log in on a phone


Board

A handful of named pets, not a Kubernetes dashboard. Each card: name, alive or not, model, channel, published vs skipped MCP. Nags (dead process, skipped grant, needs-auth) sit on the card — you do not have to open Tools. Click through. Kit’s page is Kit — graphs and log, not a mixed fleet dump. The host card (CPU bubble) is the Mini itself: CPU, RAM, and Docker network (rx/tx), gantree.toml, yard sqlite, and how this process was started. People (roles) stay on the cog.

Build a crane from the board (yard type first: home Mini or cloud VM). Telegram: Create with BotFather copies /newbot, opens the chat, and suggests {slug}_bot — paste the token it replies with. Upload a photo on the crane — it lands in persona/avatar.jpg, shows on the board, and if the channel is Telegram the bot’s face updates too. On a Telegram crane, Telegram (below the photo) can getMe the token, push name / about / the / command menu (including /new), ask an allowlisted chatter to tap /new (distill, then drop that thread — the yard cannot send as her), and edit TELEGRAM_ALLOWED_USERS from numeric ids (slog user_id after someone talks — not @username). Grant a tool, recreate, watch that crane’s doctor. Message it on Telegram. /tools is the crane’s mouth; this page is the operator’s.

The yard on a phone   Jules on a phone — start/stop and boxed folds

Pin: shotah/ai-gantry:latest (Hub). Nested repos/ai-gantry is dev only — do not copy .env or data/ from a private checkout.


Grant is how it stays personal

Long-horizon is useless if the container is “healthy” with zero tools and a dead token. MCP is the grant. The UI is a structured editor of that crane’s mcp.toml, not a second inventory.

Hand-editing mcp.toml still works. Your own binary: custom-mcp.md.

Two files, not a catalog

File Who writes it
PERSONA.md You — who it should be, who you are
persona/avatar.jpg You — face on the board; Telegram bots get the same picture
SELF.md The agent — voice, rituals, north-star aims that survive /new

Gantree edits those files (and .env, and mcp.toml). It does not merge memories across cranes. Isolation is the feature: one human, one bot, one directory, one data/. Delete a tryout = delete that directory.

The seed on disk is lib/yard/crane/templates/PERSONA.example.md — keep it identical to ai-gantry examples/persona/PERSONA.example.md. Replace from template loads that file (agent Identity name from the slug). Inject user (admin) copies selected profile fields into About you only; Save still writes PERSONA.md. Recreate / rebuild never replace an existing persona file.


Pull, don’t punch

The crane does not grow a /metrics port. The yard pulls (docker inspect, docker logs, sampled stats, JSON slog). Parse what the harness already emits. Do not tax parallel tool calls so a chart looks nicer. Samples and turns land in yard sqlite (7-day cap) so bouncing npm start does not wipe Kit’s graphs. Mutations (grant, recreate, env, operators) show who on a small events strip — not a SIEM. Admin also sees logins and logouts there; other operators do not.

Doctor says why a tool is skipped (no binary / no key / no OAuth). Recreate keeps the host user that owns data/ (never Distroless 65532), network_mode, and extra binds.


Operator loop

  1. npm start (or compose) on the Docker host
  2. Board shows every gantry in gantree.toml — alive or not
  3. Click a card: per-instance graphs + visual logs. Host card: Mini CPU/RAM/net + gantree.toml
  4. Build a crane (yard type → slug → model → channel → profile)
  5. Grant / revoke MCP; files update; container recreates
  6. Watch that agent’s log and metrics until the grant is real
  7. Doctor says why something is skipped

The board has to work. The speed the human feels is still the crane.