Skip to content

[Proposal] OpenShell integration for restricted model routing #1175

Open

Description

@afourniernv

Related Switchyard design: #1164. This issue covers how OpenShell supplies the decision and controls model execution; #1164 owns the Rust API and approved-model behavior.

Background

A bank's assistant starts with public research, then reads confidential customer notes, contracts and negotiated pricing. Before those records reach a model, subsequent model calls must use the bank's approved private providers.

An administrator identifies sensitive sources in OpenShell policy. No content detector is required for this version. The agent sends ordinary requests; it cannot supply or clear the saved restriction.

Goals

  • Save the sandbox restriction before allowing access to a sensitive source. If saving fails, block access.
  • Pass that saved decision and the verified sandbox identity into Switchyard.
  • Keep normal routing within approved models, including judges, retries, answers, fallbacks and auxiliary calls.
  • Keep provider authorization, credentials and model calls under OpenShell's supervisor. Block direct provider bypasses.

Proposal

  1. An administrator marks accounts.internal.bank as sensitive.
  2. When the agent accesses that host, OpenShell calls the registered middleware with the matched policy and authenticated sandbox identity.
  3. The middleware records the sandbox as restricted before allowing source access. Public reads never clear the restriction.
  4. On later inference, the saved restriction reaches Switchyard's configured route. Stage chooses only among approved private models, as described in [Proposal] Target eligibility from trusted application requirements #1164. Requests before the restriction can use the normal models.
  5. The same supervisor authorizes the selected provider, applies credentials and makes the model call. All routing-time calls and fallbacks must follow that supervised path.

Steps 4–5 require a new routing/execution connection. They are not supplied by a YAML toggle, an existing privacy flag or Route::decide(...) alone.

flowchart TD
    A["Agent inside sandbox<br/>ordinary source and model requests"] --> O["OpenShell supervisor<br/>matched policy + verified sandbox identity"]
    P["Administrator policy<br/>sensitive account source"] -.-> O
    O -->|"Before sensitive source access"| M["Trusted middleware<br/>save restriction before allowing the read"]
    O -->|"Later model request"| R["Switchyard<br/>saved decision + configured router"]
    M -.->|"Saved sandbox restriction"| R
    R -->|"Approved model choice"| E["Same supervisor<br/>authorize provider + credentials + model calls"]
    E -->|"Standard"| S["Normal models"]
    E -->|"Restricted"| T["Bank-approved private models"]
    classDef supervisor fill:#e8f0fa,stroke:#5179a5,color:#1b2938;
    classDef approved fill:#e9f5d6,stroke:#76b900,color:#1b2938;
    class O,E supervisor;
    class T approved;
Loading

This diagram is the proposed supervisor-executed path, not a completed integration. Response inspection remains in OpenShell. Switchyard selects within the permitted models; it does not authorize network access.

Source Policy

Illustrative attachment to a registered service implementing mark_restricted:

network_middlewares:
  customer_accounts:
    middleware: switchyard
    order: 10
    on_error: fail_closed
    config:
      action: mark_restricted
    endpoints:
      include: [accounts.internal.bank]

mark_restricted is configuration interpreted by our service, not a built-in OpenShell privacy flag. The registered Rust service implements the middleware gRPC API. It validates the supervisor's identity token and checks the sandbox identity before accepting the action.

Network permissions separately allow the intended source/model traffic and block alternative provider paths. This source rule does not detect pasted sensitive text or unrelated local files. A resume retrieved from a marked source is covered; a pasted resume is not automatically covered.

Switchyard Connection

The trusted Rust caller uses Switchyard's existing normalized request plus the draft restriction API:

request.metadata.get_or_insert_default().task_id = Some(sandbox_id);
require_restricted_targets(&mut request, restricted)?;

sandbox_id is verified OpenShell identity, not agent-supplied JSON. restricted is saved policy state, not a model prediction. Switchyard's TOML, types and public routing/execution methods are documented in #1164 rather than duplicated here.

The current service calls Route::execute(...) and makes provider calls itself. The proposal instead needs a supported connection that preserves request changes and approved model choices while having the supervisor make every model call. Returning only a model name does not provide provider authorization, credentials, translation or fallback execution.

Current Code and Validation

Draft #1169 adds a Rust service under examples/openshell. mark_restricted saves the restriction before allowing source access; execute runs the configured route and returns a buffered answer. The service makes provider calls itself, outside the sandbox supervisor's controls. Its outbound connections and credentials require separate protection.

It requires the unpublished OpenShell buffered-response change; it does not work with released OpenShell. This is not the proposed supervisor-executed path.

  • Three current Rust gRPC tests use OpenAI/Anthropic HTTP fixtures, not real models or an OpenShell sandbox. They cover policy/authentication, saved state, translation, private fallback and failure behavior.
  • An earlier real-sandbox test used synthetic account data and fixture providers. Eight model calls were audited; no confidential payload reached the normal provider fixture. That test was not rerun on the current PR revision.
  • Local-model inference passed separately. A successful real cloud-to-private run remains unverified. Current example coverage is buffered Chat with judge-free Stage/Passthrough, not streaming, judges or auxiliary operations. The latest full-suite rerun did not finish because local disk space was exhausted.

Remaining Work and Limits

  • Define how the supervisor supplies verified policy, saved restrictions and approved provider choices to Switchyard, then executes every model call it requests.
  • Connect request/response translation, routing judges, retries, answer fallbacks and token-counting/compaction to that supervised execution path.
  • Test restriction before source release, sandbox isolation, cancellation, bypass prevention and private-only fallback. Then complete the Pi-in-OpenShell demonstration.

The current service saves restrictions in a local journal across restarts; Runner's own task memory is in-process. Neither provides shared state across replicas or an expiry/reset mechanism. Already-dispatched calls cannot be recalled when a later source read restricts the sandbox. Any supervisor API/protocol additions need separate review.

Feedback requested on where saved restrictions belong and how Switchyard model calls stay under supervisor authorization and execution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions