rho-cli 0.1.26

Rho CLI tools for encrypted agent collaboration, dataset publishing, controlled runs, and result release workflows
Documentation
# Rho Gondolin

This document describes the current `rho run` integration for running an
untrusted command inside a Gondolin VM with mounted inputs and outputs.

## Overview

The current model is:

- YAML is treated as untrusted and declarative
- hooks are not allowed in YAML
- `pre` and `post` hooks may only be passed on the command line
- network access can be default-deny, with explicit allowlisting from the
  command line

The main entrypoint is:

```bash
./rho run <config.yaml> [options]
```

For the sample config in this repo, there is also a wrapper:

```bash
./rho-gondolin [options]
```

That wrapper expands to:

```bash
./rho run repos/gondolin/host/examples/bnumber-sandbox/run.yaml [options]
```

## Sample Config

The sample config lives at:

- [`repos/gondolin/host/examples/bnumber-sandbox/run.yaml`]/Users/madhavajay/dev/rho/main/repos/gondolin/host/examples/bnumber-sandbox/run.yaml

It contains:

- `network.defaultDeny: true`
- a read-only input file mount
- a read-only code file mount
- a writable output directory mount
- a main command that runs the mounted code file

Current sample YAML:

```yaml
network:
  defaultDeny: true

mounts:
  - host: ./input/bnumber.txt
    guest: /input/bnumber.txt
    mode: ro
  - host: ./code/increment-bnumber.sh
    guest: /code/increment-bnumber.sh
    mode: ro
  - host: ./output
    guest: /output
    mode: rw

command:
  - /bin/sh
  - /code/increment-bnumber.sh
  - /input/bnumber.txt
  - /output/result.txt
```

## Trust Model

The YAML file is intentionally limited.

Allowed in YAML:

- `mounts`
- `command`
- `args`
- `cwd`
- `env`
- `network`
- `sandbox`

Not allowed in YAML:

- `preHooks`
- `postHooks`

If either hook field appears in YAML, `rho run` fails.

Hooks must be supplied explicitly by the trusted caller on the command line:

- `--pre-hook '...'`
- `--post-hook '...'`

## Mounts

Each mount has:

- `host`: host path, resolved relative to the YAML file
- `guest`: absolute guest path
- `mode`: `ro` or `rw`

Rules:

- directory mounts can be `ro` or `rw`
- file mounts are staged as read-only mounts
- writable outputs should be directories, not single files

In the sample:

- [`input/bnumber.txt`](/Users/madhavajay/dev/rho/main/repos/gondolin/host/examples/bnumber-sandbox/input/bnumber.txt) is mounted at `/input/bnumber.txt`
- [`code/increment-bnumber.sh`](/Users/madhavajay/dev/rho/main/repos/gondolin/host/examples/bnumber-sandbox/code/increment-bnumber.sh) is mounted at `/code/increment-bnumber.sh`
- [`output/`](/Users/madhavajay/dev/rho/main/repos/gondolin/host/examples/bnumber-sandbox/output) is mounted at `/output`

## Network Policy

`network.defaultDeny: true` means outbound HTTP is blocked unless you explicitly
allow a host.

Allow hosts from the command line:

```bash
--allow-host example.com
```

You can repeat the flag:

```bash
--allow-host example.com --allow-host api.github.com
```

## Hooks

Hooks run inside the same VM as the main command.

- `pre-hook`: runs before the main command
- `post-hook`: runs after the main command

Current behavior:

- pre-hooks fail closed; if a pre-hook exits non-zero, the main command does not run
- post-hooks still run after the main command path enters cleanup
- post-hook failures propagate as exit status if the main command succeeded

Example hooks:

```bash
--pre-hook 'curl -fsS https://example.com/ > /output/example.html'
--post-hook "printf 'post-hook-ran\n' > /output/post-hook.txt"
```

## Commands

Run the sample directly through `rho`:

```bash
cd /Users/madhavajay/dev/rho/main
./rho run repos/gondolin/host/examples/bnumber-sandbox/run.yaml \
  --allow-host example.com \
  --pre-hook 'curl -fsS https://example.com/ > /output/example.html' \
  --post-hook "printf 'post-hook-ran\n' > /output/post-hook.txt"
```

Run the same sample through the wrapper:

```bash
cd /Users/madhavajay/dev/rho/main
./rho-gondolin \
  --allow-host example.com \
  --pre-hook 'curl -fsS https://example.com/ > /output/example.html' \
  --post-hook "printf 'post-hook-ran\n' > /output/post-hook.txt"
```

Blocked example:

```bash
cd /Users/madhavajay/dev/rho/main
./rho-gondolin \
  --pre-hook 'curl -fsS https://example.com/ > /output/example.html'
```

That blocked example fails because the config is default-deny and `example.com`
was not allowlisted.

## Outputs

After a successful allowed run, the sample writes:

- [`output/result.txt`]/Users/madhavajay/dev/rho/main/repos/gondolin/host/examples/bnumber-sandbox/output/result.txt
- [`output/example.html`]/Users/madhavajay/dev/rho/main/repos/gondolin/host/examples/bnumber-sandbox/output/example.html
- [`output/post-hook.txt`]/Users/madhavajay/dev/rho/main/repos/gondolin/host/examples/bnumber-sandbox/output/post-hook.txt

The main command reads the input number and writes the incremented result using:

- [`increment-bnumber.sh`]/Users/madhavajay/dev/rho/main/repos/gondolin/host/examples/bnumber-sandbox/code/increment-bnumber.sh

## Dry Run

To inspect the resolved execution plan without starting the VM:

```bash
./rho-gondolin \
  --dry-run \
  --allow-host example.com \
  --pre-hook 'curl -fsS https://example.com/ > /output/example.html' \
  --post-hook "printf 'post-hook-ran\n' > /output/post-hook.txt"
```