# 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"
```