gantree

Install — home Mini or cloud VM

Same console. Same files. Agents still open zero inbound ports. Only this UI is reachable, and only through a path you chose.

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

Home (Mini / NUC)

Docker on the box. Cast / life-cast is allowed (mDNS, TV on the LAN). Need Node 22 (node -v). Distro apt install npm is often Node 20 and will fail. Headless host, attach existing agents, tunnel, gotchas: headless.md. What the board is for: console.md. Login, profile, settings: operators.md.

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 — first boot is /setup
# or Hub / compose (LAN :80 → container :3000):
docker compose up -d

Open the board at http://<pc-lan-ip>/ or http://<headless-lan-ip>/ (compose). Create the first operator, then build a crane (yard = home). npm start stays :3000. Grant search. Chat is Telegram — not this UI.

Local screenshots / npm run dev: GANTREE_DEV=1 plus operator + passphrase in .env (.env.example). Loopback only — compose HOST=0.0.0.0 ignores the flag. Unset it to photograph /login. Details: security.md · operators.md.

First operator — create the person who owns the box   Log in   Log in on a phone

Sessions live in gantree.db next to the checkout (compose: var/gantree.db). That file is the yard’s sqlite — not a crane’s data/gantry.db. Forgot the passphrase: delete it and run /setup again. Add a partner from Settings (the cog) after login (admin / user or readonly on assigned cranes). Your photo and passphrase are Profile. People walk: operators.md. What the door checks: security.md. A partner who only updates keys is user on their crane; they hit compose :80, not gantree :3000 on the host.

A public hostname is a different file. Prefer Cloudflare Tunnel if the zone is already on Cloudflare (no WAN ports, orange cloud is fine):

# .env: CLOUDFLARE_TUNNEL_TOKEN=...
docker compose -f compose.cloudflare.yml up -d

Dashboard public hostname origin: http://gantree:3000. Login is still gantree’s door.

Origin TLS on this box (grey-cloud DNS, router 80 and 443): compose.nginx.yml + GANTREE_DOMAIN / LETSENCRYPT_EMAIL. Do not mix the two hostname files.

Cloud (your GCE / EC2)

Still your machine. Layout in the spirit of ai-gantry examples/hosting (/opt/gantree, compose, Hub pull). life-cast is hidden.

sudo mkdir -p /opt/gantree
sudo git clone https://github.com/shotah/gantree.git /opt/gantree
cd /opt/gantree
cp gantree.toml.example gantree.toml
# gantree.toml: yard = "cloud"
GANTREE_LISTEN=127.0.0.1 docker compose up -d

On a cloud VM pin the publish to loopback (GANTREE_LISTEN=127.0.0.1). The process inside still listens on 0.0.0.0 so the port map works; that is not a public load balancer. Do not open a cloud firewall port to the world. A public hostname on Cloudflare: compose.cloudflare.yml (no WAN ports). Origin TLS on the VM: compose.nginx.yml.

Reach it from a laptop:

Tailscale (preferred): install Tailscale on the VM, then Serve or an SSH tunnel to 127.0.0.1:80 (compose). Do not open a cloud firewall port to the world.

ssh -N -L 3000:127.0.0.1:80 user@host

Cloudflare Tunnel (console only — never the agents): compose.cloudflare.yml + CLOUDFLARE_TUNNEL_TOKEN in .env, origin http://gantree:3000. Quick tunnel without compose:

cloudflared tunnel --url http://127.0.0.1:80

OAuth is always the laptop hop: needs auth → start hop or /auth in Telegram → paste the code on the Tools screen.

Agents have no ports: in their compose. Do not add any.

A server that is not on the Tools grid: hand-edit that crane’s mcp.toml (custom MCP).

Image pin

New cranes use shotah/ai-gantry:latest (DEFAULT_IMAGE in lib/yard/types.ts — that is the only place the tag lives). pull + recreate refreshes the floating tag. Override a crane with a 0.x.y tag only if you need to freeze.

pull + recreate runs docker pull then replaces the container as the host user that owns data/ (file owner, then the compose shell UID). Distroless default uid 65532 cannot open a gantry.db written by your login — that is session store open failed. Recreate without pull keeps that uid too; it does not fetch a new image. Do not delete data/ or re-import.

Console-in-Docker (sock + same-path binds for attached agents): headless.md. Uncomment the volume in compose.yml when gantree.toml uses absolute host paths.

Hub image: shotah/gantree (latest / edge / 0.x.y). Same secrets as ai-gantry on this GitHub repo: DOCKER_HUB_USERNAME, DOCKER_HUB_ACCESS_TOKEN.

Release

No Makefile. This repo is npm.

npm test
npm run release:dry          # show next tag
npm run release              # patch bump, tag v*, push → Hub + GH Release
npm run release -- --bump=minor

Requires a clean tree. Updates package.json / lockfile, annotated vX.Y.Z, moves floating git tag latest, pushes. CI builds linux/amd64 only.