> **🏗️ build-from-issue-agent** Closes #79 ## Summary Replaces the gateway-proxied inference model (`ProxyInference` RPC) with sandbox-local execution. The sandbox now resolves routes from either a standalone YAML route file or a cluster bundle fetched via the new `GetSandboxInferenceBundle` RPC, then forwards requests directly to inference backends using `navigator-router`. ### Benefits of removing the gRPC hop Moving inference execution from the gateway to the sandbox eliminates the gRPC round-trip for every inference request: - **No payload size limits**: Direct HTTP forwarding replaces protobuf-over-gRPC serialization. - **Streaming-ready**: Preserves connection semantics for future SSE support (e.g., `stream: true`). - **Lower latency**: Direct sandbox → backend path instead of sandbox → gateway → backend round-trip. - **Reduced gateway load**: Gateway only serves the control-plane bundle delivery RPC. - **Simpler error model**: HTTP status codes flow through directly without gRPC wrapping. ## Changes Made - **Proto**: Removed `ProxyInference` RPC. Added `GetSandboxInferenceBundle` RPC for route bundle delivery. - **Gateway**: Replaced `proxy_inference` with `get_sandbox_inference_bundle`. Removed `Router` and `navigator-router` dependency — gateway is now control-plane only for inference. - **Router**: Switched config file format from TOML to YAML. Added `Debug` impl that redacts API keys. - **Sandbox**: New `InferenceContext` with local `Router` + route cache. Routes loaded from `--inference-routes` file (standalone) or cluster bundle via gRPC, with 30s background refresh. New `--inference-routes` / `NAVIGATOR_INFERENCE_ROUTES` CLI arg. - **Dev sandbox**: Added `inference-routes.yaml` at repo root with default NVIDIA NIM route. `mise run sandbox` now mounts it automatically and supports `-e` flag to forward env vars (e.g., `mise run sandbox`). - **Example**: Added standalone example (`examples/inference/routes.yaml`) and rewrote README to cover both standalone and cluster workflows. - **E2E tests**: Updated existing test names/docs. Added tests for Anthropic messages protocol and multi-route policy filtering. ## Tests Added - **Unit (21 new):** `router_error_to_http` (6 variants), `load_from_file` YAML round-trip (5), `resolve_sandbox_inference_bundle` gRPC handler (6), `build_inference_context` route loading (4) - **E2E (2 new):** Anthropic messages protocol routing, route filtering by `allowed_routes` - **Total:** 188 unit tests passing across 3 crates ## Documentation Updated - `architecture/gateway.md`: Removed router, updated Inference Service - `architecture/sandbox.md`: Updated orchestration flow, InferenceContext, route loading - `architecture/inference-routing.md`: Complete rewrite for sandbox-local architecture ## Verification - [x] All unit tests passing (188 across 3 crates) - [x] Pre-commit Rust checks passing (fmt, clippy, tests) - [x] Architecture documentation updated - [x] E2E tests updated with new test cases - [x] Standalone example and documentation added - [x] Default inference-routes.yaml with env passthrough for mise run sandbox
Inference Routing Example
This example demonstrates Navigator's inference interception and routing. A sandbox process makes standard API calls (OpenAI, Anthropic, etc.) and Navigator transparently intercepts, enforces policy, and reroutes them to a configured backend — without any code changes in the sandboxed application.
How It Works
- The sandbox proxy intercepts outgoing HTTPS connections.
- OPA policy determines the action: if the binary has no explicit network policy but inference routing is configured, the connection is inspected.
- The proxy TLS-terminates, parses the HTTP request, and detects known
inference patterns (e.g.,
POST /v1/chat/completions). - Matching requests are forwarded directly to the policy-allowed inference backend via the sandbox's local router. Non-inference requests are denied.
Files
| File | Description |
|---|---|
inference.py |
Python script that calls the OpenAI SDK — works unmodified inside a sandbox |
sandbox-policy.yaml |
Sandbox policy with inference routing enabled (route hint: local) |
routes.yaml |
Example YAML route file for standalone (no-cluster) mode |
Quick Start
There are two ways to run inference routing: with a cluster (managed routes, multi-sandbox) or standalone (single sandbox, routes from a file).
Standalone (no cluster)
Run the sandbox binary directly with a route file — no Navigator cluster needed:
# 1. Edit routes.yaml to point at your local LLM (e.g. LM Studio on :1234)
# See examples/inference/routes.yaml
# 2. Run the sandbox with --inference-routes
navigator-sandbox \
--inference-routes examples/inference/routes.yaml \
--policy-rules <your-policy.rego> \
--policy-data examples/inference/sandbox-policy.yaml \
-- python examples/inference/inference.py
The sandbox loads routes from the YAML file at startup and routes inference requests locally — no gRPC server or cluster required.
With a cluster
1. Start a Navigator cluster
mise run cluster
navigator cluster status
2. Create an inference route
Point the route at any OpenAI-compatible endpoint (local or remote):
# Local model (e.g., LM Studio, Ollama, vLLM)
navigator inference create \
--routing-hint local \
--base-url http://<HOST>:<PORT> \
--model-id <MODEL_NAME>
# Remote provider (e.g., OpenAI, NVIDIA NIM)
navigator inference create \
--routing-hint local \
--base-url https://api.openai.com \
--api-key sk-... \
--model-id gpt-4o-mini
If --protocol is omitted, Navigator auto-detects supported protocols by
probing the endpoint (sends minimal requests with max_tokens: 1).
Verify the route:
navigator inference list
3. Run the example inside a sandbox
navigator sandbox create \
--policy examples/inference/sandbox-policy.yaml \
--keep \
--name inference-demo \
-- python examples/inference/inference.py
The script targets https://api.openai.com by default, but Navigator
intercepts the connection and routes it to whatever backend the local
route points at.
Expected output:
model=<backend model name>
content=NAV_OK
4. (Optional) Interactive session
navigator sandbox connect inference-demo
# Inside the sandbox:
python examples/inference/inference.py
5. Cleanup
navigator sandbox delete inference-demo
navigator inference delete <route-name>
Customizing the Policy
Edit sandbox-policy.yaml to control which routes are available:
inference:
allowed_routes:
- local # matches the --routing-hint used in step 2
- production # add more route hints as needed
The allowed_routes list determines which inference routes a sandbox can
use. Routes are matched by their routing_hint field.
Supported Protocols
Navigator detects and routes the following inference API patterns:
| Pattern | Protocol | Kind |
|---|---|---|
POST /v1/chat/completions |
openai_chat_completions |
Chat completion |
POST /v1/completions |
openai_completions |
Text completion |
POST /v1/responses |
openai_responses |
Responses API |
POST /v1/messages |
anthropic_messages |
Anthropic messages |