From 16617fa28c50fa3b7ae6cdb6e14620bec6f9c59e Mon Sep 17 00:00:00 2001
From: David Karlsson <35727626+dvdksn@users.noreply.github.com>
Date: Mon, 25 May 2026 16:45:16 +0200
Subject: [PATCH 1/9] sbx: restructure governance docs and add governance API
reference
Splits the existing security/governance and security/policy pages into a
new governance section with concepts, local, organization, monitoring,
and API reference sub-pages, and adds a custom api-reference Hugo layout
that renders the colocated OpenAPI 3 spec directly from the spec file.
The layout reads the spec via transform.Unmarshal, resolves $ref nodes
through a small returning partial, and overrides baseof's main block to
take the full content width. The built-in right-rail TOC is replaced
with a sticky endpoint navigator that groups operations by tag with
method-colored pills.
Each operation card shows method/path, summary, description, parameters,
request body, and responses. Responses use native elements so
the list of status codes stays scannable; 2xx defaults to open. JSON
examples are wrapped in the site's syntax-light/dark utility so Chroma
classes pick up the theme. Each operation also exposes a copy-as-cURL
button that assembles a ready-to-paste command from the spec's path and
query parameter examples, the bearer-auth scheme (with $TOKEN as a
literal placeholder), and the request body's default JSON example.
A companion api-reference.markdown.md template renders the same spec as
a clean markdown document so the page's "Copy Markdown" / "View
Markdown" actions and the .md alternate link return real content
instead of just the page title.
Co-Authored-By: Claude Sonnet 4.6
Co-Authored-By: Claude Opus 4.7
---
content/manuals/ai/sandboxes/_index.md | 2 +-
content/manuals/ai/sandboxes/architecture.md | 2 +-
content/manuals/ai/sandboxes/faq.md | 6 +-
content/manuals/ai/sandboxes/get-started.md | 6 +-
.../manuals/ai/sandboxes/governance/_index.md | 39 +
.../ai/sandboxes/governance/api/api.yaml | 759 ++++++++++++++++++
.../ai/sandboxes/governance/api/index.md | 8 +
.../ai/sandboxes/governance/concepts.md | 106 +++
.../manuals/ai/sandboxes/governance/local.md | 159 ++++
.../ai/sandboxes/governance/monitoring.md | 107 +++
.../governance.md => governance/org.md} | 74 +-
.../manuals/ai/sandboxes/security/_index.md | 7 +-
.../manuals/ai/sandboxes/security/defaults.md | 2 +-
.../ai/sandboxes/security/isolation.md | 2 +-
.../manuals/ai/sandboxes/security/policy.md | 263 ------
.../manuals/ai/sandboxes/troubleshooting.md | 6 +-
content/manuals/ai/sandboxes/usage.md | 2 +-
layouts/_partials/api-ref/curl.html | 125 +++
layouts/_partials/api-ref/nav.html | 86 ++
layouts/_partials/api-ref/resolve.html | 26 +
layouts/api-reference.html | 682 ++++++++++++++++
layouts/api-reference.markdown.md | 173 ++++
.../manuals/ai/sandboxes/governance/api.yaml | 759 ++++++++++++++++++
23 files changed, 3072 insertions(+), 329 deletions(-)
create mode 100644 content/manuals/ai/sandboxes/governance/_index.md
create mode 100644 content/manuals/ai/sandboxes/governance/api/api.yaml
create mode 100644 content/manuals/ai/sandboxes/governance/api/index.md
create mode 100644 content/manuals/ai/sandboxes/governance/concepts.md
create mode 100644 content/manuals/ai/sandboxes/governance/local.md
create mode 100644 content/manuals/ai/sandboxes/governance/monitoring.md
rename content/manuals/ai/sandboxes/{security/governance.md => governance/org.md} (58%)
delete mode 100644 content/manuals/ai/sandboxes/security/policy.md
create mode 100644 layouts/_partials/api-ref/curl.html
create mode 100644 layouts/_partials/api-ref/nav.html
create mode 100644 layouts/_partials/api-ref/resolve.html
create mode 100644 layouts/api-reference.html
create mode 100644 layouts/api-reference.markdown.md
create mode 100644 static/manuals/ai/sandboxes/governance/api.yaml
diff --git a/content/manuals/ai/sandboxes/_index.md b/content/manuals/ai/sandboxes/_index.md
index 2a39b478dd10..30cfd0f773d8 100644
--- a/content/manuals/ai/sandboxes/_index.md
+++ b/content/manuals/ai/sandboxes/_index.md
@@ -14,7 +14,7 @@ build containers, install packages, and modify files without touching your host
system.
Organization admins can
-[centrally manage sandbox network and filesystem policies](security/governance.md)
+[centrally manage sandbox network and filesystem policies](governance/org.md)
from the Docker Admin Console, so the same rules apply uniformly across every
developer's machine. Available on a separate paid subscription.
diff --git a/content/manuals/ai/sandboxes/architecture.md b/content/manuals/ai/sandboxes/architecture.md
index fab768d60552..e24a1e3603c9 100644
--- a/content/manuals/ai/sandboxes/architecture.md
+++ b/content/manuals/ai/sandboxes/architecture.md
@@ -36,7 +36,7 @@ layers, and volumes, and this grows as you build images and install packages.
All outbound traffic from the sandbox routes through an HTTP/HTTPS proxy on
your host. Agents are configured to use the proxy automatically. The proxy
-enforces [network access policies](security/policy.md) and handles
+enforces [network access policies](governance/local.md) and handles
[credential injection](security/credentials.md). See
[Network isolation](security/isolation.md#network-isolation) for how this
works and [Default security posture](security/defaults.md) for what is
diff --git a/content/manuals/ai/sandboxes/faq.md b/content/manuals/ai/sandboxes/faq.md
index b8584cac88ca..d0b473253f01 100644
--- a/content/manuals/ai/sandboxes/faq.md
+++ b/content/manuals/ai/sandboxes/faq.md
@@ -14,7 +14,7 @@ Signing in gives each sandbox a verified identity, which lets Docker:
containers, install packages, and push code. Your Docker identity is the
anchor.
- **Enable team features.** Team-scale features like
- [organization governance](security/governance.md), shared environments, and
+ [organization governance](governance/org.md), shared environments, and
audit logs need a concept of "who," and adding that later would be worse for
everyone.
- **Authenticate against Docker infrastructure.** Sandboxes pull images, run
@@ -30,7 +30,7 @@ organization and take precedence over local rules set with `sbx policy`.
Admins can optionally delegate specific rule types back to local control so
developers can add additional allow rules.
-See [Organization governance](security/governance.md). This feature requires
+See [Organization governance](governance/org.md). This feature requires
a separate paid subscription —
[contact Docker Sales](https://www.docker.com/products/ai-governance/#contact-sales)
to get started.
@@ -99,7 +99,7 @@ $ echo $BRAVE_API_KEY
## Why do agents run without approval prompts?
The sandbox itself is the safety boundary. Because agents run inside an
-isolated microVM with [network policies](security/policy.md),
+isolated microVM with [network policies](governance/local.md),
[credential isolation](security/credentials.md), and no access to your host
system outside the workspace, the usual reasons for approval prompts (preventing
destructive commands, network access, file modifications) are handled by the
diff --git a/content/manuals/ai/sandboxes/get-started.md b/content/manuals/ai/sandboxes/get-started.md
index 2e1d97d380af..f178251bb8d4 100644
--- a/content/manuals/ai/sandboxes/get-started.md
+++ b/content/manuals/ai/sandboxes/get-started.md
@@ -114,7 +114,7 @@ Use ↑/↓ to navigate, Enter to select, or press 1–3.
**Balanced** is a good starting point — it permits traffic to common
development services while blocking everything else. You can adjust individual
-rules later. See [Policies](security/policy.md) for a full description of each
+rules later. See [Policies](governance/local.md) for a full description of each
option.
> [!NOTE]
@@ -233,7 +233,7 @@ $ sbx policy allow network -g registry.npmjs.org
With **Locked Down**, even your model provider API is blocked unless you
explicitly allow it. With **Balanced**, common development services are
-permitted by default. See [Policies](security/policy.md) for the full rule
+permitted by default. See [Policies](governance/local.md) for the full rule
set and how to customize it.
## Clean up
@@ -269,4 +269,4 @@ working tree are unaffected.
with kits
- [Credentials](security/credentials.md) — credential storage and management
- [Workspace trust](security/workspace.md) — review agent changes safely
-- [Policies](security/policy.md) — control outbound access
+- [Policies](governance/local.md) — control outbound access
diff --git a/content/manuals/ai/sandboxes/governance/_index.md b/content/manuals/ai/sandboxes/governance/_index.md
new file mode 100644
index 000000000000..f09e86ce1a5f
--- /dev/null
+++ b/content/manuals/ai/sandboxes/governance/_index.md
@@ -0,0 +1,39 @@
+---
+title: Governance
+weight: 55
+description: Control what sandboxes can access, from local developer rules to org-wide enforcement.
+keywords: docker sandboxes, governance, policy, network access, filesystem access, organization policy
+---
+
+Sandbox governance covers the policy system that controls what sandboxes can
+access over the network and on the filesystem. It operates at two layers that
+build on each other:
+
+**Local policy** is configured per machine using the `sbx policy` CLI. It
+lets individual developers customize which domains their sandboxes can reach.
+See [Local policy](local.md).
+
+**Organization policy** is configured centrally in the Docker Admin Console or
+via the [Governance API](api.md). Rules defined at the org level apply
+uniformly across every sandbox in the organization and take precedence over
+local rules. Admins can optionally delegate specific rule types back to local
+control so developers can extend the org policy with additional allow rules.
+See [Organization policy](org.md).
+
+> [!NOTE]
+> Organization governance is available on a separate paid subscription.
+> [Contact Docker Sales](https://www.docker.com/products/ai-governance/#contact-sales)
+> to request access.
+
+## Learn more
+
+- [Policy concepts](concepts.md): resource model, rule syntax, evaluation,
+ and precedence
+- [Local policy](local.md): configure network and filesystem rules on your
+ machine with the `sbx policy` CLI
+- [Organization policy](org.md): centrally manage sandbox policies across
+ your organization from the Admin Console
+- [Monitoring](monitoring.md): inspect active rules and monitor sandbox
+ network traffic with `sbx policy ls` and `sbx policy log`
+- [API reference](api.md): manage org policies programmatically via the
+ Governance API
diff --git a/content/manuals/ai/sandboxes/governance/api/api.yaml b/content/manuals/ai/sandboxes/governance/api/api.yaml
new file mode 100644
index 000000000000..0ef5f3dc6ed3
--- /dev/null
+++ b/content/manuals/ai/sandboxes/governance/api/api.yaml
@@ -0,0 +1,759 @@
+openapi: "3.1.0"
+
+info:
+ title: Docker AI Governance Policy API
+ version: "1"
+ description: |
+ HTTP+JSON API for managing Docker governance policies and rules.
+
+ **Resource model.** An organization owns one or more policies. Each policy
+ contains a list of rules grouped into a single domain: either `network` or
+ `filesystem`. A policy's domain is derived from its rule actions; mixing
+ domains within a single policy is not permitted.
+
+ **Lifecycle.** Create a policy with CreatePolicy, then add rules with
+ CreateRule. Rules can be updated in place with UpdateRule or removed with
+ DeleteRule. Deleting all rules does not delete the policy itself.
+
+ **Rule evaluation.** All rules in a policy are tested against every request.
+ `deny` always wins: if any rule matches with `decision: deny`, the request
+ is denied regardless of any `allow` rules.
+
+ **Enforcement and delegation.** Organization policies take precedence over
+ local sandbox policies and cannot be overridden by individual users. When
+ an administrator enables delegation for a rule type, users may supplement
+ the organization policy with local allow rules; organization-level deny
+ rules remain binding regardless of delegation. Delegation is configured
+ through the governance settings API, not through this API.
+
+ **Propagation.** Policy changes take up to five minutes to reach developer
+ machines after being written.
+
+ See https://docs.docker.com/ai/sandboxes/governance/ for product
+ documentation.
+ contact:
+ name: Docker
+ url: https://www.docker.com/products/ai-governance/
+
+tags:
+ - name: policies
+ description: Policy lifecycle management
+ - name: rules
+ description: Rule management within an allowlist policy
+
+servers:
+ - url: https://hub.docker.com/api/governance/v1
+
+security:
+ - bearerAuth: []
+
+paths:
+ /orgs/{org_name}/policies:
+ parameters:
+ - $ref: "#/components/parameters/OrgName"
+ get:
+ operationId: listPolicies
+ tags: [policies]
+ summary: List policies
+ description: >
+ Returns a shallow summary of all policies for the org.
+ The rule set is not included; use GetPolicy to fetch the full object.
+ responses:
+ "200":
+ description: Array of policy summaries. Rule sets are not included; use GetPolicy to fetch a full policy.
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/PolicySummary"
+ examples:
+ default:
+ value:
+ - id: pol_06evsmp24r1pg71cm8500546pkbn
+ name: "Security Research — hardened"
+ org: my-org
+ scope:
+ profiles: [security]
+ created_at: "2026-04-22T00:00:00Z"
+ updated_at: "2026-04-22T00:00:00Z"
+ type: allowlist_v0
+ "401":
+ $ref: "#/components/responses/Unauthenticated"
+ "403":
+ $ref: "#/components/responses/PermissionDenied"
+ "500":
+ $ref: "#/components/responses/InternalError"
+
+ post:
+ operationId: createPolicy
+ tags: [policies]
+ summary: Create policy
+ description: >
+ Creates a new policy with an empty rule set. Rules are added separately
+ via the rules sub-resource.
+ requestBody:
+ description: Policy name and optional scope.
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/CreatePolicyRequest"
+ examples:
+ default:
+ value:
+ name: "Security Research — hardened"
+ scope:
+ profiles: [security]
+ responses:
+ "201":
+ description: Policy created. Returns the new policy without its rule set.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Policy"
+ examples:
+ default:
+ value:
+ id: pol_06evsmp24r1pg71cm8500546pkbn
+ name: "Security Research — hardened"
+ org: my-org
+ scope:
+ profiles: [security]
+ created_at: "2026-04-22T00:00:00Z"
+ updated_at: "2026-04-22T00:00:00Z"
+ "400":
+ $ref: "#/components/responses/InvalidArgument"
+ "401":
+ $ref: "#/components/responses/Unauthenticated"
+ "403":
+ $ref: "#/components/responses/PermissionDenied"
+ "409":
+ $ref: "#/components/responses/Conflict"
+ "500":
+ $ref: "#/components/responses/InternalError"
+
+ /orgs/{org_name}/policies/{policy_id}:
+ parameters:
+ - $ref: "#/components/parameters/OrgName"
+ - $ref: "#/components/parameters/PolicyID"
+ get:
+ operationId: getPolicy
+ tags: [policies]
+ summary: Get policy
+ description: Returns the full policy including its `allowlist_v0` rule set.
+ responses:
+ "200":
+ description: Full policy including its `allowlist_v0` rule set.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Policy"
+ examples:
+ default:
+ value:
+ id: pol_06evsmp24r1pg71cm8500546pkbn
+ name: "Security Research — hardened"
+ org: my-org
+ scope:
+ profiles: [security]
+ created_at: "2026-04-22T00:00:00Z"
+ updated_at: "2026-04-22T00:00:00Z"
+ allowlist_v0:
+ domain: network
+ rules:
+ - id: rule_06evsm9qjm1pdsk0a8nkfaxy7jna
+ name: allow research mirrors
+ actions: [connect:tcp, connect:udp]
+ resources: [research.mitre.org, cve.mitre.org]
+ decision: allow
+ "401":
+ $ref: "#/components/responses/Unauthenticated"
+ "403":
+ $ref: "#/components/responses/PermissionDenied"
+ "404":
+ $ref: "#/components/responses/NotFound"
+ "500":
+ $ref: "#/components/responses/InternalError"
+
+ /orgs/{org_name}/policies/{policy_id}/rules:
+ parameters:
+ - $ref: "#/components/parameters/OrgName"
+ - $ref: "#/components/parameters/PolicyID"
+ post:
+ operationId: createRule
+ tags: [rules]
+ summary: Create rule
+ description: |
+ Adds a rule to the policy's rule set. All rules in a policy must share
+ the same domain (network or filesystem); mixing domains is rejected.
+
+ **Network** actions: `connect:tcp`, `connect:udp`. Resources are
+ hostnames (for example, `example.com`), wildcard subdomains (`*.example.com`
+ for one level, `**.example.com` for any depth), hostnames with an optional
+ port (for example, `example.com:443`), or CIDRs in IPv4 or IPv6 notation
+ (for example, `10.0.0.0/8` or `2001:db8::/32`).
+
+ **Filesystem** actions: `read`, `write`. Resources are paths (for example,
+ `/data`). Use `*` to match within a single path segment and `**` to match
+ recursively across segments (for example, `/data/**`).
+
+ Changes may take up to five minutes to reach developer machines.
+ requestBody:
+ description: Rule definition including actions, resources, and decision.
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/CreateRuleRequest"
+ examples:
+ network:
+ summary: Network rule
+ value:
+ name: allow research mirrors
+ actions: [connect:tcp, connect:udp]
+ resources: [research.mitre.org, cve.mitre.org]
+ decision: allow
+ filesystem:
+ summary: Filesystem rule
+ value:
+ name: allow data directory
+ actions: [read, write]
+ resources: [/data]
+ decision: allow
+ responses:
+ "201":
+ description: Rule created and added to the policy's rule set.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Rule"
+ examples:
+ network:
+ summary: Network rule
+ value:
+ id: rule_06evsm9qjm1pdsk0a8nkfaxy7jna
+ name: allow research mirrors
+ actions: [connect:tcp, connect:udp]
+ resources: [research.mitre.org, cve.mitre.org]
+ decision: allow
+ filesystem:
+ summary: Filesystem rule
+ value:
+ id: rule_07fwtnr0kn2qetl1b9olfbyz8kob
+ name: allow data directory
+ actions: [read, write]
+ resources: [/data]
+ decision: allow
+ "400":
+ $ref: "#/components/responses/InvalidArgument"
+ "401":
+ $ref: "#/components/responses/Unauthenticated"
+ "403":
+ $ref: "#/components/responses/PermissionDenied"
+ "404":
+ $ref: "#/components/responses/NotFound"
+ "500":
+ $ref: "#/components/responses/InternalError"
+
+ /orgs/{org_name}/policies/{policy_id}/rules/{rule_id}:
+ parameters:
+ - $ref: "#/components/parameters/OrgName"
+ - $ref: "#/components/parameters/PolicyID"
+ - $ref: "#/components/parameters/RuleID"
+ patch:
+ operationId: updateRule
+ tags: [rules]
+ summary: Update rule
+ description: |
+ Partially updates a rule using JSON Merge Patch semantics (RFC 7396).
+ Only fields present in the request body are updated; absent fields are
+ left unchanged. Returns the rule in both its old and new states.
+
+ Changing `actions` across domains (for example, from network actions to
+ filesystem actions) is rejected. Changes may take up to five minutes to
+ reach developer machines.
+ requestBody:
+ description: Fields to update. Absent fields are left unchanged.
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UpdateRuleRequest"
+ examples:
+ default:
+ value:
+ resources: ["research.mitre.org"]
+ responses:
+ "200":
+ description: Rule updated, returns old and new states.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UpdateRuleResponse"
+ examples:
+ default:
+ value:
+ old:
+ id: rule_06evsm9qjm1pdsk0a8nkfaxy7jna
+ name: allow research mirrors
+ actions: [connect:tcp, connect:udp]
+ resources: [research.mitre.org, cve.mitre.org]
+ decision: allow
+ new:
+ id: rule_06evsm9qjm1pdsk0a8nkfaxy7jna
+ name: allow research mirrors
+ actions: [connect:tcp, connect:udp]
+ resources: [research.mitre.org]
+ decision: allow
+ "400":
+ $ref: "#/components/responses/InvalidArgument"
+ "401":
+ $ref: "#/components/responses/Unauthenticated"
+ "403":
+ $ref: "#/components/responses/PermissionDenied"
+ "404":
+ $ref: "#/components/responses/NotFound"
+ "500":
+ $ref: "#/components/responses/InternalError"
+
+ delete:
+ operationId: deleteRule
+ tags: [rules]
+ summary: Delete rule
+ description: |
+ Deletes a rule from the policy. Returns the deleted rule. Changes may
+ take up to five minutes to reach developer machines.
+ responses:
+ "200":
+ description: Rule deleted, returns the deleted rule.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/DeleteRuleResponse"
+ examples:
+ default:
+ value:
+ deleted:
+ id: rule_06evsm9qjm1pdsk0a8nkfaxy7jna
+ name: allow research mirrors
+ actions: [connect:tcp, connect:udp]
+ resources: [research.mitre.org, cve.mitre.org]
+ decision: allow
+ "401":
+ $ref: "#/components/responses/Unauthenticated"
+ "403":
+ $ref: "#/components/responses/PermissionDenied"
+ "404":
+ $ref: "#/components/responses/NotFound"
+ "500":
+ $ref: "#/components/responses/InternalError"
+
+components:
+ securitySchemes:
+ bearerAuth:
+ type: http
+ scheme: bearer
+ bearerFormat: JWT
+ description: |
+ Short-lived JWT obtained by exchanging Docker Hub credentials at
+ `POST https://hub.docker.com/v2/users/login`. Pass the JWT in the
+ `Authorization: Bearer ` header. Tokens expire after a short
+ period; request a fresh one when you receive a `401`.
+
+ The `password` field of the login request accepts any of the following
+ credential types:
+
+ | Type | Format | Notes |
+ |------|--------|-------|
+ | Password | Plain text | Your Docker Hub account password. |
+ | Personal Access Token (PAT) | `dckr_pat_*` | Recommended over passwords. Create one under Account Settings → Security. |
+ | Organization Access Token (OAT) | `dckr_oat_*` | Scoped to an organization. Create one under Organization Settings → Access Tokens. |
+
+ Note: PAT and OAT strings cannot be used directly as a bearer token —
+ they must be exchanged at the login endpoint first.
+
+ See [Docker Hub authentication](https://docs.docker.com/reference/api/hub/latest/#tag/authentication-api/operation/AuthCreateAccessToken)
+ for full details.
+
+ parameters:
+ OrgName:
+ name: org_name
+ in: path
+ required: true
+ description: Docker Hub organization name.
+ schema:
+ type: string
+ examples:
+ default:
+ value: my-org
+
+ PolicyID:
+ name: policy_id
+ in: path
+ required: true
+ description: Unique policy identifier.
+ schema:
+ type: string
+ examples:
+ default:
+ value: pol_06evsmp24r1pg71cm8500546pkbn
+
+ RuleID:
+ name: rule_id
+ in: path
+ required: true
+ description: Unique rule identifier within the policy.
+ schema:
+ type: string
+ examples:
+ default:
+ value: rule_06evsm9qjm1pdsk0a8nkfaxy7jna
+
+ schemas:
+ PolicySummary:
+ type: object
+ description: Shallow policy representation returned by ListPolicies. Excludes the rule set.
+ required: [id, name, org, scope, created_at, updated_at, type]
+ properties:
+ id:
+ type: string
+ examples:
+ - pol_06evsmp24r1pg71cm8500546pkbn
+ name:
+ type: string
+ description: Human-readable label, unique within the organization.
+ examples:
+ - "Security Research — hardened"
+ org:
+ type: string
+ examples:
+ - my-org
+ scope:
+ $ref: "#/components/schemas/Scope"
+ created_at:
+ type: string
+ format: date-time
+ examples:
+ - "2026-04-22T00:00:00Z"
+ updated_at:
+ type: string
+ format: date-time
+ examples:
+ - "2026-04-22T00:00:00Z"
+ type:
+ type: string
+ description: >
+ Identifies the rule-set format. Currently always `allowlist_v0`,
+ corresponding to the `allowlist_v0` property on the full Policy object.
+ examples:
+ - allowlist_v0
+
+ Policy:
+ type: object
+ description: Full policy representation including the allowlist rule set.
+ required: [id, name, org, scope, created_at, updated_at]
+ properties:
+ id:
+ type: string
+ examples:
+ - pol_06evsmp24r1pg71cm8500546pkbn
+ name:
+ type: string
+ description: Human-readable label, unique within the organization.
+ examples:
+ - "Security Research — hardened"
+ org:
+ type: string
+ examples:
+ - my-org
+ scope:
+ $ref: "#/components/schemas/Scope"
+ created_at:
+ type: string
+ format: date-time
+ examples:
+ - "2026-04-22T00:00:00Z"
+ updated_at:
+ type: string
+ format: date-time
+ examples:
+ - "2026-04-22T00:00:00Z"
+ allowlist_v0:
+ $ref: "#/components/schemas/AllowlistV0"
+
+ Scope:
+ type: object
+ description: Restricts the policy to specific profiles or teams. Empty or absent lists mean the policy applies org-wide.
+ properties:
+ profiles:
+ type: array
+ items:
+ type: string
+ examples:
+ - ["security"]
+ teams:
+ type: array
+ items:
+ type: string
+
+ AllowlistV0:
+ type: object
+ description: |
+ Network or filesystem allowlist containing a list of rules. Present on
+ Policy when `PolicySummary.type` is `allowlist_v0`; omitted when the
+ policy has no rules yet. All rules in an allowlist share the same domain.
+ All rules are evaluated on every request: `deny` always wins over `allow`.
+ required: [rules]
+ properties:
+ domain:
+ type: string
+ description: >
+ The access-control domain shared by all rules in this allowlist.
+ Derived from rule actions: network actions (`connect:tcp`,
+ `connect:udp`) produce `network`; filesystem actions (`read`,
+ `write`) produce `filesystem`. Omitted for empty allowlists.
+ enum: [network, filesystem]
+ examples:
+ - network
+ rules:
+ type: array
+ items:
+ $ref: "#/components/schemas/Rule"
+
+ Rule:
+ type: object
+ description: A single allow or deny rule within an allowlist policy.
+ required: [id, name, actions, resources, decision]
+ properties:
+ id:
+ type: string
+ examples:
+ - rule_06evsm9qjm1pdsk0a8nkfaxy7jna
+ name:
+ type: string
+ description: Human-readable label for the rule.
+ examples:
+ - allow research mirrors
+ actions:
+ $ref: "#/components/schemas/RuleActions"
+ resources:
+ $ref: "#/components/schemas/RuleResources"
+ decision:
+ $ref: "#/components/schemas/RuleDecision"
+
+ CreatePolicyRequest:
+ type: object
+ description: Fields required to create a new policy.
+ required: [name]
+ properties:
+ name:
+ type: string
+ description: Policy name, unique within the organization.
+ examples:
+ - "Security Research — hardened"
+ scope:
+ $ref: "#/components/schemas/Scope"
+
+ CreateRuleRequest:
+ type: object
+ description: Fields required to create a new rule within a policy's rule set.
+ required: [name, actions, resources, decision]
+ properties:
+ name:
+ type: string
+ description: Human-readable label for the rule.
+ examples:
+ - allow research mirrors
+ actions:
+ $ref: "#/components/schemas/RuleActions"
+ resources:
+ $ref: "#/components/schemas/RuleResources"
+ decision:
+ $ref: "#/components/schemas/RuleDecision"
+
+ UpdateRuleRequest:
+ type: object
+ description: JSON Merge Patch (RFC 7396). Only present fields are updated.
+ properties:
+ name:
+ type: string
+ description: Human-readable label for the rule.
+ examples:
+ - allow research mirrors
+ actions:
+ $ref: "#/components/schemas/RuleActions"
+ resources:
+ $ref: "#/components/schemas/RuleResources"
+ decision:
+ $ref: "#/components/schemas/RuleDecision"
+
+ UpdateRuleResponse:
+ type: object
+ description: The rule state before and after the update.
+ required: [old, new]
+ properties:
+ old:
+ $ref: "#/components/schemas/Rule"
+ new:
+ $ref: "#/components/schemas/Rule"
+
+ DeleteRuleResponse:
+ type: object
+ description: The deleted rule.
+ required: [deleted]
+ properties:
+ deleted:
+ $ref: "#/components/schemas/Rule"
+
+ RuleActions:
+ type: array
+ items:
+ type: string
+ enum: [connect:tcp, connect:udp, read, write]
+ minItems: 1
+ description: >
+ Network actions: `connect:tcp`, `connect:udp`.
+ Filesystem actions: `read`, `write`.
+ All actions in a rule must belong to the same domain; mixing network
+ and filesystem actions in one rule is rejected.
+ examples:
+ - ["connect:tcp", "connect:udp"]
+
+ RuleResources:
+ type: array
+ items:
+ type: string
+ minItems: 1
+ description: >
+ Network domain: hostnames (for example, `example.com`), wildcard
+ subdomains (`*.example.com` or `**.example.com`), hostnames with port
+ (for example, `example.com:443`), or CIDRs in IPv4 or IPv6 notation
+ (for example, `10.0.0.0/8` or `2001:db8::/32`). Filesystem domain:
+ paths (for example, `/data`); `*` matches within one path segment,
+ `**` matches recursively (for example, `/data/**`).
+ examples:
+ - ["research.mitre.org", "cve.mitre.org"]
+
+ RuleDecision:
+ type: string
+ enum: [allow, deny]
+ description: >
+ Outcome applied when this rule matches a request. `deny` always
+ wins: if any rule in the policy matches with `decision: deny`, the
+ request is denied even if other rules match with `decision: allow`.
+ examples:
+ - allow
+
+ Error:
+ type: object
+ description: Error envelope returned on all non-2xx responses.
+ required: [error]
+ properties:
+ error:
+ type: object
+ description: Error detail.
+ required: [code, message]
+ examples:
+ - code: not_found
+ message: policy not found
+ properties:
+ code:
+ type: string
+ description: >
+ Machine-readable error code. `not_found`: the requested resource
+ does not exist. `conflict`: a resource with the same name already
+ exists. `invalid_argument`: the request body is malformed or
+ fails validation. `unauthenticated`: missing or invalid
+ credentials. `permission_denied`: the caller lacks the required
+ permission. `unimplemented`: the endpoint or feature is not yet
+ available. `internal`: unexpected server error.
+ enum:
+ - not_found
+ - conflict
+ - invalid_argument
+ - unauthenticated
+ - permission_denied
+ - unimplemented
+ - internal
+ message:
+ type: string
+
+ responses:
+ NotFound:
+ description: Not found
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ examples:
+ default:
+ value:
+ error:
+ code: not_found
+ message: policy not found
+
+ Conflict:
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ examples:
+ default:
+ value:
+ error:
+ code: conflict
+ message: policy name already in use
+
+ InvalidArgument:
+ description: Bad request
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ examples:
+ default:
+ value:
+ error:
+ code: invalid_argument
+ message: "name is required"
+
+ Unauthenticated:
+ description: Missing or invalid credentials
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ examples:
+ default:
+ value:
+ error:
+ code: unauthenticated
+ message: unauthenticated
+
+ PermissionDenied:
+ description: Caller lacks the required permission for this org
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ examples:
+ default:
+ value:
+ error:
+ code: permission_denied
+ message: permission denied
+
+ InternalError:
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ examples:
+ default:
+ value:
+ error:
+ code: internal
+ message: internal error
diff --git a/content/manuals/ai/sandboxes/governance/api/index.md b/content/manuals/ai/sandboxes/governance/api/index.md
new file mode 100644
index 000000000000..b1ac25c8ab67
--- /dev/null
+++ b/content/manuals/ai/sandboxes/governance/api/index.md
@@ -0,0 +1,8 @@
+---
+layout: api-reference
+title: Governance API reference
+linkTitle: API reference
+weight: 30
+description: HTTP API reference for managing Docker AI Governance policies and rules programmatically.
+keywords: docker sandboxes, governance API, policy API, organization policy, REST API, openapi
+---
diff --git a/content/manuals/ai/sandboxes/governance/concepts.md b/content/manuals/ai/sandboxes/governance/concepts.md
new file mode 100644
index 000000000000..db3c0afa1764
--- /dev/null
+++ b/content/manuals/ai/sandboxes/governance/concepts.md
@@ -0,0 +1,106 @@
+---
+title: Policy concepts
+weight: 5
+description: The resource model, rule syntax, and evaluation logic behind Docker sandbox governance.
+keywords: docker sandboxes, policy concepts, rule syntax, network rules, filesystem rules, precedence, rule evaluation
+---
+
+## Resource model
+
+Docker sandbox governance is built around two resource types: **policies** and
+**rules**.
+
+A **policy** is a named collection of rules that controls sandbox access.
+Policies exist at different levels:
+
+- **Local**: configured per machine using the `sbx policy` CLI. Applies to
+ sandboxes on that machine only.
+- **Organization**: configured in the Docker Admin Console or via the
+ [Governance API](api.md). Applies uniformly across every sandbox in the
+ organization.
+- **Team**: applies to sandboxes used by a specific team within an
+ organization. Coming soon.
+
+When multiple levels are active, organization policies take precedence over
+local policies. See [Precedence](#precedence).
+
+A **rule** is the unit of access control within a policy. Each rule has:
+
+- **Name**: a human-readable label
+- **Actions**: the type of access the rule controls
+- **Resources**: the targets the rule matches against
+- **Decision**: `allow` or `deny`
+
+Rules are grouped by domain: all rules in a policy must share the same domain,
+either `network` or `filesystem`.
+
+## Rule syntax
+
+### Network rules
+
+Network rules use the actions `connect:tcp` and `connect:udp`. Resources are
+hostnames, CIDR ranges, or ports.
+
+**Hostname patterns**
+
+| Pattern | Example | Matches |
+| ------- | ------- | ------- |
+| Exact hostname | `example.com` | `example.com` only, not subdomains |
+| Single-level wildcard | `*.example.com` | One subdomain level: `api.example.com` |
+| Multi-level wildcard | `**.example.com` | Any depth: `api.example.com`, `v2.api.example.com` |
+| Hostname with port | `example.com:443` | `example.com` on port 443 only |
+
+`example.com` and `*.example.com` don't cover each other. Specify both if you
+need to match the root domain and its subdomains.
+
+**CIDR ranges**
+
+Both IPv4 and IPv6 notation are supported: `10.0.0.0/8`, `192.168.1.0/24`,
+`2001:db8::/32`.
+
+### Filesystem rules
+
+Filesystem rules use the actions `read` and `write`. Resources are host paths
+that sandboxes can mount as workspaces.
+
+| Pattern | Example | Matches |
+| ------- | ------- | ------- |
+| Exact path | `/data` | `/data` only |
+| Segment wildcard | `/data/*` | `/data/project`, one path segment only, not subdirectories |
+| Recursive wildcard | `/data/**` | `/data/project`, `/data/project/src`, any depth |
+
+Use `**` when you intend to match a directory tree recursively. A single `*`
+only matches within one path segment and won't cross directory boundaries.
+For example, `~/**` matches all paths under the home directory, while `~/*`
+matches only direct children of `~`.
+
+## Rule evaluation
+
+All rules in a policy are evaluated against every request. The outcome follows
+two principles:
+
+**Deny wins.** If any rule matches with `decision: deny`, the request is
+denied regardless of any matching allow rules.
+
+**Default deny.** Outbound traffic is blocked unless an explicit allow rule
+matches.
+
+These principles apply within each policy level independently. A deny in an
+organization policy can't be overridden by a local allow.
+
+## Precedence
+
+When only local policies are active, local rules determine what sandboxes can
+access.
+
+When organization governance is active:
+
+- Organization rules take effect across all developer machines.
+- Local rules are not evaluated by default.
+- Admins can [delegate](org.md#delegate-rules-to-local-policy) specific rule
+ types back to local control. Delegated local rules can expand access for
+ targets the organization hasn't explicitly denied, but can't override
+ organization-level deny rules.
+
+Within any level, deny rules beat allow rules regardless of specificity or
+order.
diff --git a/content/manuals/ai/sandboxes/governance/local.md b/content/manuals/ai/sandboxes/governance/local.md
new file mode 100644
index 000000000000..e201df5eec05
--- /dev/null
+++ b/content/manuals/ai/sandboxes/governance/local.md
@@ -0,0 +1,159 @@
+---
+title: Local policy
+weight: 10
+description: Configure network access rules for sandboxes on your local machine.
+keywords: docker sandboxes, local policy, network access, allow rules, deny rules, sbx policy
+aliases:
+ - /ai/sandboxes/security/policy/
+---
+
+The `sbx policy` command manages network access rules on your local machine.
+Rules apply to all sandboxes on the machine when you use the global scope, or
+to a single sandbox when scoped by name.
+
+Local rules work differently depending on whether your organization uses
+governance:
+
+- **No org governance**: local rules fully control what sandboxes can access.
+- **Org governance, delegation enabled**: local rules of the delegated type
+ are evaluated alongside org rules. You can extend access for domains your org
+ hasn't explicitly denied, but org-level denials still take precedence.
+- **Org governance, no delegation**: local rules are inactive. `sbx policy
+ allow` and `sbx policy deny` have no effect for that rule type.
+
+See [Organization policy](org.md) for how admins configure delegation.
+
+For domain patterns, wildcards, CIDR ranges, and filesystem path syntax, see
+[Policy concepts](concepts.md#rule-syntax).
+
+## Default preset
+
+The only way traffic can leave a sandbox is through an HTTP/HTTPS proxy on
+your host, which enforces access rules on every outbound request. Non-HTTP TCP
+traffic, including SSH, can be allowed by adding a policy rule for the
+destination IP and port (for example, `sbx policy allow network -g
+"10.1.2.3:22"`). UDP and ICMP are blocked at the network layer and can't be
+unblocked with policy rules.
+
+On first start, and after running `sbx policy reset`, the daemon prompts you
+to choose a network preset:
+
+```plaintext
+Choose a default network policy:
+
+ 1. Open — All network traffic allowed, no restrictions.
+ 2. Balanced — Default deny, with common dev sites allowed.
+ 3. Locked Down — All network traffic blocked unless you allow it.
+
+ Use ↑/↓ to navigate, Enter to select, or press 1–3.
+```
+
+| Preset | Description |
+| ------ | ----------- |
+| Open | All outbound traffic is allowed. Equivalent to adding a wildcard allow rule with `sbx policy allow network -g "**"`. |
+| Balanced | Default deny, with a baseline allowlist covering AI provider APIs, package managers, code hosts, container registries, and common cloud services. |
+| Locked Down | All outbound traffic is blocked, including model provider APIs (for example, `api.anthropic.com`). You must explicitly allow everything you need. |
+
+The **Balanced** preset's baseline allowlist is a good starting point for most
+workflows. Run `sbx policy ls` to see exactly which rules it includes.
+
+> [!NOTE]
+> If your organization manages sandbox policies centrally, organization rules
+> take precedence over the preset you select here. See
+> [Organization policy](org.md).
+
+### Non-interactive environments
+
+In non-interactive environments such as CI pipelines or headless servers, the
+interactive prompt can't be displayed. Use `sbx policy set-default` to set the
+preset before running any other `sbx` commands:
+
+```console
+$ sbx policy set-default balanced
+```
+
+Available values are `allow-all`, `balanced`, and `deny-all`.
+
+## Managing rules
+
+Use [`sbx policy allow`](/reference/cli/sbx/policy/allow/) and
+[`sbx policy deny`](/reference/cli/sbx/policy/deny/) to add or restrict access
+on top of the active preset. Changes take effect immediately. Pass `-g` to
+apply a rule globally to all sandboxes:
+
+```console
+$ sbx policy allow network -g api.anthropic.com
+$ sbx policy deny network -g ads.example.com
+```
+
+Pass a sandbox name to scope a rule to one sandbox:
+
+```console
+$ sbx policy allow network my-sandbox api.example.com
+$ sbx policy deny network my-sandbox ads.example.com
+```
+
+Specify multiple hosts in one command with a comma-separated list:
+
+```console
+$ sbx policy allow network -g "api.anthropic.com,*.npmjs.org,*.pypi.org"
+```
+
+Remove a rule by resource or by rule ID:
+
+```console
+$ sbx policy rm network -g --resource ads.example.com
+$ sbx policy rm network -g --id 2d3c1f0e-4a73-4e05-bc9d-f2f9a4b50d67
+```
+
+To remove a sandbox-scoped rule, include the sandbox name:
+
+```console
+$ sbx policy rm network my-sandbox --resource api.example.com
+```
+
+To inspect which rules are active and where they come from, use
+`sbx policy ls`. See [Monitoring](monitoring.md).
+
+### Resetting
+
+To remove all custom rules and start fresh with a new preset, use
+`sbx policy reset`:
+
+```console
+$ sbx policy reset
+```
+
+This deletes the local policy store and stops the daemon. When the daemon
+restarts on the next command, you are prompted to choose a new preset. Running
+sandboxes stop when the daemon shuts down. Pass `--force` to skip the
+confirmation prompt:
+
+```console
+$ sbx policy reset --force
+```
+
+## Troubleshooting
+
+### Local rules have no effect
+
+If rules you add with `sbx policy allow` or `sbx policy deny` don't change
+sandbox behavior, your organization likely has governance enabled without
+delegating that rule type to local control. Run `sbx policy ls` to check: if
+the output starts with a `Governance: managed by ` header, org governance
+is active. If your rules appear with `inactive` status, org governance is
+suppressing them.
+
+To use local rules alongside org rules, ask your admin to enable delegation for
+the relevant rule type in the Admin Console. See
+[Delegate rules to local policy](org.md#delegate-rules-to-local-policy).
+
+### A domain is still blocked after adding an allow rule
+
+If a domain remains blocked after you add a local allow rule, an org-level deny
+rule may be covering it. [Delegation](org.md#delegate-rules-to-local-policy)
+lets local rules expand access, but org deny rules always take precedence. Run `sbx policy ls` to check whether a rule
+with `remote` origin and `deny` decision matches the domain. If so, the block
+can only be lifted by updating the org policy in the Admin Console or via the
+[API](api.md).
+
diff --git a/content/manuals/ai/sandboxes/governance/monitoring.md b/content/manuals/ai/sandboxes/governance/monitoring.md
new file mode 100644
index 000000000000..1c0b6df08e55
--- /dev/null
+++ b/content/manuals/ai/sandboxes/governance/monitoring.md
@@ -0,0 +1,107 @@
+---
+title: Monitoring policies
+weight: 25
+description: Inspect active policy rules and monitor sandbox network traffic with sbx policy ls and sbx policy log.
+keywords: docker sandboxes, policy monitoring, sbx policy ls, sbx policy log, network traffic, policy debugging
+---
+
+`sbx policy ls` and `sbx policy log` give you a combined view of all active
+policy rules and sandbox network activity, regardless of whether those rules
+come from local configuration or organization governance. They're useful both
+for verifying rules you've written and for debugging why a request is being
+blocked or allowed.
+
+## Listing rules
+
+Use `sbx policy ls` to see all active rules and their current status:
+
+```console
+$ sbx policy ls
+NAME TYPE ORIGIN DECISION STATUS RESOURCES
+balanced-dev network local allow active api.anthropic.com
+ads-block network local deny active ads.example.com
+kit:my-sandbox network sandbox:my-sandbox allow active api.example.com
+kit:my-sandbox:deny network sandbox:my-sandbox deny active telemetry.example.com
+```
+
+The columns are:
+
+- `NAME`: the rule name.
+- `TYPE`: the rule domain, such as `network`.
+- `ORIGIN`: where the rule was configured. `local` means the rule is global
+ and applies to all sandboxes. `sandbox:` means the rule is scoped to
+ the named sandbox. `remote` means the rule was set by your organization.
+- `DECISION`: whether the rule allows or denies the resource.
+- `STATUS`: whether the rule is in effect. A rule may be `inactive` if it's
+ overridden or suppressed (for example, when organization governance is
+ active and hasn't delegated that rule type to local control).
+- `RESOURCES`: the hosts or patterns the rule applies to.
+
+When organization governance is active, the output starts with a governance
+header showing which organization manages the policy and when it last synced:
+
+```console
+$ sbx policy ls
+Governance: managed by my-org
+[OK] last synced 13:54:21
+NAME TYPE ORIGIN DECISION STATUS RESOURCES
+balanced-dev network local allow inactive — corporate policy takes precedence and does api.anthropic.com
+ not delegate this rule type to local policy.
+allow AI services network remote allow active api.anthropic.com
+ api.openai.com
+allow Docker services network remote allow active *.docker.com
+ *.docker.io
+```
+
+The governance header shows which organization is managing the policy and
+confirms the daemon has successfully pulled the latest rules. If the sync
+status shows an error or a stale timestamp, the daemon may not have the most
+recent org policy. Run `sbx policy reset` to force a fresh pull.
+
+Use `--type network` to show only network rules. Without a sandbox argument,
+`sbx policy ls` shows every rule across all sandboxes. Pass a sandbox name to
+filter to global rules and rules scoped to that sandbox:
+
+```console
+$ sbx policy ls my-sandbox
+```
+
+## Monitoring traffic
+
+Use `sbx policy log` to see which hosts your sandboxes have contacted and
+which rules matched:
+
+```console
+$ sbx policy log
+Blocked requests:
+SANDBOX TYPE HOST PROXY RULE REASON LAST SEEN COUNT
+my-sandbox network blocked.example.com transparent domain-blocked default-deny 10:15:25 29-Jan 1
+
+Allowed requests:
+SANDBOX TYPE HOST PROXY RULE REASON LAST SEEN COUNT
+my-sandbox network api.anthropic.com forward domain-allowed 10:15:23 29-Jan 42
+my-sandbox network registry.npmjs.org forward-bypass domain-allowed 10:15:20 29-Jan 18
+my-sandbox network app.example.com browser-open 10:15:10 29-Jan 1
+```
+
+The `PROXY` column shows how the request left the sandbox:
+
+| Value | Description |
+| ----- | ----------- |
+| `forward` | Routed through the forward proxy. Supports [credential injection](../security/credentials.md). |
+| `forward-bypass` | Routed through the forward proxy without credential injection. |
+| `transparent` | Intercepted by the transparent proxy. Policy is enforced but credential injection is not available. |
+| `network` | Non-HTTP traffic (raw TCP, UDP, ICMP). TCP can be allowed with a policy rule; UDP and ICMP are always blocked. |
+| `browser-open` | A sandbox process requested opening a URL in the host browser. Policy is enforced before opening the URL. |
+
+The `RULE` column identifies the policy rule that matched the request. The
+`REASON` column includes extra context when the daemon records one.
+
+Filter by sandbox name by passing it as an argument:
+
+```console
+$ sbx policy log my-sandbox
+```
+
+Use `--limit N` to show only the last `N` entries, `--json` for
+machine-readable output, or `--type network` to filter by policy type.
diff --git a/content/manuals/ai/sandboxes/security/governance.md b/content/manuals/ai/sandboxes/governance/org.md
similarity index 58%
rename from content/manuals/ai/sandboxes/security/governance.md
rename to content/manuals/ai/sandboxes/governance/org.md
index 7185c78b10f4..23036edfb8da 100644
--- a/content/manuals/ai/sandboxes/security/governance.md
+++ b/content/manuals/ai/sandboxes/governance/org.md
@@ -1,22 +1,21 @@
---
-title: Organization governance
-linkTitle: Org governance
-weight: 35
+title: Organization policy
+linkTitle: Org policy
+weight: 20
description: Centrally manage sandbox network and filesystem policies for your organization.
keywords: docker sandboxes, governance, organization policy, AI governance, admin console, network access, filesystem access
+aliases:
+ - /ai/sandboxes/security/governance/
---
-This page covers how to configure organization policies in the Docker Admin
-Console under AI governance settings. For local sandbox policies that
-individual users configure on their own machine, see [Policies](policy.md).
+[Local policies](local.md) give individual developers control over what their
+sandboxes can access. Organization policy extends this to the admin level:
+rules defined in the [Docker Admin Console](https://app.docker.com/admin) apply
+uniformly to every sandbox in the organization, take precedence over local
+`sbx policy` rules, and can't be overridden by individual users.
-Sandbox network and filesystem policies defined in the
-[Docker Admin Console](https://app.docker.com/admin) apply uniformly to every
-sandbox in the organization. Rules are enforced across all developers'
-machines, take precedence over local `sbx policy` rules, and can't be
-overridden by individual users. Admins can optionally
-[delegate](#delegate-rules-to-local-policy) specific rule types back to local
-control so developers can add additional allow rules.
+Admins can manage organization policies through the Admin Console UI or
+programmatically using the [Governance API](api.md).
> [!NOTE]
> Sandbox organization governance is available on a separate paid
@@ -29,42 +28,25 @@ control so developers can add additional allow rules.
### Configuring org-level network rules
Define network allow and deny rules in the Admin Console under
-**AI governance > Network access**. Each rule takes a network target (domain,
-wildcard, or CIDR range) and an action (allow or deny). You can add multiple
-entries at once, one per line.
+**AI governance > Network access**. Each rule takes a network target and an
+action (allow or deny). You can add multiple entries at once, one per line.
-Rules support exact domains (`example.com`), wildcard subdomains
-(`*.example.com`), and optional port suffixes (`example.com:443`).
-
-`example.com` doesn't match subdomains, and `*.example.com` doesn't match
-the root domain. Specify both to cover both.
+For the full syntax reference (exact hostnames, wildcard subdomains, port
+suffixes, and CIDR ranges), see [Policy concepts](concepts.md#network-rules).
### Delegate rules to local policy
-When organization governance is active, local rules are ignored by default —
-only the organization policy is in effect. Admins can delegate a rule type
+When organization governance is active, local rules are ignored by default.
+Only the organization policy is in effect. Admins can delegate a rule type
back to local policy by turning on the **User defined** setting for that
rule type in AI governance settings. Turning the setting on delegates the
rule type: local `sbx policy` rules of that type are evaluated alongside
organization rules, letting users add hosts to the allowlist from their own
machine.
-If a rule type isn't delegated, local rules of that type still appear in
-`sbx policy ls` but with an `inactive` status and a note that the
-organization hasn't delegated the rule type to local policy:
-
-```console
-$ sbx policy ls
-NAME TYPE ORIGIN DECISION STATUS RESOURCES
-balanced-dev network local allow inactive — corporate policy takes precedence and does api.anthropic.com
- not delegate this rule type to local policy.
-allow AI services network remote allow active api.anthropic.com
- api.openai.com
-allow Docker services network remote allow active *.docker.com
- *.docker.io
-```
-
-Organization rules show up with `remote` in the `ORIGIN` column.
+When a rule type isn't delegated, local rules of that type still appear in
+`sbx policy ls` but with an `inactive` status. See [Monitoring](monitoring.md)
+for how to read the combined rule view.
Delegated local rules can expand access for domains the organization hasn't
explicitly denied, but can't override organization-level deny rules. This
@@ -75,9 +57,9 @@ org-level wildcard deny covers it.
For example, given an organization policy that allows `api.anthropic.com`
and denies `*.corp.internal`:
-- `sbx policy allow network -g api.example.com` — works, because the
+- `sbx policy allow network -g api.example.com`: works, because the
organization hasn't denied `api.example.com`
-- `sbx policy allow network -g build.corp.internal` — no effect, because the
+- `sbx policy allow network -g build.corp.internal`: no effect, because the
organization denies `*.corp.internal`
#### Blocked values in delegated rules
@@ -101,12 +83,8 @@ Admins can restrict which paths are mountable by defining filesystem allow
and deny rules in the Admin Console under **AI governance > Filesystem
access**. Each rule takes a path pattern and an action (allow or deny).
-> [!CAUTION]
-> Use `**` (double wildcard) rather than `*` (single wildcard) when writing
-> path patterns to match path segments recursively. A single `*` only matches
-> within a single path segment. For example, `~/**` matches all paths under
-> the user's home directory, whereas `~/*` matches only paths directly
-> under `~`.
+For path pattern syntax including the difference between `*` and `**`, see
+[Policy concepts](concepts.md#filesystem-rules).
## Precedence
@@ -129,7 +107,7 @@ precedence over local behavior.
To unblock a domain, identify where the deny rule comes from. For local
rules, remove it with `sbx policy rm`. For organization-level rules, update
-the rule in the Admin Console.
+the rule in the Admin Console or via the [API](api.md).
## Troubleshooting
diff --git a/content/manuals/ai/sandboxes/security/_index.md b/content/manuals/ai/sandboxes/security/_index.md
index ef6048e4446a..c2d911118d35 100644
--- a/content/manuals/ai/sandboxes/security/_index.md
+++ b/content/manuals/ai/sandboxes/security/_index.md
@@ -89,7 +89,7 @@ organization and take precedence over local rules. Admins can optionally
delegate specific rule types back to local control so developers can add
additional allow rules.
-See [Organization governance](governance/) for details.
+See [Organization policy](../governance/org.md) for details.
## Learn more
@@ -98,7 +98,6 @@ See [Organization governance](governance/) for details.
- [Default security posture](defaults/): what a fresh sandbox permits and
blocks
- [Credentials](credentials/): how to provide and manage API keys
-- [Policies](policy/): how to customize network access rules
-- [Organization governance](governance/): centrally manage policies across
- an organization
+- [Governance](../governance/): configure network and filesystem access rules,
+ locally or across your organization
- [Workspace trust](workspace/): what to review after an agent session
diff --git a/content/manuals/ai/sandboxes/security/defaults.md b/content/manuals/ai/sandboxes/security/defaults.md
index 8c8a1f9a1753..07536772325e 100644
--- a/content/manuals/ai/sandboxes/security/defaults.md
+++ b/content/manuals/ai/sandboxes/security/defaults.md
@@ -17,7 +17,7 @@ ICMP) are blocked at the network layer. Traffic to private IP ranges, loopback
addresses, and link-local addresses is also blocked.
Run `sbx policy ls` to see the active network rules for your installation. To
-customize network access, see [Policies](policy.md). If your organization
+customize network access, see [Policies](../governance/local.md). If your organization
manages sandbox policies centrally, those rules apply on top of the defaults
described here. See [Organization governance](governance.md).
diff --git a/content/manuals/ai/sandboxes/security/isolation.md b/content/manuals/ai/sandboxes/security/isolation.md
index ab220ad26ba7..27575f4d8f73 100644
--- a/content/manuals/ai/sandboxes/security/isolation.md
+++ b/content/manuals/ai/sandboxes/security/isolation.md
@@ -34,7 +34,7 @@ each other and cannot reach your host's localhost. There is no shared network
between sandboxes or between a sandbox and your host.
All HTTP and HTTPS traffic leaving a sandbox passes through a proxy on your
-host that enforces the [network policy](policy.md). The sandbox routes
+host that enforces the [network policy](../governance/local.md). The sandbox routes
traffic through either a forward proxy or a transparent proxy depending on the
client's configuration. Both enforce the network policy; only the forward proxy
[injects credentials](credentials.md) for AI services.
diff --git a/content/manuals/ai/sandboxes/security/policy.md b/content/manuals/ai/sandboxes/security/policy.md
deleted file mode 100644
index b58175a825cb..000000000000
--- a/content/manuals/ai/sandboxes/security/policy.md
+++ /dev/null
@@ -1,263 +0,0 @@
----
-title: Policies
-weight: 30
-description: Configure network access rules for sandboxes.
-keywords: docker sandboxes, policies, network access, allow rules, deny rules
----
-
-Sandboxes are [network-isolated](isolation.md) from your host and from each
-other. A policy system controls what a sandbox can access over the network.
-
-Use the `sbx policy` command to configure network access rules. Rules apply
-to all sandboxes on the machine when you use the global scope. Network allow,
-deny, list, and remove commands can also target one sandbox.
-
-If your organization manages sandbox policies centrally, organization rules
-take precedence over the local rules described on this page. See
-[Organization governance](governance.md).
-
-## Network policies
-
-The only way traffic can leave a sandbox is through an HTTP/HTTPS proxy on
-your host, which enforces access rules on every outbound request.
-
-Non-HTTP TCP traffic, including SSH, can be allowed by adding a policy rule
-for the destination IP address and port (for example,
-`sbx policy allow network -g "10.1.2.3:22"`). UDP and ICMP traffic is blocked
-at the network layer and can't be unblocked with policy rules.
-
-### Initial policy selection
-
-On first start, and after running `sbx policy reset`, the daemon prompts you to
-choose a network policy:
-
-```plaintext
-Choose a default network policy:
-
- 1. Open — All network traffic allowed, no restrictions.
- 2. Balanced — Default deny, with common dev sites allowed.
- 3. Locked Down — All network traffic blocked unless you allow it.
-
- Use ↑/↓ to navigate, Enter to select, or press 1–3.
-```
-
-| Policy | Description |
-| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Open | All outbound traffic is allowed. No restrictions. Equivalent to adding a wildcard allow rule with `sbx policy allow network -g "**"`. |
-| Balanced | Default deny, with a baseline allowlist covering AI provider APIs, package managers, code hosts, container registries, and common cloud services. You can extend this with `sbx policy allow`. |
-| Locked Down | All outbound traffic is blocked, including model provider APIs (for example, `api.anthropic.com`). You must explicitly allow everything you need. |
-
-You can change your effective policy at any time using `sbx policy allow` and
-`sbx policy deny`, or start over by running `sbx policy reset`.
-
-> [!NOTE]
-> If your organization manages sandbox policies centrally, organization rules
-> take precedence over the policy you select here. See
-> [Organization governance](governance.md).
-
-### Non-interactive environments
-
-In non-interactive environments such as CI pipelines or headless servers, the
-interactive prompt can't be displayed. Use `sbx policy set-default` to set the
-default network policy before running any other `sbx` commands:
-
-```console
-$ sbx policy set-default balanced
-```
-
-Available values are `allow-all`, `balanced`, and `deny-all`. After setting the
-default, you can customize further with `sbx policy allow` and
-`sbx policy deny` as usual.
-
-### Default policy
-
-All outbound HTTP/HTTPS traffic is blocked by default unless an explicit rule
-allows it. The **Balanced** policy ships with a baseline allowlist covering AI provider
-APIs, package managers, code hosts, container registries, and common cloud
-services. Run `sbx policy ls` to see the active rules for your installation.
-
-### Managing rules
-
-Use [`sbx policy allow`](/reference/cli/sbx/policy/allow/) and
-[`sbx policy deny`](/reference/cli/sbx/policy/deny/) to add network access
-rules. Changes take effect immediately. Pass `-g` to apply a rule to all
-sandboxes:
-
-```console
-$ sbx policy allow network -g api.anthropic.com
-$ sbx policy deny network -g ads.example.com
-```
-
-Pass a sandbox name to scope a rule to one sandbox:
-
-```console
-$ sbx policy allow network my-sandbox api.example.com
-$ sbx policy deny network my-sandbox ads.example.com
-```
-
-Specify multiple hosts in one command with a comma-separated list:
-
-```console
-$ sbx policy allow network -g "api.anthropic.com,*.npmjs.org,*.pypi.org"
-```
-
-List all active policy rules with `sbx policy ls`:
-
-```console
-$ sbx policy ls
-NAME TYPE ORIGIN DECISION STATUS RESOURCES
-balanced-dev network local allow active api.anthropic.com
-ads-block network local deny active ads.example.com
-kit:my-sandbox network sandbox:my-sandbox allow active api.example.com
-kit:my-sandbox:deny network sandbox:my-sandbox deny active telemetry.example.com
-```
-
-The columns are:
-
-- `NAME`: the rule name.
-- `TYPE`: the rule type, such as `network`.
-- `ORIGIN`: where the rule applies. `local` means the rule is global and
- applies to all sandboxes. `sandbox:` means the rule is scoped to the
- named sandbox.
-- `DECISION`: whether the rule allows or denies the resource.
-- `STATUS`: whether the rule is currently in effect. A rule may be inactive if
- it's overridden by another rule, for example.
-- `RESOURCES`: the hosts or patterns the rule applies to.
-
-Use `--type network` to show only network policies. Without a sandbox argument,
-`sbx policy ls` shows every rule across all sandboxes. Pass a sandbox name to
-filter the list to global rules and rules scoped to that sandbox only:
-
-```console
-$ sbx policy ls my-sandbox
-```
-
-Remove a policy by resource or by rule ID:
-
-```console
-$ sbx policy rm network -g --resource ads.example.com
-$ sbx policy rm network -g --id 2d3c1f0e-4a73-4e05-bc9d-f2f9a4b50d67
-```
-
-To remove a sandbox-scoped policy, include the sandbox name:
-
-```console
-$ sbx policy rm network my-sandbox --resource api.example.com
-```
-
-### Resetting to defaults
-
-To remove all custom policies and restore the default policy, use
-`sbx policy reset`:
-
-```console
-$ sbx policy reset
-```
-
-This deletes the local policy store and stops the daemon. When the daemon
-restarts on the next command, you are prompted to choose a new network policy.
-If sandboxes are running, they stop when the daemon shuts down. You are prompted for
-confirmation unless you pass `--force`:
-
-```console
-$ sbx policy reset --force
-```
-
-### Switching to allow-by-default
-
-If you prefer a permissive policy where all outbound traffic is allowed, add
-a wildcard allow rule:
-
-```console
-$ sbx policy allow network -g "**"
-```
-
-This lets agents install packages and call any external API without additional
-configuration. You can still deny specific hosts with `sbx policy deny`.
-
-### Wildcard syntax
-
-Rules support exact domains (`example.com`), wildcard subdomains
-(`*.example.com`), and optional port suffixes (`example.com:443`).
-
-Note that `example.com` doesn't match subdomains, and `*.example.com` doesn't
-match the root domain. Specify both to cover both.
-
-### Common patterns
-
-Allow access to package managers so agents can install dependencies:
-
-```console
-$ sbx policy allow network -g "*.npmjs.org,*.pypi.org,files.pythonhosted.org,github.com"
-```
-
-The **Balanced** policy already includes AI provider APIs, package managers,
-code hosts, and container registries. You only need to add allow rules for
-additional domains your workflow requires. If you chose **Locked Down**, you
-must explicitly allow everything.
-
-> [!WARNING]
-> Allowing broad domains like `github.com` permits access to any content on
-> that domain, including user-generated content. Only allow domains you trust
-> with your data.
-
-### Monitoring
-
-Use `sbx policy log` to see which hosts your sandboxes have contacted:
-
-```console
-$ sbx policy log
-Blocked requests:
-SANDBOX TYPE HOST PROXY RULE REASON LAST SEEN COUNT
-my-sandbox network blocked.example.com transparent domain-blocked default-deny 10:15:25 29-Jan 1
-
-Allowed requests:
-SANDBOX TYPE HOST PROXY RULE REASON LAST SEEN COUNT
-my-sandbox network api.anthropic.com forward domain-allowed 10:15:23 29-Jan 42
-my-sandbox network registry.npmjs.org forward-bypass domain-allowed 10:15:20 29-Jan 18
-my-sandbox network app.example.com browser-open 10:15:10 29-Jan 1
-```
-
-The **PROXY** column shows how the request left the sandbox:
-
-| Value | Description |
-| ---------------- | -------------------------------------------------------------------------------------------------------------- |
-| `forward` | Routed through the forward proxy. Supports [credential injection](credentials.md). |
-| `forward-bypass` | Routed through the forward proxy without credential injection. |
-| `transparent` | Intercepted by the transparent proxy. Policy is enforced but credential injection is not available. |
-| `network` | Non-HTTP traffic (raw TCP, UDP, ICMP). TCP can be allowed with a policy rule; UDP and ICMP are always blocked. |
-| `browser-open` | A sandbox process requested opening a URL in the host browser. Policy is enforced before opening the URL. |
-
-The **RULE** column identifies the policy rule that matched the request. The
-**REASON** column includes extra context when the daemon records one.
-
-Filter by sandbox name by passing it as an argument:
-
-```console
-$ sbx policy log my-sandbox
-```
-
-Use `--limit N` to show only the last `N` entries, `--json` for
-machine-readable output, or `--type network` to filter by policy type.
-
-## Precedence
-
-All outbound traffic is blocked by default unless an explicit rule allows it.
-If a domain matches both an allow and a deny rule, the deny rule wins
-regardless of specificity. A sandbox-scoped deny rule can block a domain for
-one sandbox even when a global rule permits the same domain.
-
-To unblock a domain, find the deny rule with `sbx policy ls` and remove it
-with `sbx policy rm`.
-
-If your organization manages sandbox policies centrally, organization rules
-take precedence and local rules are not evaluated unless the admin delegates
-that rule type. See [Organization governance](governance.md).
-
-## Troubleshooting
-
-### Policy changes not taking effect
-
-If policy changes aren't taking effect, run `sbx policy reset` to wipe the
-local policy store and restart the daemon. On next start, you are prompted to
-choose a new network policy.
diff --git a/content/manuals/ai/sandboxes/troubleshooting.md b/content/manuals/ai/sandboxes/troubleshooting.md
index 0430031207db..e23fda5fbb22 100644
--- a/content/manuals/ai/sandboxes/troubleshooting.md
+++ b/content/manuals/ai/sandboxes/troubleshooting.md
@@ -30,7 +30,7 @@ data. Create fresh sandboxes afterwards.
## Agent can't install packages or reach an API
-Sandboxes use a [deny-by-default network policy](security/policy.md).
+Sandboxes use a [deny-by-default network policy](governance/local.md).
If the agent fails to install packages or call an external API, the target
domain is likely not in the allow list. Check which requests are being blocked:
@@ -52,7 +52,7 @@ $ sbx policy allow network -g "**"
If `sbx policy allow` doesn't unblock the request, your organization may
manage sandbox policies centrally and take precedence over local rules. See
-[Organization governance](security/governance.md).
+[Organization governance](governance/org.md).
## SSH and other non-HTTP connections fail
@@ -106,7 +106,7 @@ If credentials are configured correctly but API calls still fail, check
the `transparent` proxy don't get credential injection. This can happen when a
client inside the sandbox (such as a process in a Docker container) isn't
configured to use the forward proxy. See
-[Monitoring network activity](security/policy.md#monitoring)
+[Monitoring network activity](governance/monitoring.md)
for details.
## Docker build export fails with an ownership error
diff --git a/content/manuals/ai/sandboxes/usage.md b/content/manuals/ai/sandboxes/usage.md
index c8fa2e8f757a..088bafeca027 100644
--- a/content/manuals/ai/sandboxes/usage.md
+++ b/content/manuals/ai/sandboxes/usage.md
@@ -391,7 +391,7 @@ needs:
- [Custom templates and kits](customize/) let you package reusable agent
configurations, MCP servers, base images, and per-project policies. Every
developer pulls them down with their workspace.
-- [Organization governance](security/governance.md) lets admins define
+- [Organization governance](governance/org.md) lets admins define
network and filesystem rules in the Docker Admin Console. The rules apply
across every developer's sandboxes and take precedence over local policy.
Available on a separate paid subscription.
diff --git a/layouts/_partials/api-ref/curl.html b/layouts/_partials/api-ref/curl.html
new file mode 100644
index 000000000000..366645adbb21
--- /dev/null
+++ b/layouts/_partials/api-ref/curl.html
@@ -0,0 +1,125 @@
+{{/*
+ Build a curl command for an OpenAPI operation.
+
+ Input dict:
+ api — unmarshaled spec root
+ op — operation object
+ method — http method (lowercase string)
+ path — path string (e.g. "/orgs/{org_name}/policies")
+ sharedParams — already-resolved path-level parameters
+
+ Output: a multi-line curl command string (line-continued with " \\\n ").
+ Path placeholders are filled from parameter examples when available;
+ unresolved path params fall back to ``. Query params without an
+ example are omitted. Bearer-auth schemes contribute an Authorization
+ header with the literal `$TOKEN` placeholder.
+*/}}
+{{- $api := .api -}}
+{{- $op := .op -}}
+{{- $method := .method -}}
+{{- $path := .path -}}
+{{- $sharedParams := .sharedParams -}}
+
+{{/* Combine path-level + operation-level parameters, resolving $refs. */}}
+{{- $params := $sharedParams -}}
+{{- with index $op "parameters" -}}
+ {{- range . -}}
+ {{- $p := partial "api-ref/resolve.html" (dict "api" $api "node" .) -}}
+ {{- $params = $params | append $p -}}
+ {{- end -}}
+{{- end -}}
+
+{{/* Substitute path placeholders. */}}
+{{- $url := $path -}}
+{{- range $params -}}
+ {{- if eq .in "path" -}}
+ {{- $val := printf "<%s>" .name -}}
+ {{- with .example -}}{{- $val = . | string -}}{{- end -}}
+ {{- with .examples -}}
+ {{- $picked := false -}}
+ {{- with index . "default" -}}
+ {{- with .value -}}{{- $val = . | string -}}{{- $picked = true -}}{{- end -}}
+ {{- end -}}
+ {{- if not $picked -}}
+ {{- range . -}}
+ {{- if not $picked -}}
+ {{- with .value -}}{{- $val = . | string -}}{{- $picked = true -}}{{- end -}}
+ {{- end -}}
+ {{- end -}}
+ {{- end -}}
+ {{- end -}}
+ {{- $url = replace $url (printf "{%s}" .name) $val -}}
+ {{- end -}}
+{{- end -}}
+
+{{/* Append query string from query parameters that have examples. */}}
+{{- $qs := slice -}}
+{{- range $params -}}
+ {{- if eq .in "query" -}}
+ {{- $val := "" -}}
+ {{- with .example -}}{{- $val = . | string -}}{{- end -}}
+ {{- with .examples -}}
+ {{- $picked := false -}}
+ {{- with index . "default" -}}
+ {{- with .value -}}{{- $val = . | string -}}{{- $picked = true -}}{{- end -}}
+ {{- end -}}
+ {{- if not $picked -}}
+ {{- range . -}}
+ {{- if not $picked -}}
+ {{- with .value -}}{{- $val = . | string -}}{{- $picked = true -}}{{- end -}}
+ {{- end -}}
+ {{- end -}}
+ {{- end -}}
+ {{- end -}}
+ {{- if $val -}}{{- $qs = $qs | append (printf "%s=%s" .name $val) -}}{{- end -}}
+ {{- end -}}
+{{- end -}}
+{{- if $qs -}}{{- $url = printf "%s?%s" $url (delimit $qs "&") -}}{{- end -}}
+
+{{/* Prepend base URL from the first server entry. */}}
+{{- $base := "" -}}
+{{- with index $api.servers 0 -}}{{- $base = .url -}}{{- end -}}
+{{- $fullURL := printf "%s%s" $base $url -}}
+
+{{- $lines := slice (printf "curl -X %s '%s'" (upper $method) $fullURL) -}}
+
+{{/* Bearer auth header if the spec defines a bearer scheme. */}}
+{{- $hasBearer := false -}}
+{{- with $api.components -}}
+ {{- with .securitySchemes -}}
+ {{- range . -}}
+ {{- if and (eq .type "http") (eq .scheme "bearer") -}}
+ {{- $hasBearer = true -}}
+ {{- end -}}
+ {{- end -}}
+ {{- end -}}
+{{- end -}}
+{{- if $hasBearer -}}
+ {{- $lines = $lines | append "-H 'Authorization: Bearer $TOKEN'" -}}
+{{- end -}}
+
+{{/* Request body example. */}}
+{{- with $op.requestBody -}}
+ {{- $body := partial "api-ref/resolve.html" (dict "api" $api "node" .) -}}
+ {{- with index $body.content "application/json" -}}
+ {{- $lines = $lines | append "-H 'Content-Type: application/json'" -}}
+ {{- $example := "" -}}
+ {{- with .examples -}}
+ {{- with index . "default" -}}
+ {{- with .value -}}{{- $example = . | jsonify -}}{{- end -}}
+ {{- end -}}
+ {{- if not $example -}}
+ {{- range . -}}
+ {{- if not $example -}}
+ {{- with .value -}}{{- $example = . | jsonify -}}{{- end -}}
+ {{- end -}}
+ {{- end -}}
+ {{- end -}}
+ {{- end -}}
+ {{- if $example -}}
+ {{- $lines = $lines | append (printf "-d '%s'" $example) -}}
+ {{- end -}}
+ {{- end -}}
+{{- end -}}
+
+{{- return (delimit $lines " \\\n ") -}}
diff --git a/layouts/_partials/api-ref/nav.html b/layouts/_partials/api-ref/nav.html
new file mode 100644
index 000000000000..f182e4d8a369
--- /dev/null
+++ b/layouts/_partials/api-ref/nav.html
@@ -0,0 +1,86 @@
+{{/* Right-rail navigation for an OpenAPI reference page.
+
+ Input: the unmarshaled spec root (plain map from transform.Unmarshal).
+
+ Lists Authentication, every tag with its operations (linking to the same
+ anchors the operation cards expose), and the Schemas section heading.
+*/}}
+{{- $api := . -}}
+{{- $paths := $api.paths -}}
+{{- $methods := slice "get" "post" "put" "patch" "delete" -}}
+{{- $methodColors := dict
+ "get" "text-blue-700 dark:text-blue-300"
+ "post" "text-green-700 dark:text-green-300"
+ "put" "text-yellow-700 dark:text-yellow-300"
+ "patch" "text-yellow-700 dark:text-yellow-300"
+ "delete" "text-red-700 dark:text-red-300"
+-}}
+
+
+
+ On this page
+
+
+ {{- with $api.components }}
+ {{- if .securitySchemes }}
+
diff --git a/layouts/_partials/api-ref/resolve.html b/layouts/_partials/api-ref/resolve.html
new file mode 100644
index 000000000000..80c7e25fe7de
--- /dev/null
+++ b/layouts/_partials/api-ref/resolve.html
@@ -0,0 +1,26 @@
+{{/* Resolve a possibly-$ref'd OpenAPI node against the spec root.
+
+ Input dict:
+ api — the unmarshaled spec root (plain map from transform.Unmarshal)
+ node — the node to resolve; may be a map, slice, primitive, or nil
+
+ Returns:
+ - if node is a map containing a "$ref" key, returns the target node
+ reached by walking the JSON-Pointer fragment (only internal "#/" refs
+ are supported)
+ - otherwise returns node unchanged
+*/}}
+{{- $node := .node -}}
+{{- if reflect.IsMap $node -}}
+ {{- with index $node "$ref" -}}
+ {{- if hasPrefix . "#/" -}}
+ {{- $parts := after 1 (split . "/") -}}
+ {{- $cur := $.api -}}
+ {{- range $parts -}}
+ {{- $cur = index $cur . -}}
+ {{- end -}}
+ {{- $node = $cur -}}
+ {{- end -}}
+ {{- end -}}
+{{- end -}}
+{{- return $node -}}
diff --git a/layouts/api-reference.html b/layouts/api-reference.html
new file mode 100644
index 000000000000..08c622aaf36b
--- /dev/null
+++ b/layouts/api-reference.html
@@ -0,0 +1,682 @@
+{{/* Wide-mode API reference layout.
+
+ Reads the colocated *.yaml Page Resource, unmarshals it into a plain Hugo
+ map with transform.Unmarshal, and renders the OpenAPI 3 spec inline using
+ Docker docs styling. Overrides baseof's "main" block so the article spans
+ the full available width; the built-in right-rail TOC is replaced with a
+ custom endpoint navigator (partials/api-ref/nav.html) sticky-pinned to
+ the viewport.
+
+ $ref nodes are resolved on the fly through partials/api-ref/resolve.html;
+ no oapi-codegen or third-party JS runtime is involved.
+*/}}
+
+{{ define "main" }}
+ {{- $specRes := .Resources.GetMatch "*.yaml" -}}
+ {{- if not $specRes -}}
+ {{- errorf "api-reference: no .yaml resource found alongside %s" .File.Path -}}
+ {{- end -}}
+ {{- $api := $specRes | transform.Unmarshal -}}
+
+
+
>{{ $r.description }}
- {{- with index $r.content "application/json" }}
+ {{- $ct := "" -}}
+ {{- $media := dict -}}
+ {{- range $name, $m := $r.content -}}
+ {{- if not $ct -}}
+ {{- $ct = $name -}}{{- $media = $m -}}
+ {{- end -}}
+ {{- end -}}
+ {{- if $ct }}
- {{ template "api-ref-schema-link" (dict "api" $api "schema" .schema) }}
+ {{ template "api-ref-schema-link" (dict "api" $api "schema" $media.schema) }}
{{ template "api-ref-examples" (dict
- "examples" .examples
+ "examples" $media.examples
"prefix" (printf "resp-%s" $code)
)
}}
diff --git a/layouts/api-reference.markdown.md b/layouts/api-reference.markdown.md
index 261a5d2d7544..182a063d3cb1 100644
--- a/layouts/api-reference.markdown.md
+++ b/layouts/api-reference.markdown.md
@@ -78,9 +78,16 @@
{{- $body := partial "api-ref/resolve.html" (dict "api" $api "node" .) }}
**Request body**{{ with $body.description }} — {{ . | strings.TrimSpace }}{{ end }}
-{{ with index $body.content "application/json" -}}
-{{- template "api-ref-md-schema-link" (dict "schema" .schema) }}
-{{ range $exName, $ex := .examples }}{{ with $ex.value }}
+{{- $ct := "" -}}
+{{- $media := dict -}}
+{{- range $n, $m := $body.content -}}
+ {{- if not $ct -}}{{- $ct = $n -}}{{- $media = $m -}}{{- end -}}
+{{- end -}}
+{{ if $ct }}
+Content type: `{{ $ct }}`
+
+{{ template "api-ref-md-schema-link" (dict "schema" $media.schema) }}
+{{ range $exName, $ex := $media.examples }}{{ with $ex.value }}
```json
{{ . | jsonify (dict "indent" " ") }}
```
@@ -94,9 +101,14 @@
{{ range $code, $resp := . -}}
{{- $r := partial "api-ref/resolve.html" (dict "api" $api "node" $resp) }}
`{{ $code }}` — {{ with $r.description }}{{ . | strings.TrimSpace }}{{ end }}
-{{ with index $r.content "application/json" }}
-{{ template "api-ref-md-schema-link" (dict "schema" .schema) }}
-{{ range $exName, $ex := .examples }}{{ with $ex.value }}
+{{- $ct := "" -}}
+{{- $media := dict -}}
+{{- range $n, $m := $r.content -}}
+ {{- if not $ct -}}{{- $ct = $n -}}{{- $media = $m -}}{{- end -}}
+{{- end }}
+{{ if $ct }}
+{{ template "api-ref-md-schema-link" (dict "schema" $media.schema) }}
+{{ range $exName, $ex := $media.examples }}{{ with $ex.value }}
```json
{{ . | jsonify (dict "indent" " ") }}
```
From 4468f79c07d046893dbfe64d339e6dd3c9b2527b Mon Sep 17 00:00:00 2001
From: David Karlsson <35727626+dvdksn@users.noreply.github.com>
Date: Tue, 26 May 2026 16:56:04 +0200
Subject: [PATCH 8/9] sbx: scaffold a Use the API guide for governance docs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Adds content/manuals/ai/sandboxes/governance/using-the-api.md as a
task-oriented companion to the reference. The page sits between
Monitoring and the AI Governance reference link in the sidebar
(weight 28). Authentication is fully written out — a portable JWT
exchange snippet that keeps the credential out of shell history and
out of the process env. The walkthrough and recipe sections are TODO
scaffolds for follow-up content authoring.
Co-Authored-By: Claude Opus 4.7
---
.../ai/sandboxes/governance/using-the-api.md | 144 ++++++++++++++++++
1 file changed, 144 insertions(+)
create mode 100644 content/manuals/ai/sandboxes/governance/using-the-api.md
diff --git a/content/manuals/ai/sandboxes/governance/using-the-api.md b/content/manuals/ai/sandboxes/governance/using-the-api.md
new file mode 100644
index 000000000000..427641e92df9
--- /dev/null
+++ b/content/manuals/ai/sandboxes/governance/using-the-api.md
@@ -0,0 +1,144 @@
+---
+title: Use the Governance API
+linkTitle: Use the API
+description: Authenticate against the Governance API and work through common policy management tasks.
+keywords: governance api, policy api, authentication, jwt, token, organization policy, walkthrough, recipes
+weight: 28
+---
+
+The [Governance API](/reference/api/ai-governance/) lets you manage
+organization policies programmatically — useful for syncing policies from
+infrastructure-as-code, automating team onboarding, or integrating with
+internal tooling. This page walks through authentication and a few common
+workflows. For the full endpoint surface, see the
+[API reference](/reference/api/ai-governance/).
+
+## Prerequisites
+
+
+
+- A Docker Hub organization on a plan that includes AI Governance
+- A Docker Hub account with administrator permissions on that organization
+- `curl` and `jq` available locally — the examples below use both
+
+## Authenticate
+
+The API accepts short-lived JWT bearer tokens obtained by exchanging Docker
+Hub credentials at the login endpoint. Tokens expire after a short period;
+when a request returns `401`, request a fresh one.
+
+The login endpoint accepts a password, a personal access token (PAT), or an
+organization access token (OAT) in the `password` field. PAT and OAT strings
+cannot be used directly as bearer tokens — they must be exchanged at the
+login endpoint first.
+
+Exchange your credential for a JWT and store it in a shell variable:
+
+```sh
+printf "Docker Hub password or token: " >&2
+read -s PW; echo
+TOKEN=$(curl -sS -X POST https://hub.docker.com/v2/users/login \
+ -H 'Content-Type: application/json' \
+ -d "{\"username\":\"\",\"password\":\"$PW\"}" \
+ | jq -r .token)
+unset PW
+```
+
+`read -s` keeps the credential out of shell history. `TOKEN` is a normal
+shell variable, not exported, so it isn't visible to child processes. Open
+a new shell to discard it.
+
+Every subsequent API call references the same `$TOKEN`:
+
+```sh
+curl -H "Authorization: Bearer $TOKEN" \
+ https://hub.docker.com/api/governance/v1/orgs//policies
+```
+
+The copy-as-cURL buttons in the [API reference](/reference/api/ai-governance/)
+emit commands that already reference `$TOKEN`, so they paste in unchanged
+after the snippet above.
+
+## Walkthrough: Create and populate a policy
+
+
+
+### 1. Create the policy
+
+
+
+```sh
+# TODO
+```
+
+### 2. Add a rule
+
+
+
+```sh
+# TODO
+```
+
+### 3. Verify the policy contents
+
+
+
+```sh
+# TODO
+```
+
+### 4. Update or remove a rule
+
+
+
+```sh
+# TODO
+```
+
+## Common patterns
+
+### Scope a policy to teams or profiles
+
+
+
+### Sync policies from version control
+
+
+
+### Compose with local delegation
+
+
+
+### Account for propagation
+
+
+
+## Errors
+
+
+
+## Next steps
+
+- [API reference](/reference/api/ai-governance/) — every endpoint, request,
+ and response schema
+- [Policy concepts](concepts.md) — resource model, rule syntax, and
+ evaluation
+- [Organization policy](org.md) — managing the same data from the Admin
+ Console
+- [Monitoring](monitoring.md) — observing policies in effect on developer
+ machines
From 5d453f308ae8de97f12fa88f72345275027b755a Mon Sep 17 00:00:00 2001
From: David Karlsson <35727626+dvdksn@users.noreply.github.com>
Date: Tue, 26 May 2026 17:17:56 +0200
Subject: [PATCH 9/9] sbx: use standard governance callout and "Docker account"
wording
Replaces the bespoke prerequisites bullet with the same paid-
subscription callout that ai/sandboxes/governance/_index.md and org.md
use, drops "Docker Hub" in favor of "Docker" in prose, and switches
the access-requirement wording to "owner role in the organization".
Co-Authored-By: Claude Opus 4.7
---
.../ai/sandboxes/governance/using-the-api.md | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/content/manuals/ai/sandboxes/governance/using-the-api.md b/content/manuals/ai/sandboxes/governance/using-the-api.md
index 427641e92df9..7a1373075dfc 100644
--- a/content/manuals/ai/sandboxes/governance/using-the-api.md
+++ b/content/manuals/ai/sandboxes/governance/using-the-api.md
@@ -15,17 +15,20 @@ workflows. For the full endpoint surface, see the
## Prerequisites
-
+> [!NOTE]
+> Sandbox organization governance is available on a separate paid
+> subscription.
+> [Contact Docker Sales](https://www.docker.com/products/ai-governance/#contact-sales)
+> to request access.
-- A Docker Hub organization on a plan that includes AI Governance
-- A Docker Hub account with administrator permissions on that organization
+- A Docker account with the owner role in the organization
- `curl` and `jq` available locally — the examples below use both
## Authenticate
-The API accepts short-lived JWT bearer tokens obtained by exchanging Docker
-Hub credentials at the login endpoint. Tokens expire after a short period;
-when a request returns `401`, request a fresh one.
+The API accepts short-lived JWT bearer tokens obtained by exchanging your
+Docker credentials at the login endpoint. Tokens expire after a short
+period; when a request returns `401`, request a fresh one.
The login endpoint accepts a password, a personal access token (PAT), or an
organization access token (OAT) in the `password` field. PAT and OAT strings
@@ -35,7 +38,7 @@ login endpoint first.
Exchange your credential for a JWT and store it in a shell variable:
```sh
-printf "Docker Hub password or token: " >&2
+printf "Docker password or token: " >&2
read -s PW; echo
TOKEN=$(curl -sS -X POST https://hub.docker.com/v2/users/login \
-H 'Content-Type: application/json' \