gantree
The household: gantree writes files to the crane and never sits in the turn. Pendant, cab, and helm dial the mailbox. The crane dials out to that room, to Telegram, and to a model.

The household

The crane is the product. This is the door to the whole household.

ai-gantry is the runtime: one Distroless Go process, one persona, one OpenAI-compat model, memory you can sqlite3. Chat dials out. /new distills; the person is still there on Thursday. Health is an exit code. No open ports. Ever.

Most things branded “harness engineering” are skills and slash commands inside a host you do not own. This stack is the host. Gantree is the shipping yard those cranes live in — a board for an operator, never a hop in a chat turn. You talk through a mailbox we own: gantry-pendant in the browser, gantry-cab on Android (and Android Auto), gantry-helm on iPhone (and CarPlay). Telegram, Discord, and Slack still work.

gantry
The rigid frame that holds and positions tools. The frame does nothing by itself; the tools and the memory do the work.
gantree
The shipping yard those cranes live in. Files and Docker. Pull, don’t punch. Never a hop in a turn.
pendant
The web app and the mailbox. React, open it anywhere — desktop or phone. One Cloudflare Durable Object room per crane. The browser and the crane both dial in.
cab
The Android seat. Pocket thread and Auto message cards. Same room, same crane. A native app on the pendant mailbox.
helm
The iPhone sister. Pocket thread and CarPlay message cards. Same mailbox. Same frames.

Five repos

One file contract. No shared database.

The crane never learns the yard exists. The Worker never sees the yard cookie. Gantree writes files (and pendant Worker secrets from Settings). The crane’s .env is the one human allowlist. MCP binaries are optional children, when you grant them.

How a person crosses the yard, the pendant, and the crane: access. How the yard is put together: architecture. How the wires meet: ecosystem. What every mouth has to paint the same way: frontends.

The difference

A harness is a process. A marketplace is a costume.

What this stack is

  • A binary you run. Aims, personality, and work still exist on Thursday.
  • The model is a socket — Ollama, Gemini, ChatGPT, Grok. Swap it. The loop stays.
  • MCP listed in mcp.toml is the grant. Omit it and it does not exist.
  • Outbound chat. Default channel is pendant: the web app, cab, and helm share the room. Telegram, Discord, and Slack still work. No open ports. Ever.
  • Independent lookups fan out in one round; the next round uses the results. Garmin → sheet → Strava is one thread.

What it is not

  • A Claude Code or Copilot plugin marketplace with “harness” in the README.
  • Prompts, skills, and sub-agents that still live in someone else’s context window.
  • A chatbot that dies when the window gets expensive, then /new lobotomizes it.
  • A dashboard or gateway in the token path so every message pays a platform tax.
  • MkDocs around a wrapper. Documentation is not a runtime.

The crane

ai-gantry holds the person. The yard does not talk.

One process. One persona. Chat is a mouth. Health is an exit code. Mid-chain does not die on a typo — the loop was hardened on the 7–12B class, then pointed at Flash when you want the trajectory now. Chat, memory, cron, and web search work with zero extra tools.

static binary + persona + mcp.toml + any OpenAI-compat LLM  →  outbound chat

PERSONA.md is who it should be, written by you. SELF.md is voice and a few north-star aims, written by the agent, and it survives /new. Two files. The live tool catalog stays in the turn, from mcp.toml.

The mouths

One mailbox. A browser and two native apps. Vendor chat skips it.

Pendant is a React web app. Open it on a desktop or a phone, wherever a browser is. Cab and helm are the native apps. All three dial the same Durable Object — on the wire the role is phone. The crane dials that room role=crane, outbound only. The Mini opens zero inbound ports. Home internet can drop; a note still waits in the room, and Kit thinks again when the crane has a network.

Goals and the pocket list are snapshots the crane pushes. Photos ride the same frame as the caption. Voice stays on the device — the wire carries text, including a spoken turn, and no audio. Cab is Kotlin. Helm is Swift. Android Auto and CarPlay are message cards on that room. One CHANNEL per crane. The yard writes CHANNEL=pendant and mints the bearer.

The yard

Operate named pets. Never sit in the turn.

Gantree is the operator plane: board, doctor, grants, logs, spend. It reads Docker and files after the fact. If a chart would make the harness slower, the chart is wrong. Agents open zero inbound ports. Only this UI is reachable, and only through a path you chose.

Shipping yard — one console, many cranes, outbound only

Yard board — host, boards, named cranes, spend, tags

Ada’s dashboard — spend, metrics, tags

Crane metric tiles — CPU, memory, tokens, completer, MCP, uptime

Log in — the door to the yard, not the chat The yard on a phone — named cranes, spend, tags A crane on a phone — run controls and metrics

Hello

Docker on the same Linux host. Node 22. First boot is /setup.

git clone https://github.com/shotah/gantree.git
cd gantree
cp gantree.toml.example gantree.toml
npm install
npm run build
npm start                 # http://127.0.0.1:3000

Open the board. Settings → Pendant. Build a crane. Grant a tool. Message it from the browser, from Cab, or from Helm. The UI never sits in a chat turn. You do not need the other checkouts to stand a crane up — this yard pulls shotah/ai-gantry, writes the files, recreates. Home Mini vs cloud VM: install.

Start here

The board, the door, the mouths.