Skip to navigation

Add APF to an OpenShell Gateway

View as Markdown

APF governs an OpenShell deployment through two OpenShell extension points. You register both in the OpenShell gateway configuration and restart the gateway. You do not change how sandboxes are created, and you do not rewrite your existing policy. You move it into a signed bundle.

On Kubernetes, the OpenShell chart renders the gateway configuration from its values, so the registrations below go into a gatewayConfig values overlay rather than a TOML file. Applying the overlay with helm upgrade rolls the gateway pod, so there is nothing to edit or restart by hand. Refer to Register APF through the OpenShell chart.

Extension pointAPF surfaceDefault address in the dev stackRegistration name used in this guide
Gateway interceptorapf-policy-gateway --openshell-interceptor-addr127.0.0.1:18202apf-policy-adapter
Supervisor middlewareapf-policy-gateway --openshell-middleware-addr127.0.0.1:9099apf-content-policy

The interceptor puts sandbox admission and policy changes under APF. The middleware adds per-request content and context decisions. To run with the interceptor alone, remove the network_middlewares entry from your bundle’s sandbox policy, because OpenShell refuses to create a sandbox whose policy selects a middleware that is not registered.

Before You Begin

  • APF is running. Refer to Installation.
  • You can edit the OpenShell gateway TOML and restart the gateway. Refer to OpenShell gateway configuration.
  • Both APF listeners are reachable from the OpenShell gateway. The middleware listener must also be reachable from every sandbox supervisor.
1

Move Your Existing Policy into a Bundle

If you already run OpenShell with your own sandbox policy, put that policy into the bundle APF serves. The demo bundle in examples/demo-bundle/ shows the layout.

  1. Translate your OpenShell policy into an APF SandboxPolicy in sandbox/default-policy.yaml. Set apiVersion: nvidia.agent-policy-fabric.sandbox/v1alpha1, kind: SandboxPolicy, and metadata.name: default. Put filesystem, landlock, process, network_policies, and network_middlewares under spec, rename OpenShell’s filesystem_policy to filesystem, and drop the top-level version. The interceptor injects the SandboxPolicy named default unless you start the gateway with a different --document-name.
  2. Put each provider your sandboxes may use under provider-profiles/ as a kind: Provider document.
  3. List every file in the bundle template’s manifest and sign the bundle.

Refer to Your First Signed Policy for the signing steps and Sandbox Policies for the document format.

2

Register the Gateway Interceptor

Add an interceptor registration to the OpenShell gateway TOML. For local development with APF’s insecure listener, use plaintext and opt out of extension authentication on both sides:

[[openshell.gateway.interceptors]]
name = "apf-policy-adapter"
grpc_endpoint = "http://127.0.0.1:18202"
order = 0
failure_policy = "fail_closed"
allow_insecure_transport = true

For an authenticated deployment, remove allow_insecure_transport, point grpc_endpoint at APF’s HTTPS listener, and give OpenShell the CA that issued APF’s listener certificate:

[[openshell.gateway.interceptors]]
name = "apf-policy-adapter"
grpc_endpoint = "https://apf.example.internal:18202"
tls_ca_cert_path = "/etc/openshell/apf-ca.pem"
order = 0
failure_policy = "fail_closed"

APF’s manifest declares its own failure policy for every binding: fail-closed for each admission and governance binding, and fail-open for its two post-commit bindings. With OpenShell’s default binding_policy = "dynamic", those per-binding values take precedence, so if APF is unreachable OpenShell refuses governed operations instead of running them ungoverned. The registration’s failure_policy applies only to bindings that declare none, and setting it to fail_closed documents the intent.

Keep the default binding_policy = "dynamic".

APF does not work with OpenShell’s allowlist or exact binding policies in this release. APF declares three bindings each on CreateSandbox and UpdateConfig, and the strict policies accept only one binding per configured RPC. With exact, the OpenShell gateway does not start. With allowlist, you would have to leave sandbox admission and refresh out of the allowlist, which leaves them ungoverned. Refer to Gateway Interceptor for the RPCs and phases APF declares.

3

Register the Supervisor Middleware

Add a middleware registration. The name must be apf-content-policy, because the sandbox policy selects the middleware by that name. Register it before you admit sandboxes under a bundle that selects it.

[[openshell.supervisor.middleware]]
name = "apf-content-policy"
grpc_endpoint = "http://127.0.0.1:9099"
max_payload_bytes = 4194304
allow_insecure_transport = true

The 127.0.0.1 endpoint works only when sandbox supervisors share the host’s network, as with OpenShell’s Docker driver on Linux. With other setups, such as Docker Desktop, Podman on macOS, a microVM, or Kubernetes, bind APF’s middleware listener to an address the supervisors can reach and register that address, for example http://host.openshell.internal:9099. If supervisors cannot reach APF, every request to a host routed to APF fails closed and APF writes no record.

max_payload_bytes is required by OpenShell and must not exceed the 4 MiB APF advertises. For an authenticated deployment, use an https:// endpoint, add tls_ca_cert_path, and remove allow_insecure_transport, as for the interceptor. OpenShell’s default evaluation timeout is 500ms. Refer to OpenShell middleware configuration.

4

Select the Middleware in Sandbox Policy

OpenShell runs a middleware only for destinations that the sandbox policy selects. Add a network_middlewares entry under spec in the SandboxPolicy in your bundle. The demo bundle selects APF for four hosts:

spec:
network_middlewares:
apf_content_policy:
name: APF content policy
middleware: apf-content-policy
order: 10
on_error: fail_closed
endpoints:
include:
- "httpbin.org"
- "api.github.com"
- "api.openai.com"
- "host.openshell.internal"

APF’s middleware takes no configuration, and an APF SandboxPolicy has no field to give it one. If a policy reaches OpenShell with a non-empty config for it, APF’s ValidateConfig marks the policy invalid, and APF denies any request evaluated with one.

5

Make APF the Provider Profile Source

The APF interceptor can vend the provider profiles from the signed bundle. To make the bundle the authoritative provider catalog, select the interceptor as the only profile source in the OpenShell gateway TOML. If your file already has an [openshell.gateway] table, add provider_profile_sources to it rather than repeating the table header:

[openshell.gateway]
provider_profile_sources = [
{ type = "interceptor", name = "apf-policy-adapter" },
]

Independently, when you start apf-policy-gateway with --openshell-gateway-url, it reconciles the bundle’s provider profiles into the OpenShell gateway at startup and whenever the served policy set changes. APF denies imports and updates of profiles the bundle does not manage or whose content differs from the bundle, and it denies every profile deletion.

6

Turn Off Agent Policy Proposals

On any host where APF governs sandbox policy, OpenShell’s agent-driven policy proposals must stay off. Otherwise the effective policy is whatever OpenShell accepted most recently, not what was signed. Set it explicitly at global scope. --yes confirms the global change in a non-interactive shell:

openshell settings set --global --key agent_policy_proposals_enabled --value false --yes
openshell settings get --global

APF also refuses agent-authored proposals and every draft approval at the interceptor, but the setting is the first line of defense. Re-check it as part of every deployment review.

7

Restart the OpenShell Gateway

Registrations are read at startup. Restart the OpenShell gateway so it calls APF’s Describe, negotiates the extension protocol, and builds its interceptor plan. If APF is unreachable or its protocol does not match, the OpenShell gateway does not start.

Verify the Registration

Create a sandbox and confirm APF admitted it. The policy gateway’s interceptor audit file records an injection and a validation for the sandbox:

grep -E '"event_type":"(policy_injection|admission_validation)"' \
"$APF_AUDIT_LOG_DIR/apf-policy-gateway-interceptor-audit.jsonl"

Each record carries "outcome":"success" with the reason codes interceptor.policy_injected and interceptor.validate_allowed. Then try a direct policy edit on the sandbox. APF refuses it with direct policy mutation not permitted; policy is managed by APF.

Next Steps