Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
faf3093
feat(egress-gate): add attested Pi admission
johnnygreco Aug 12, 2026
021281f
docs(egress-gate): add Pi admission example
johnnygreco Aug 12, 2026
c5601c0
fix(egress-gate): own Pi integration extension
johnnygreco Aug 12, 2026
355db91
refactor(egress-gate): use user message append hook
johnnygreco Aug 12, 2026
9d53095
refactor(egress-gate): focus Pi example on deny and redact
johnnygreco Aug 12, 2026
22c93e6
docs(egress-gate): replace simulated Pi example
johnnygreco Aug 17, 2026
0540f54
fix(egress-gate): clean up Pi admission integration
johnnygreco Aug 17, 2026
35ec55c
docs(egress-gate): simplify Pi admission demo
johnnygreco Aug 20, 2026
de716d7
chore: add example license headers
johnnygreco Aug 20, 2026
c8be825
chore: ignore local planning files
johnnygreco Aug 24, 2026
80b268d
chore(egress-gate): merge upstream main
johnnygreco Aug 26, 2026
acebc5d
docs(egress-gate): sync Pi example forks
johnnygreco Aug 26, 2026
7f71b16
docs(egress-gate): streamline Pi example setup
johnnygreco Aug 26, 2026
324d744
fix(egress-gate): isolate nested OpenShell checkout
johnnygreco Aug 27, 2026
f512f94
feat(egress-gate): complete Pi attested admission example
johnnygreco Aug 28, 2026
252a977
fix(egress-gate): recreate example provider profile on launch
johnnygreco Aug 28, 2026
18158b9
fix(egress-gate): redact model credentials from Pi tool output
johnnygreco Aug 28, 2026
fb51e76
fix(egress-gate): distinguish credential placeholders from secrets
johnnygreco Aug 28, 2026
8d1b739
fix(egress-gate): allow maximum Pi request payloads
johnnygreco Aug 28, 2026
8d9f96b
feat(egress-gate): isolate managed Pi admission
johnnygreco Aug 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ temp/
*.temp
*.bak
.scratch/
plans/

# Python
__pycache__/
Expand Down
1 change: 1 addition & 0 deletions projects/egress-gate/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.workspaces/
19 changes: 15 additions & 4 deletions projects/egress-gate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ commands work from any directory and do not depend on repository-only files:
egress-gate gates list
egress-gate gates schema
egress-gate validate --policy /absolute/path/to/your-policy.yaml
egress-gate serve --listen 127.0.0.1:50051
egress-gate serve --listen 127.0.0.1:50051 --no-require-pi-attestation
```

## Source-checkout quickstart
Expand All @@ -39,7 +39,7 @@ uv run egress-gate gates list
uv run egress-gate gates schema
uv run egress-gate validate \
--policy examples/regex-redaction/egress-gate-config.yaml
uv run egress-gate serve --listen 127.0.0.1:50051
uv run egress-gate serve --listen 127.0.0.1:50051 --no-require-pi-attestation
uv run egress-gate evaluate \
--policy examples/regex-redaction/egress-gate-config.yaml \
--cases examples/regex-redaction/cases.yaml
Expand All @@ -49,6 +49,13 @@ Use `0.0.0.0` only when the OpenShell supervisor must reach the service across
network namespaces. The development server uses plaintext gRPC. Restrict its
listen port to trusted networks.

The CLI requires managed Pi context attestations by default, coupling admission
to provider egress verification. The general Gate quickstarts opt out
explicitly. Keep the default, or pass `--require-pi-attestation`, for managed
Pi; use `--no-require-pi-attestation` only for an intentionally unmanaged deployment.
See the [managed Pi example](examples/pi-attested-admission/README.md) for the
matching Pi and OpenShell fork branches, startup contract, and current limits.

## Policy shape

The registry builds an exact strict schema from installed gate types:
Expand Down Expand Up @@ -87,7 +94,7 @@ need initialization, helper bases, or typed resources use the full class-based

```bash
uv run egress-gate --registry my_gates:registry gates list
uv run egress-gate --registry my_gates:registry serve
uv run egress-gate --registry my_gates:registry serve --no-require-pi-attestation
```

OpenShell owns interception, routing, and credential attachment. Egress Gate
Expand All @@ -103,11 +110,14 @@ from egress_gate.service import EgressGateServer
server = EgressGateServer(
create_builtin_registry(),
timeout_middleware_processing=10,
require_pi_attestation=False,
)
server.serve_sync("127.0.0.1:50051")
```

In this example, `timeout_middleware_processing` gives each evaluation 10
Make the `require_pi_attestation` choice explicit in programmatic deployments; set
it to `True` for managed Pi. In this unmanaged example,
`timeout_middleware_processing` gives each evaluation 10
seconds. Omitting it uses the one-second service default. The value is expressed
in seconds, must be at least 10 milliseconds, and must resolve to whole
milliseconds. The service passes one resulting `Timeout` through slot
Expand Down Expand Up @@ -136,6 +146,7 @@ timeout failures must deny.
- [Architecture](https://github.com/NVIDIA/OpenShell-Research/blob/main/projects/egress-gate/docs/architecture/index.md)
- [Limits and failures](https://github.com/NVIDIA/OpenShell-Research/blob/main/projects/egress-gate/docs/reference/limits-and-failures.md)
- [Regex redaction composition](https://github.com/NVIDIA/OpenShell-Research/tree/main/projects/egress-gate/examples/regex-redaction)
- [Pi attested-admission example](examples/pi-attested-admission/README.md)
- [Function-based custom gate](https://github.com/NVIDIA/OpenShell-Research/tree/main/projects/egress-gate/examples/custom-gate)
- [Class-based custom gate](https://github.com/NVIDIA/OpenShell-Research/tree/main/projects/egress-gate/examples/class-based-gate)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
EGRESS_GATE_HOST_IP=YOUR_HOST_IPV4
PI_MODEL_BASE_URL=https://provider.example.com/v1
PI_MODEL_ID=your-model-id
PI_MODEL_API_KEY=your-provider-key
193 changes: 193 additions & 0 deletions projects/egress-gate/examples/pi-attested-admission/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# Managed Pi attested-admission example

This example runs a normal interactive Pi TUI inside OpenShell and sends
admitted conversation context to a model endpoint you choose. The endpoint may be a hosted
provider, an internal gateway, or a local server. It must accept the OpenAI Chat
Completions request shape used by the current attestation adapter; it does not
need to be OpenAI.

The example demonstrates the same policy at both context boundaries:

- `DENY_THIS` is rejected before Pi adds a user message or tool result to its
live context.
- `REDACT_THIS` becomes `[REDACTED]` before Pi adds or sends it.

The redaction case makes one real request to your configured endpoint and may
incur charges from that provider.

## Before you start

Use these matching fork branches:

- [Pi `johnny/before-user-message-commit`](https://github.com/johnnygreco/pi/tree/johnny/before-user-message-commit)
- [OpenShell `openshell/pi-egress-admission`](https://github.com/johnnygreco/OpenShell/tree/openshell/pi-egress-admission)
- [OpenShell Research integration branch](https://github.com/NVIDIA/OpenShell-Research/tree/johnny/pi-attested-admission)

You do not need to clone the Pi or OpenShell forks manually. The first
`./demo.sh prepare` clones both into the ignored local workspace
`projects/egress-gate/.workspaces/pi-attested-admission/`. Later runs update
them with fast-forward-only pulls, so the fork contents never appear as
OpenShell Research changes. To reuse a checkout elsewhere, set `PI_REPO` or
`OPENSHELL_REPO` to its absolute path.

The OpenShell gateway needs a running compute backend. On macOS, start Docker
Desktop and wait until `docker info` succeeds before running the gateway;
Podman is also supported. Building the gateway also requires Z3 (`brew install
z3` on macOS or `libz3-dev` on Debian and Ubuntu). The fork recommends `mise`
2026.4.25 or newer.

From the `OpenShell-Research` checkout, change to the example directory. Run
all remaining commands there:

```shell
cd projects/egress-gate/examples/pi-attested-admission
```

Create the local configuration file, replace every example value, and load it
into the current shell:

```shell
cp .env.example .env
# Edit .env before continuing.
set -a
source .env
set +a
```

If the model endpoint does not require authentication, set
`PI_MODEL_API_KEY=unused`. Source `.env` again in each new terminal that runs
`demo.sh`.

`EGRESS_GATE_HOST_IP` is the address OpenShell uses to reach Egress Gate on this
machine. It must be a reachable, non-loopback IPv4 address; do not use
`127.0.0.1`. `PI_MODEL_BASE_URL` is separate: it is the model endpoint Pi will
call. A model server running on this machine must likewise use a hostname or
address reachable from the sandbox rather than `localhost`.

`demo.sh prepare` derives the endpoint policy and Pi model configuration from
these values. You do not need to edit `policy.yaml`. If required values are
missing or still contain placeholders, the script prints the configuration
steps and stops before performing any work.

Preview the complete workflow before running anything:

```shell
./demo.sh --print all
```

The walkthrough lists the terminal sequence and configuration visible to the
current shell. To inspect the exact commands for one action, use its name—for
example, `./demo.sh --print prepare` or `./demo.sh --print launch`.

## Run the example

Prepare the forks, build Pi, generate the endpoint-specific runtime
configuration, and generate the Egress Gate registration used by Terminal 2:

```shell
./demo.sh prepare
```

The updates use fast-forward-only pulls and stop instead of merging divergent
local work.

Keep Egress Gate running in one terminal:

```shell title="Terminal 1: Egress Gate"
./demo.sh serve
```

Start the matching OpenShell gateway in a second terminal:

```shell title="Terminal 2: OpenShell gateway"
./demo.sh gateway
```

The example uses its own gateway name and passes it explicitly to every
OpenShell command. It does not depend on or change your globally selected
OpenShell gateway.

After the gateway reports that it is ready, launch Pi from a third terminal:

```shell title="Terminal 3: managed Pi"
./demo.sh launch
```

Each launch replaces the example's `pi-egress-demo` sandbox, provider, and
custom provider profile so the current Pi runtime, managed harness, policy,
endpoint, and OpenShell supervisor are used together.

The example registers an endpoint-specific provider profile using the host-side
`PI_MODEL_API_KEY`. The real credential remains in OpenShell. Pi receives only
an opaque, endpoint-bound resolver placeholder; OpenShell resolves it in the
authorization header for the configured model endpoint.

At the Pi prompt, submit both of these in the same session:

```text
Reply with exactly: DENY_THIS
```

```text
Reply with exactly: REDACT_THIS
```

The first submission is denied without starting a model request. The second
makes a request containing `[REDACTED]`.

To exercise tool-result admission without putting the marker in the user
message, ask Pi:

```text
Use bash to print the concatenation of DENY_ and THIS, then tell me the output.
```

The tool runs, but its result is replaced by Pi's protocol-safe blocked result
before it enters live context. Repeat with `REDACT_` and `THIS` to see the tool
result admitted as `[REDACTED]`.

This example deliberately uses Pi's in-memory session manager. The interactive
TUI, tools, queued messages, retries, and `/new` work normally during the run,
but the session is not written inside the sandbox and cannot be resumed after
Pi exits. That is the minimal isolation guarantee: unadmitted context cannot be
recovered from a workload-owned session file.

## How it works

1. `managed-pi.ts` creates the regular Pi `InteractiveMode` with a mandatory SDK
`ContextAdmission` boundary and an in-memory session manager. It disables
dynamically loaded extensions, so project or user extensions cannot replace
this boundary.
2. Pi calls that boundary for each rendered user message and finalized tool
result before it queues, appends, or persists the value.
3. The adapter sends the exact context addition to OpenShell's sandbox-local
bridge. Egress Gate applies `policy.yaml` and returns allow, deny, or a
complete replacement.
4. OpenShell keeps the signed attestation and gives Pi only an opaque handle.
The adapter keeps handles in its private closure, outside Pi messages.
5. For each provider request or retry, Pi passes the exact outbound context to
the adapter. It selects the handle for the newest admitted user message or
tool result in that context.
6. OpenShell strips the handle, resolves the supervisor-held attestation, and
supplies it only to the configured Egress Gate middleware stage. Egress Gate
verifies the latest context addition and scans the complete provider request
before OpenShell resolves the model credential.

## Current scope

The attestation adapter supports normal text turns, text tool results, queued
steering and follow-up messages, retries, and automatic model continuations,
using the OpenAI Chat Completions wire format. Providers with a different native
protocol and image inputs are not covered by this example and fail closed.

## Cleanup

Exit Pi, but leave the OpenShell gateway running while cleanup deletes the
sandbox and provider:

```shell
./demo.sh cleanup
```

Then stop the OpenShell gateway and Egress Gate with `Ctrl-C`. To run the
example again, start from `./demo.sh prepare`.
Loading
Loading