Repository navigation
feat: agent-readable policy manifest for tool/endpoint discovery #1386
Description
Problem Statement
Agents inside OpenShell sandboxes have no way to discover what the network policy allows before making requests. Today, discovery is purely trial-and-error: the agent makes an HTTP request, the proxy returns a 403 with {"error":"policy_denied","detail":"...not permitted by policy"}, and the agent adjusts and retries. This has two costs:
- Wasted tokens and API spend: Each denied request consumes context window and inference cost with no useful outcome.
- Inability to plan: The agent cannot reason upfront about which workflows are feasible. It can't answer "can I create a pull request?" without actually attempting it and potentially failing.
The network policy already contains all the information an agent would need — allowed endpoints, methods, path patterns, and access presets. But this information is only available to the supervisor and proxy, not to the agent.
Proposed Design
The supervisor generates a structured YAML file at /etc/openshell/agent-manifest.yaml (read-only to the agent) that projects the effective network policy into an agent-consumable format. The file contains only network access rules — filesystem and process policy are omitted since they don't inform the agent's API call planning.
Schema:
version: 1
network:
- name: github-api
description: "GitHub REST API for repository operations"
endpoints:
- host: api.github.com
port: 443
protocol: rest
access: read-write
rules:
- methods: [GET]
paths: ["/repos/**"]
- methods: [POST]
paths: ["/repos/*/pulls"]
binaries: [/usr/bin/curl, /usr/bin/gh]
- name: pypi
description: "Python package index"
endpoints:
- host: pypi.org
port: 443
access: read-onlyKey design decisions:
- Separate file, not the policy itself: The manifest is derived from the policy but restructured for agent consumption. The policy YAML is supervisor-oriented; the manifest is agent-oriented.
- Optional
descriptionfield on policy rules: The policy schema gains an optionaldescriptionstring on network rules (alongside the existingname). Policy authors can use it to explain what access is for. It flows through to the manifest. If omitted, it defaults to empty. - Groups by policy rule, not by endpoint: Matches how policy authors think about access grants.
- Includes binary restrictions: Agents need to know which tool (curl, gh, python) can reach a given endpoint.
- Omits deny rules: Everything not listed is implicitly denied — no need to enumerate what's blocked.
- Omits internal fields: TLS mode, enforcement mode, SSRF allowlists, credential rewriting flags, and other proxy-internal concerns are not included.
Lifecycle:
- Supervisor loads the policy (from disk or gRPC).
- Supervisor generates
agent-manifest.yamlat the well-known path before launching the agent process. - On policy hot-reload, the supervisor regenerates the file atomically (write to temp, rename).
- No notification mechanism — the agent reads the file on its own schedule (at startup, before critical operations, etc.).
This follows the existing pattern of the supervisor writing read-only agent-facing files (e.g., /etc/openshell/skills/policy_advisor.md).
Alternatives Considered
-
Local query API (supervisor exposes a loopback endpoint for "can I do X?" pre-flight checks): More dynamic but adds protocol complexity. Agents would need to know a query API rather than just reading a file. Could be a future enhancement on top of the manifest.
-
Enhanced 403 responses (denied responses include what is allowed for that destination): Enriches the trial-and-error loop but doesn't solve the planning problem — agents still have to attempt requests to learn what's available.
-
Policy file as the manifest (agents read
/etc/openshell/policy.yamldirectly): The policy contains information irrelevant to agents (filesystem rules, process restrictions, TLS config, SSRF allowlists) and is structured for the supervisor, not for agent reasoning. A separate projection is cleaner. -
Tool definitions generated from policy (policy generates MCP-style tool schemas): Rejected because the policy doesn't know API semantics — it knows access rules (methods, paths) but not what those endpoints do, their input schemas, or their descriptions. Generating tool definitions would cross the responsibility boundary.
Agent Investigation
- Network policy rules already have a
namefield (crates/openshell-policy/src/lib.rs,serde(default)) but nodescriptionfield. - The supervisor already writes agent-facing read-only files to
/etc/openshell/skills/(seecrates/openshell-sandbox/src/skills.rs,install_static_skills()). - Policy hot-reload is already handled by the supervisor via config revision polling (
GetSandboxConfig). The manifest regeneration would hook into this existing mechanism. - The 403 response is constructed by
build_json_error_response()incrates/openshell-sandbox/src/proxy.rs— it returns{"error":"policy_denied","detail":"..."}with no information about what is allowed.
Activity
johntmyers commentedon May 21, 2026
Check out https://nvidia-preview-pr-1480.docs.buildwithfern.com/openshell/sandboxes/policy-advisor (the feature is available now but the docs need to merge still). Most of your alternatives are actually available. Writing a YAML to disk would be problematic because with Providers v2 the effective running policy is composed JIT.
I'm still not sure I understand the use case behind this and what an agent would do with it? Our approach to solve this is with the policy advisor. I'm going to close this for now but we can re-visit if a more concrete use case arises.