# Rho Git Collaboration Tutorial

This is the intended end-to-end Rho workflow for a GitHub-backed project:

- a project owner creates a Rho repo and publishes policy
- a collaborator joins by submitting their identity in a PR
- the owner admits the collaborator
- the owner publishes mock data while keeping real data private
- the collaborator writes analysis code against the mock data
- the collaborator requests a protected real-data run
- the owner reviews the PR, validates policy, converts a bounded receiver-side proposal into an exact action, approves it, runs it in the sandbox, and releases the result

The current automated version of this flow is:

```bash
RHO_LOCAL_GIT_PI_LIVE=1 bash tests/e2e/local-git-pi-sandbox-encrypted.sh
```

That test uses a local bare Git repo instead of GitHub PRs, but the roles and files match the flow below.

## Project Owner

Install Rho:

```bash
git clone git@github.com:<you>/rho.git
cd rho
./install.sh
rho --version
```

Create a GitHub repository, then clone it:

```bash
git clone git@github.com:<owner>/<project>.git
cd <project>
```

Initialize the Rho project:

```bash
rho repo init \
  --repo-id rho://repo/github/<owner>/<project> \
  --owner rho://id/github/<owner>
```

Create or select your local identity:

```bash
rho id init \
  --github <owner> \
  --generate-ssh-key \
  --display-name "<Your Name>"
```

Export your public identity into the repo:

```bash
mkdir -p rho/participants
rho id export \
  --identity rho://id/github/<owner> \
  --out rho/participants/<owner>.yaml
```

Add encrypted inbox policy for yourself:

```bash
rho repo protect-path "rho/messages/inbox/<owner>/**" \
  --recipient rho://id/github/<owner>
```

Check the repo:

```bash
rho repo doctor --root .
```

Commit and push:

```bash
git add rho.yaml rho .gitattributes
git commit -m "Initialize Rho project"
git push origin main
```

## Project Collaborator

Install Rho:

```bash
git clone git@github.com:<you>/rho.git
cd rho
./install.sh
rho --version
```

Find and clone the project:

```bash
git clone git@github.com:<owner>/<project>.git
cd <project>
```

Install the repo-local crypto filters:

```bash
rho repo install-filters --root .
rho id import rho/participants/<owner>.yaml
```

Create or select your local identity:

```bash
rho id init \
  --github <collaborator> \
  --generate-ssh-key \
  --display-name "<Your Name>"
```

Export your public identity into the repo on a branch:

```bash
git checkout -b <collaborator>/join-rho
mkdir -p rho/participants
rho id export \
  --identity rho://id/github/<collaborator> \
  --out rho/participants/<collaborator>.yaml
```

Commit, push, and open a PR:

```bash
git add rho/participants/<collaborator>.yaml
git commit -m "Request Rho project access"
git push origin <collaborator>/join-rho
```

Today, this is the join request: a PR containing the collaborator's public identity. Later this should become a higher-level `rho repo join` command that writes the same files.

## Owner: Review Join PR

Fetch the collaborator branch and inspect the identity:

```bash
git fetch origin <collaborator>/join-rho
git checkout -b review/<collaborator>/join-rho origin/<collaborator>/join-rho
rho id import rho/participants/<collaborator>.yaml
rho id show rho://id/github/<collaborator>
```

Verify the GitHub profile claim:

```bash
rho id verify-github --identity rho://id/github/<collaborator>
```

Run doctor:

```bash
rho repo doctor --root .
```

If the identity and PR are acceptable, admit the collaborator by adding the paths they are allowed to receive encrypted messages on:

```bash
rho repo protect-path "rho/messages/inbox/<collaborator>/**" \
  --recipient rho://id/github/<collaborator>
```

Commit the owner-controlled policy update and merge the PR:

```bash
git add rho/participants/<collaborator>.yaml rho/policy/permissions.yaml .gitattributes
git commit -m "Admit <collaborator> to Rho project"
git push origin review/<collaborator>/join-rho
```

In the current implementation, the owner is responsible for making this policy update. Later this should be wrapped by an explicit `rho repo admit` command.

## Owner: Add Data

Prepare real data and mock data. The mock data is committed to Git. The real data stays in the owner's private local storage.

```bash
mkdir -p data/private data/mock
$EDITOR data/private/prices-real.csv
$EDITOR data/mock/prices-mock.csv
```

Create the twin dataset:

```bash
rho dataset \
  --name prices \
  --owner rho://id/github/<owner> \
  --real data/private/prices-real.csv \
  --mock data/mock/prices-mock.csv \
  --share-dir users/<owner>/datasets/share \
  --private-dir ~/.rho/projects/<project>/datasets/private
```

Publish the mock dataset into the project:

```bash
rho publish <owner> <dataset-uuid> \
  --source-root users \
  --target-root datasets
```

Commit and push:

```bash
git add datasets
git commit -m "Publish prices mock dataset"
git push origin main
```

The local test currently uses:

```text
sandbox/live-git-pi-sandbox-encrypted/user1/datasets/public/<dataset-uuid>/mock/prices-mock.csv
sandbox/live-git-pi-sandbox-encrypted/user1-private/users/rho-user1/datasets/private/<dataset-uuid>/real/prices-real.csv
```

## Collaborator: Create Code And Request A Run

Pull the latest project:

```bash
git checkout main
git pull origin main
rho repo install-filters --root .
```

Create code against the mock dataset. You can write this yourself or ask your agent to do it.

Example prompt:

```text
Write a Python script at workspace/sum_prices.py that reads a CSV path from argv[1],
sums the price column, prints only the numeric total, and write a Rho request for
dataset <dataset-uuid>. Do not create a controlled action or tool call.
```

Run against mock data locally:

```bash
python3 workspace/sum_prices.py datasets/<dataset-uuid>/mock/prices-mock.csv
```

Create a branch:

```bash
git checkout -b <collaborator>/prices-analysis
```

Submit the run request. The lower-level command today writes a request manifest; the encrypted inbox message is what the Pi-backed flow writes around it:

```bash
rho request create-run \
  --shared-root . \
  --id req-<id> \
  --from rho://id/github/<collaborator> \
  --to rho://id/github/<owner> \
  --tool-id run_real \
  --dataset-uuid <dataset-uuid> \
  --code-path workspace/sum_prices.py \
  --command "python3 workspace/sum_prices.py DATASET_CSV" \
  --tier real
```

For the Pi-backed flow, the collaborator-side agent writes only code and a request message. It must not create a controlled action or tool call. The live e2e test proves this path: Pi creates `workspace/sum_prices.py` and the encrypted request message, while the trusted owner side later decides whether to construct an action.

Sign the encrypted inbox request if your flow created one:

```bash
rho crypto sign rho/messages/inbox/<owner>/req-<id>/request.yaml \
  --identity rho://id/github/<collaborator> \
  --out rho/messages/inbox/<owner>/req-<id>/request.rhosig.yaml

git add workspace rho/messages .rho/requests
git commit -m "Request prices real-data run"
git push origin <collaborator>/prices-analysis
```

Open a PR.

## Owner: Review Run PR

Fetch the PR branch:

```bash
git fetch origin <collaborator>/prices-analysis
git checkout -b review/<collaborator>/prices-analysis origin/<collaborator>/prices-analysis
rho repo install-filters --root .
```

Validate the repo state:

```bash
rho repo doctor --root .
```

Review the code:

```bash
sed -n '1,200p' workspace/sum_prices.py
```

Optionally run it against mock data:

```bash
python3 workspace/sum_prices.py datasets/<dataset-uuid>/mock/prices-mock.csv
```

Verify the collaborator signature:

```bash
rho crypto verify rho/messages/inbox/<owner>/req-<id>/request.yaml \
  --signature rho/messages/inbox/<owner>/req-<id>/request.rhosig.yaml \
  --identity rho://id/github/<collaborator>
```

Have your trusted owner-side agent read the encrypted request and reviewed code if you want help deciding what to do. That agent writes a proposal, not a controlled action. The important boundary is:

- collaborator-side agent writes request/code only
- owner-side agent writes proposal only
- host maps and validates the proposal into `control/outbox/<action-id>.json`
- owner grants the exact action and input hashes
- sandbox execution touches real data only after grant

```bash
mkdir -p host-control/proposals
$EDITOR host-control/proposals/act-<id>.yaml
```

The proposal format is intentionally smaller than a controlled action:

```yaml
version: 1
proposed_action:
  action_id: act-<id>
  request_id: req-<id>
  tool_id: run_real
  requested_by: rho://id/github/<collaborator>
  requested_for: rho://id/github/<owner>
  action_type: run_real_data
  script_path: /exec/workspace/sum_prices.py
  output_path: /exec/.rho/runs/run-<id>/stdout.txt
  summary: Run the reviewed price summing script on real data.
  reason: The request signature and script shape were reviewed.
```

Convert the proposal into a controlled action using explicit allowlists:

```bash
rho run proposal-action \
  --proposal host-control/proposals/act-<id>.yaml \
  --control-root host-control \
  --action-id act-<id> \
  --map /exec=<owner-exec-workspace> \
  --script-root <owner-exec-workspace>/workspace \
  --output-root <owner-exec-workspace>/.rho/runs \
  --allow-tool run_real \
  --allow-requested-by rho://id/github/<collaborator> \
  --allow-requested-for rho://id/github/<owner> \
  --allow-action-type run_real_data
```

Approve the exact action and input hashes:

```bash
rho run grant-action \
  --shared-root . \
  --control-root host-control \
  --action-id act-<id> \
  --granted-by rho://id/github/<owner>
```

Run it in the sandbox:

```bash
RHO_SANDBOX_RUNNER=gondolin rho run controlled-action \
  --shared-root <owner-exec-workspace> \
  --control-root host-control \
  --action-id act-<id> \
  --private-root ~/.rho/projects/<project>/datasets/private
```

Release the result back to the collaborator:

```bash
rho result publish \
  --shared-root . \
  --request-id req-<id> \
  --result-id result-<id> \
  --from rho://id/github/<owner> \
  --to rho://id/github/<collaborator> \
  --artifact .rho/runs/run-<id>/stdout.txt
```

Commit and push:

```bash
git add rho/messages .rho/results
git commit -m "Release prices run result"
git push origin main
```

## Collaborator: Read Result

Pull the owner release:

```bash
git checkout main
git pull origin main
rho repo install-filters --root .
rho repo doctor --root .
```

Read the decrypted result:

```bash
sed -n '1,120p' rho/messages/inbox/<collaborator>/req-<id>/result.yaml
```

## Local Automated Version

Run the deterministic cached version:

```bash
bash tests/e2e/local-git-pi-sandbox-encrypted.sh
```

Run the live Pi + Gondolin version:

```bash
RHO_LOCAL_GIT_PI_LIVE=1 bash tests/e2e/local-git-pi-sandbox-encrypted.sh
```

Expected final line:

```text
live git pi sandbox encrypted e2e passed
```

The live test creates:

```text
sandbox/live-git-pi-sandbox-encrypted/remote.git
sandbox/live-git-pi-sandbox-encrypted/user1
sandbox/live-git-pi-sandbox-encrypted/user2
sandbox/live-git-pi-sandbox-encrypted/user1-review
sandbox/live-git-pi-sandbox-encrypted/user1-exec
sandbox/live-git-pi-sandbox-encrypted/user1-release
sandbox/live-git-pi-sandbox-encrypted/user2-review
```

It also writes Gondolin policy artifacts for the two live Pi calls:

```text
sandbox/live-git-pi-sandbox-encrypted/user2-agent-policy.json
sandbox/live-git-pi-sandbox-encrypted/user1-review-agent-policy.json
```

Those policies record the explicit host filesystem mounts, fixed environment, default-deny network policy, synthetic DNS, disabled websockets, and allowed outbound hosts used by `rho agent-run --sandbox`.

Those folders map to the GitHub flow:

```text
remote.git       GitHub repo
user1            owner clone
user2            collaborator clone
user1-review     owner checkout of collaborator PR
user1-exec       owner execution workspace
user1-release    owner result branch
user2-review     collaborator checkout after release
```

## Current Gaps

- Join and admit are still composed from `rho id export`, Git commits, and `rho repo protect-path`. They should become first-class commands.
- The GitHub PR version is not automated yet. The e2e test uses local branches and a local bare remote.
- Release/publish actions still need the same proposal-to-controlled-action boundary as run actions.
- The tutorial shows the target human flow. The automated test is the source of truth for the exact commands that currently pass end to end.
