# Security Notes

This repo currently uses two different security patterns for secrets in sandboxed agent runs.

## Stronger Pattern: Gondolin Placeholder Secrets

Gondolin supports host-side secret mediation where the guest only sees placeholders and the host swaps in the real secret on outbound HTTP requests to allowed hosts.

References:

- [repos/gondolin/docs/secrets.md](/Users/madhavajay/dev/rho/main/repos/gondolin/docs/secrets.md)
- [repos/gondolin/docs/security.md](/Users/madhavajay/dev/rho/main/repos/gondolin/docs/security.md)

Important properties:

- the guest does not receive the real secret value
- substitution is host-mediated
- substitution is limited to allowed hosts
- request headers are the primary supported substitution point

This is the preferred long-term model.

Important limitation for Pi today:

- Gondolin's built-in placeholder substitution targets headers by default
- Pi's `openai-codex` OAuth refresh sends the refresh token in the POST body to `auth.openai.com`
- so header substitution alone is not enough for a fully non-readable `auth.json` replacement

That means a fake credential file plus simple `createHttpHooks({ secrets })` is only enough for flows where:

- the guest can operate on a placeholder token value, and
- the real secret only needs to appear in outbound headers

It is not enough for refresh flows unless we add custom host-side request rewriting.

Observed with the current `two-console-demo-agent` scenario:

- Pi pinned to `openai-codex/gpt-5.4`
- sandbox allowlist included `auth.openai.com` and `chatgpt.com`
- the sandbox successfully reached `auth.openai.com`
- the run failed there with `refresh_token_expired`, so the direct model call to `chatgpt.com` was never reached in that run

## Pi Gondolin Example Caveat

The upstream Gondolin example at:

- [repos/gondolin/host/examples/pi-gondolin.ts](/Users/madhavajay/dev/rho/main/repos/gondolin/host/examples/pi-gondolin.ts)

works by starting Pi on the host and overriding Pi's built-in `read` / `write` / `edit` / `bash` tools so they execute inside a Gondolin VM.

That is useful for sandboxing tool effects, but it is not the same as running the agent itself inside the sandbox.

Why that matters:

- the model still plans on the host first
- prompt injection can still influence host-side planning before a tool call enters Gondolin
- only the tool execution path is sandboxed

For high-assurance containment, the model process itself needs to run inside Gondolin, which is what `rho agent-run --sandbox` is intended to move toward.

## Current Pi Sandbox Compatibility Path

To make Pi run inside Gondolin today, [rho-agent-run.md](/Users/madhavajay/dev/rho/main/docs/rho-agent-run.md) supports:

```bash
./rho agent-run --sandbox --auth-json-source /Users/madhavajay/.pi/agent/auth.json ...
```

That path stages a copy of `auth.json` into a temporary host directory and mounts it into the guest agent directory.

This makes Pi functional inside the sandbox, but it has a known weakness:

- the staged credentials are readable by the guest process
- the guest can read both access and refresh tokens from the mounted compatibility file
- if those credentials are reused outside the sandbox, the guest can exfiltrate them to any allowed egress target

That means this path is weaker than Gondolin placeholder substitution and should be treated as a temporary compatibility path.

## Known Issue To Fix

We need a future path where Pi provider credentials can be used inside Gondolin without making them guest-readable.

Likely direction:

- run Pi itself inside Gondolin, not only its tool calls
- prefer provider API keys via env placeholders when possible
- add an explicit `openai-codex` env/runtime credential path in Pi so it does not require `auth.json`
- use a non-secret placeholder token shape when the guest needs token structure, for example a JWT-shaped placeholder that preserves non-secret account metadata only
- wire host-side provider secret injection into `rho run` / `rho agent-run`
- add custom host-side request rewriting for credential flows that currently place secrets in request bodies
- avoid mounting real `auth.json` into the guest

Until then:

- do not treat `--auth-json-source` as high-assurance secret isolation
- use sandbox mounts and host allowlists carefully
- prefer minimal writable mounts and minimal outbound host allowlists
