TEHAS

Installing pi-tehas for a new project

One install serves one project: one host, one Unix user, one product repository. Installing it for another repository means preparing a host and filling in an instance directory. No script is edited.

The work comes in three levels.

Level You get Effort
1. Issue to pull request Queue, cycles, gates, dashboard, live console Settings only, plus your toolchain in the sandbox image
2. Database clones A private copy of your data per cycle or preview Project-specific. Not generic yet
3. Staging stacks and previews The product running per pull request behind fake domains Project-specific. Not generic yet

Level 1 is the supported contract. This page walks through it with the instance that works on tehas itself: host tehas-tehas, user boss, product janit/tehas-private. Its settings are in instances/tehas-tehas/ in this repository, and the record of setting it up is docs/bringup/2026-10-08-tehas-tehas-log.md.

What the product repository must provide

This is the contract. It lives in the product repository, is written by people, and is the only thing pi-tehas reads from it.

File Purpose Required
AGENTS.md The rules agents work under: architecture rules, how to verify, commit style, what never to touch. No sentence granting merge rights, unless you want auto-merge Yes
.pi/verify-cmd Fast gate. The first line that is not empty or a comment is run in the cycle's worktree, 10 minute limit. Without it pi-rukas guesses from package.json or Cargo.toml, and for other stacks verifies nothing Yes
.pi/verify-cmd-full Full gate, same format; stands in for CI Recommended
.pi/smoke-cmd Proves the built product starts, same format Recommended
.pi/worktree-setup Shell script run in each new worktree with no arguments and no environment: install dependencies. Must exit 0 If a bare checkout cannot run the gates
.pi/settings.json defaultProvider and defaultModel for the session Yes
.gitignore entries .pi/work-state/, .pi/permissions.json, .pi/decisions.json Yes
.agents/skills/ Project skills: short instructions plus scripts for things agents get wrong Optional

This repository is a product too, so its own AGENTS.md, .pi/ and .gitignore are a small working example to copy from. Make the gates print one line per step and exit non-zero on failure. Before going further, prove them by hand in a clean checkout.

Work lands on the repository's default branch: pi-rukas branches from it and the pull request targets it. tehas reads the default branch from the checkout. Set TEHAS_BRANCH only to use another one.

Level 1: issue to pull request

1. A host

One Linux machine, reachable over a private network, that can reach GitHub and an OpenAI-compatible model endpoint. Bootstrap a host prepares it:

sudo ./bootstrap.sh root boss
./bootstrap.sh user
./bootstrap.sh check

The tehas user ends up in the docker group, which makes it root-equivalent on the host. The boundary that matters is the sandbox: agents never get the Docker socket.

2. Credentials

The bootstrap page has the details and the token trap to avoid.

3. Two clones

The platform checkout is this repository at ~/tehas/platform, where you ran bootstrap.sh from. Clone the product next to it, with the bot's token:

git clone <the product repository> ~/tehas/repos/<name>

The product checkout is where cycles work. The platform checkout is where install.sh runs from. Keep them apart even when, as in the example, they are the same repository: the factory then changes only when a person pulls the platform checkout and installs.

4. An instance directory

Copy instances/example/ and fill it in. It can live anywhere the tehas user can read: next to the secrets in ~/.config/tehas (the default place), in a private repository of your own, or, as the example does, in the platform checkout.

echo ~/tehas/platform/instances/tehas-tehas >~/.config/tehas/instance-dir

The example's whole instance.env:

TEHAS_REPO=janit/tehas-private
TEHAS_CONTROL_ISSUE=5
TEHAS_ALLOWED_LABELERS="janit"

PI_ENSEMBLE_SUBAGENT_MODEL=qwen3.8-flash-next
PI_ENSEMBLE_SUBAGENT_PROVIDER=fleet
PI_ENSEMBLE_HOST_ALIASES=gb1:100.95.94.105

TEHAS_HOST_ADDR=100.110.154.50
Setting What to put there
TEHAS_REPO owner/name of the product repository
TEHAS_CONTROL_ISSUE Number of an issue you create for the pause label. Keep it open
TEHAS_ALLOWED_LABELERS Logins whose tehas:ready starts a cycle, space separated. Never the bot
PI_ENSEMBLE_SUBAGENT_MODEL, _PROVIDER The model every agent role uses. Must be listed in models.json
PI_ENSEMBLE_HOST_ALIASES name:address pairs the sandbox needs, such as a model host whose name only resolves privately
TEHAS_HOST_ADDR The host's private address, to reach the dashboard from other devices. Without it: 127.0.0.1

models.json next to it names the provider's baseUrl and the models it serves. List the model you use first: an agent that loses its model setting falls back to the first entry. The model in the product's .pi/settings.json must be listed too.

Everything else has a default. The limits of a cycle's container default to the host's CPUs minus one and half its memory. All settings are on the commands page.

5. Install

cd ~/tehas/platform
./install.sh --check
./install.sh
sed -i '1i [ -f ~/tehas/env.sh ] \&\& . ~/tehas/env.sh' ~/.bashrc

The last line puts env.sh first in ~/.bashrc. It has to come before the check many distributions have there that returns early for non-interactive shells, or ssh host '<command>' would run without the tehas environment.

--check first tells you what is missing or malformed in the instance, what tools the host lacks, and what would be installed. A real run with invalid settings installs nothing and exits 78.

6. The sandbox image

sandbox/Dockerfile adds deno, shellcheck and Playwright's Chromium to the pi-rukas image and remaps the user id. Add whatever your gates need. Then:

~/tehas/sandbox/build.sh

7. Prove the gates in the sandbox

Before the first issue. This runs the product's own gates inside the image, as the unprivileged user, with nothing mounted but a clone:

T=~/tehas/tmp/contract-test
git clone -q ~/tehas/repos/<name> "$T"
docker run --rm --cpus 4 --memory 4g --user "$(id -u):$(id -g)" \
  -e HOME=/home/vscode -v "$T:$T" -w "$T" --entrypoint bash \
  "$PI_ENSEMBLE_IMAGE" -c '
    sh .pi/worktree-setup &&
    sh -c "$(grep -v -e "^#" -e "^$" .pi/verify-cmd-full | head -n 1)" &&
    sh -c "$(grep -v -e "^#" -e "^$" .pi/smoke-cmd | head -n 1)"'
rm -rf "$T"

8. Start

docker compose -f ~/tehas/edge/docs.compose.yaml up -d   # this site and the dashboard
tehas-dispatch --init-labels
tehas-dispatch --dry-run
systemctl --user enable --now tehas-dispatch.timer

Then label one small, well-specified issue with tehas:ready and watch it on the dashboard. Operating tehas takes it from there.

Levels 2 and 3: clones and previews

Both are off by default (TEHAS_CLONES=0, TEHAS_PREVIEWS=0) and then nothing of them is installed or running.

What is in this repository for them is one product's implementation, not an interface:

Staging shows them in use. A second product that needs previews is what would turn this into a shared interface; nobody has needed it yet, so none was designed. Until then, adopting levels 2 or 3 means writing your own versions of those files.

Two rules to carry over whatever the product:

Switching a feature off later is safe: install.sh stops what the feature runs before it removes the files.

What is not generic yet