OpenShell policy compatibility
Source: NVIDIA OpenShell, Apache-2.0, policy schema reference, read at commit
71440b28f48d5fefc569d779f70d6e63893aa80a (2026-10-01):
https://github.com/NVIDIA/OpenShell/blob/71440b28f48d5fefc569d779f70d6e63893aa80a/docs/how-it-works/policies/schema.mdx
Pinned deliberately. A main URL moves under this document, and a branch moving
is how a compatibility claim quietly stops describing the thing it describes.
The path docs/ is 404 at this commit and was not
used; earlier drafts of this file that cited it were wrong.
No OpenShell code is copied into AIec. This document records what its schema means for a Guard policy, field by field, because "we support OpenShell policies" is not a claim that can be made honestly.
Directly expressible#
| OpenShell | Guard |
|---|---|
version: |
version: |
endpoint host, port, ports |
exact host and single port |
protocol: |
protocol: |
access: |
allowed_methods: |
access: |
allowed_methods including POST, PUT, PATCH |
access: |
no method restriction |
REST rules[].allow.method |
allowed_methods |
REST rules[].allow.path |
allowed_paths (segment-prefix match) |
enforcement: |
always: a rule either permits or refuses |
tls: |
destination-only rules; see limitations |
allowed_ips |
not accepted; Guard resolves and checks the actual address |
network_policies.<name> |
network.egress entries, keyed by host and port |
enforcement: has no equivalent, deliberately. A Guard rule that cannot
be enforced is refused rather than logged and allowed, because the audit mode's
purpose - observing what a policy would block - is only meaningful while
something else blocks it.
Not expressible, and refused rather than approximated#
Binary-scoped rules. Every OpenShell endpoint may carry a binaries list,
and the semantics are "the executable that opens the connection, or any of its
parent processes", with the executable's hash recorded on first use. A gateway
that runs outside the guest cannot establish which executable opened a
connection: a compromised guest controls what it reports. Guard's rules are
therefore per-sandbox, not per-binary, and a binaries clause is refused. A
policy that depends on binary scoping for its security property does not
convert.
Filesystem, Landlock and process identity. filesystem_policy,
landlock.compatibility, process.run_as_user and process.run_as_group are
enforced by OpenShell inside the sandbox. They have no meaning to a network
gateway, and Guard's host-side gateway cannot grant them. Note that OpenShell
itself rejects root for run_as_user; that is a property of their policy
schema, not something Guard reproduces.
Credential binding and signing. credential_binding.provider,
request_body_credential_rewrite, websocket_credential_rewrite,
allow_uninspected_credentials, credential_signing, signing_service and
signing_region describe provider-profile integration. Guard substitutes a
single bound credential on the model path and has no provider profile, body
rewriting, WebSocket credential rewriting or AWS request signing. These fields
are refused.
DNS policy. OpenShell's DNS controls are binary-scoped as well, so the same
argument applies. Guard's DNS is a strict subset by design: exact configured
names, A and AAAA only, no recursion into unknown zones, and
allowed_record_types that cannot name ANY, NS, TXT or NULL.
Middleware. network_middlewares, including on_error:, run
in-process on inspected traffic. Guard has no in-process middleware chain, so a
middleware stanza is refused.
GraphQL, MCP and JSON-RPC inspection. OpenShell inspects request bodies
against per-revision rules: GraphQL operation types and field globs, MCP tool
names and method availability per protocol revision, JSON-RPC method names.
Guard now governs the same three shapes for traffic it can see, with its own
bounds rather than OpenShell's: l7.graphql decides query versus mutation,
operation name and root fields; l7.mcp decides JSON-RPC method and tool name
for tools/; an unlisted method or tool is denied. What Guard does not do
is what OpenShell's revisioned schemas do: there is no per-revision rule table,
and a document that names a revision Guard does not model is refused by name
rather than approximated.
Two limits follow from where enforcement lives, and are stated rather than
papered over. Guard's layer 7 rules apply only where it can see the request,
which in the default SNI mode means it cannot: method and path policy needs
mode: and the operator's intercept_ack, because interception means
Guard holds a key that can read the traffic it governs. And a CONNECT tunnel to
a host the layer 7 policy governs is refused rather than forwarded ungoverned,
because a gateway that cannot read a tunnel cannot claim to have governed it.
Import behaviour#
aiec-guard-import-openshell converts a document and prints a JSON report
naming every field it converted, every field it could not, and why. It writes
no policy file on refusal, and it refuses two shapes outright rather than
approximating them: an endpoint set whose members need different body rules -
which Guard's per-attachment layer 7 scope cannot hold - and any construct whose
security property would be lost in translation.
aiec-guard-import-openshell --input policy.yaml --out guard.yaml --l7-out l7.yaml
A conversion that silently dropped a binaries clause would hand the operator
a policy that looks equivalent and is strictly weaker, so it does not exist:
the field is named in the report instead.
The honest summary#
Guard and OpenShell agree on destination-level, default-deny, operator-bounded network policy, and on refusing what cannot be enforced. They do not overlap on in-sandbox enforcement, and a policy is not portable between them in either direction without a report.