mirror of
https://github.com/docker/docs.git
synced 2026-06-19 07:35:16 +00:00
sbx: restructure governance docs and add API reference (#25162)
## Summary Restructures the Docker AI Governance documentation under \`/ai/sandboxes/governance/\` and adds the supporting API reference. Preview links: - https://deploy-preview-25162--docsdocker.netlify.app/ai/sandboxes/governance/ - https://deploy-preview-25162--docsdocker.netlify.app/reference/api/ai-governance/ ### Information architecture The existing \`security/governance\` and \`security/policy\` pages are merged into a new top-level \`governance\` section so local-policy and org-policy sit side by side instead of being split across unrelated parents: - \`/ai/sandboxes/governance/\` — section landing; explains local + org as layered enforcement - \`/ai/sandboxes/governance/concepts/\` — resource model, rule syntax, evaluation, precedence - \`/ai/sandboxes/governance/local/\` — \`sbx policy\` CLI for individual machines - \`/ai/sandboxes/governance/org/\` — Admin Console flow (was \`security/governance.md\`) - \`/ai/sandboxes/governance/monitoring/\` — \`sbx policy ls\` / \`sbx policy log\` ### API reference \`/reference/api/ai-governance/\` renders the Governance OpenAPI spec vendored at \`content/reference/api/ai-governance/api.yaml\` from \`docker/governor-services\`. Operations, schemas, examples, and status codes are fully driven by the spec — future updates land via re-vendor, not in-repo edits. Anything wrong in the rendered reference should be fixed upstream and re-vendored here. The spec has been re-vendored to the latest upstream version, which updated the server URL to \`hub.docker.com/v2\` and added the \`/governance/\` prefix to all API paths. ### Review focus 1. The \`/ai/sandboxes/governance/\` landing — does the local + org framing match how the product is positioned? 2. \`/reference/api/ai-governance/\` — does the rendered spec match the source of truth, and is anything important missing? Generated by Claude Code --------- Co-authored-by: Louis-Arnaud <la.catoire@gmail.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -42,7 +42,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/) 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
|
||||
|
||||
@@ -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
|
||||
@@ -26,11 +26,10 @@ Your Docker account email is only used for authentication, not marketing.
|
||||
|
||||
Yes. Admins can centrally manage network and filesystem policies from the
|
||||
Docker Admin Console. Rules defined there apply to every sandbox in the
|
||||
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.
|
||||
organization. When organization governance is active, it replaces local rules
|
||||
set with `sbx policy` — local rules are no longer evaluated.
|
||||
|
||||
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 +98,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/),
|
||||
[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
|
||||
|
||||
@@ -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
|
||||
@@ -270,4 +270,4 @@ working tree are unaffected.
|
||||
- [Credentials](security/credentials.md) — credential storage and management
|
||||
- [Workspace isolation](security/isolation.md#workspace-isolation) — what
|
||||
the agent can affect on your host, and how to review changes
|
||||
- [Policies](security/policy.md) — control outbound access
|
||||
- [Governance](governance/) — control outbound access
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
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, and
|
||||
only one applies at a time:
|
||||
|
||||
**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](/reference/api/ai-governance/). Rules defined at the org level apply
|
||||
uniformly across every sandbox in the organization. When organization
|
||||
governance is active, it replaces local policy entirely: local `sbx policy`
|
||||
rules are no longer evaluated. 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](/reference/api/ai-governance/): manage org policies
|
||||
programmatically via the Governance API
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: AI Governance API
|
||||
linkTitle: API reference
|
||||
description: Programmatic management of Docker AI Governance policies and rules via HTTP+JSON.
|
||||
weight: 30
|
||||
build:
|
||||
render: never
|
||||
sidebar:
|
||||
goto: /reference/api/ai-governance/
|
||||
---
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
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](/reference/api/ai-governance/). 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 whichever policy is active. When organization
|
||||
governance is active, only organization rules are evaluated; local rules have
|
||||
no effect.
|
||||
|
||||
## Precedence
|
||||
|
||||
Local and organization policies don't combine. Which one applies depends on
|
||||
whether your organization has governance enabled:
|
||||
|
||||
- **No organization governance**: local rules determine what sandboxes can
|
||||
access.
|
||||
- **Organization governance active**: organization rules apply across all
|
||||
developer machines, and local rules are not evaluated. Local rules still
|
||||
appear in `sbx policy ls`, but with an `inactive` status.
|
||||
|
||||
Within the active policy, deny rules beat allow rules regardless of specificity
|
||||
or order.
|
||||
@@ -0,0 +1,155 @@
|
||||
---
|
||||
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 apply only when your organization doesn't enforce governance:
|
||||
|
||||
- **No org governance**: local rules fully control what sandboxes can access.
|
||||
- **Org governance active**: the organization policy replaces local policy.
|
||||
Local rules are inactive, and `sbx policy allow` and `sbx policy deny` have
|
||||
no effect. Local rules still appear in `sbx policy ls` with an `inactive`
|
||||
status.
|
||||
|
||||
See [Organization policy](org.md) for how organization governance works.
|
||||
|
||||
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. Run `sbx
|
||||
policy ls` to check: if the output starts with a `Governance: managed by <org>`
|
||||
header, org governance is active. When it's active, the organization policy
|
||||
replaces local policy, so your rules appear with `inactive` status and have no
|
||||
effect.
|
||||
|
||||
Organization policy can't be supplemented from your machine. To change what
|
||||
your sandboxes can access, ask your admin to update the organization policy in
|
||||
the Admin Console.
|
||||
|
||||
### A domain is still blocked after adding an allow rule
|
||||
|
||||
If a domain remains blocked after you add a local allow rule, your organization
|
||||
likely enforces governance, which makes local rules inactive. Run `sbx policy
|
||||
ls` to check whether org governance is active and whether your rule shows an
|
||||
`inactive` status. If so, the block can only be lifted by updating the org
|
||||
policy in the Admin Console or via the [API](/reference/api/ai-governance/).
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
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:<name>` 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, local rules are not evaluated and show as `inactive`).
|
||||
- `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 api.anthropic.com
|
||||
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.
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
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/
|
||||
---
|
||||
|
||||
[Local policies](local.md) give individual developers control over what their
|
||||
sandboxes can access. Organization policy moves that control to the admin level:
|
||||
rules defined in the [Docker Admin Console](https://app.docker.com/admin) apply
|
||||
uniformly to every sandbox in the organization. When organization governance is
|
||||
active, it replaces local `sbx policy` rules entirely — local rules are no
|
||||
longer evaluated and can't be used to supplement or override the organization
|
||||
policy.
|
||||
|
||||
Admins can manage organization policies through the Admin Console UI or
|
||||
programmatically using the [Governance API](/reference/api/ai-governance/).
|
||||
|
||||
> [!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.
|
||||
|
||||
## Network policies
|
||||
|
||||
### 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 and an
|
||||
action (allow or deny). You can add multiple entries at once, one per line.
|
||||
|
||||
For the full syntax reference (exact hostnames, wildcard subdomains, port
|
||||
suffixes, and CIDR ranges), see [Policy concepts](concepts.md#network-rules).
|
||||
|
||||
When organization governance is active, local network rules are not evaluated.
|
||||
The organization policy is the only policy in effect. Local rules still appear
|
||||
in `sbx policy ls` but with an `inactive` status. See [Monitoring](monitoring.md)
|
||||
for how to read the rule view.
|
||||
|
||||
## Filesystem policies
|
||||
|
||||
Filesystem policies control which host paths a sandbox can mount as
|
||||
workspaces. By default, sandboxes can mount any directory the user has
|
||||
access to.
|
||||
|
||||
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).
|
||||
|
||||
For path pattern syntax including the difference between `*` and `**`, see
|
||||
[Policy concepts](concepts.md#filesystem-rules).
|
||||
|
||||
## Precedence
|
||||
|
||||
Within the active policy, deny rules beat allow rules. If a domain matches both,
|
||||
it's blocked regardless of specificity. Outbound traffic is blocked unless a
|
||||
rule allows it.
|
||||
|
||||
When organization governance is active, local rules are not evaluated. Only
|
||||
organization rules set in the Admin Console determine what is allowed or
|
||||
denied, and they can't be supplemented or overridden from a developer's machine.
|
||||
The same model applies to filesystem policies: organization rules replace local
|
||||
behavior entirely.
|
||||
|
||||
To unblock a domain when organization governance is active, update the rule in
|
||||
the Admin Console or via the [API](/reference/api/ai-governance/). Without
|
||||
organization governance, remove the local rule with `sbx policy rm`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Policy changes not taking effect
|
||||
|
||||
After updating organization policies in the Admin Console, changes take up
|
||||
to 5 minutes to propagate to developer machines. To apply changes
|
||||
immediately, users can run `sbx policy reset`, which stops the daemon and
|
||||
forces it to pull the latest organization policies on the next `sbx`
|
||||
command.
|
||||
|
||||
> [!WARNING]
|
||||
> `sbx policy reset` deletes all locally configured policy rules. The command
|
||||
> prompts for confirmation before proceeding.
|
||||
|
||||
### Sandbox cannot mount workspace
|
||||
|
||||
If a sandbox fails to mount with a `mount policy denied` error, verify that
|
||||
the filesystem allow rule in the Admin Console uses `**` rather than `*`. A
|
||||
single `*` doesn't match across directory separators.
|
||||
@@ -92,11 +92,9 @@ On a single developer's machine, network and filesystem policies are
|
||||
configured locally with `sbx policy`. Admins can also centrally define those
|
||||
policies in the Docker Admin Console. When organization governance is active,
|
||||
the centrally defined rules 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 add
|
||||
additional allow rules.
|
||||
organization and replace local rules, which are no longer evaluated.
|
||||
|
||||
See [Organization governance](governance/) for details.
|
||||
See [Organization policy](../governance/org.md) for details.
|
||||
|
||||
## Learn more
|
||||
|
||||
@@ -105,6 +103,5 @@ 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
|
||||
|
||||
@@ -16,10 +16,10 @@ it (deny-by-default). All non-HTTP protocols (raw TCP, UDP including DNS, and
|
||||
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
|
||||
manages sandbox policies centrally, those rules apply on top of the defaults
|
||||
described here. See [Organization governance](governance.md).
|
||||
Run `sbx policy ls` to see the active network rules for your installation.
|
||||
Rules can be customized per machine with the `sbx policy` CLI, or managed
|
||||
centrally across your organization from the Admin Console. Org-level rules
|
||||
take precedence over local rules. See [Governance](../governance/).
|
||||
|
||||
## Workspace defaults
|
||||
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
---
|
||||
title: Organization governance
|
||||
linkTitle: Org governance
|
||||
weight: 35
|
||||
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
|
||||
---
|
||||
|
||||
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).
|
||||
|
||||
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.
|
||||
|
||||
> [!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.
|
||||
|
||||
## Network policies
|
||||
|
||||
### 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.
|
||||
|
||||
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.
|
||||
|
||||
### 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
|
||||
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.
|
||||
|
||||
Delegated local rules can expand access for domains the organization hasn't
|
||||
explicitly denied, but can't override organization-level deny rules. This
|
||||
applies to exact matches and wildcard matches alike; if the organization denies
|
||||
`*.example.com`, a local allow for `api.example.com` has no effect because the
|
||||
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
|
||||
organization hasn't denied `api.example.com`
|
||||
- `sbx policy allow network -g build.corp.internal` — no effect, because the
|
||||
organization denies `*.corp.internal`
|
||||
|
||||
#### Blocked values in delegated rules
|
||||
|
||||
To prevent overly broad rules from undermining the organization's policy,
|
||||
certain catch-all values are blocked in delegated local rules:
|
||||
|
||||
- Domain patterns: `*`, `**`, `*.com`, `**.com`, `*.*`, `**.**`
|
||||
- CIDR ranges: `0.0.0.0/0`, `::/0`
|
||||
|
||||
Scoped wildcards like `*.example.com` are still allowed. If a user attempts
|
||||
to use a blocked value, `sbx policy` returns an error immediately.
|
||||
|
||||
## Filesystem policies
|
||||
|
||||
Filesystem policies control which host paths a sandbox can mount as
|
||||
workspaces. By default, sandboxes can mount any directory the user has
|
||||
access to.
|
||||
|
||||
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 `~`.
|
||||
|
||||
## Precedence
|
||||
|
||||
Within any layer, deny rules beat allow rules. If a domain matches both, it's
|
||||
blocked regardless of specificity. Outbound traffic is blocked unless a rule
|
||||
allows it.
|
||||
|
||||
When organization governance is active, local rules are not evaluated. Only
|
||||
organization rules set in the Admin Console determine what is allowed or
|
||||
denied. Organization-level denials can't be overridden locally.
|
||||
|
||||
If the admin [delegates](#delegate-rules-to-local-policy) a rule type to
|
||||
local policy by turning on the **User defined** setting, local rules of
|
||||
that type are also evaluated alongside organization rules. Delegated local
|
||||
rules can expand access for domains the organization hasn't explicitly
|
||||
denied, but can't override organization-level denials.
|
||||
|
||||
The same model applies to filesystem policies: organization-level rules take
|
||||
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.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Policy changes not taking effect
|
||||
|
||||
After updating organization policies in the Admin Console, changes take up
|
||||
to 5 minutes to propagate to developer machines. To apply changes
|
||||
immediately, users can run `sbx policy reset`, which stops the daemon and
|
||||
forces it to pull the latest organization policies on the next `sbx`
|
||||
command.
|
||||
|
||||
> [!WARNING]
|
||||
> `sbx policy reset` deletes all locally configured policy rules. The command
|
||||
> prompts for confirmation before proceeding.
|
||||
|
||||
### Sandbox cannot mount workspace
|
||||
|
||||
If a sandbox fails to mount with a `mount policy denied` error, verify that
|
||||
the filesystem allow rule in the Admin Console uses `**` rather than `*`. A
|
||||
single `*` doesn't match across directory separators.
|
||||
@@ -37,7 +37,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/). 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.
|
||||
|
||||
@@ -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:<name>` 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.
|
||||
@@ -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
|
||||
|
||||
@@ -442,7 +442,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.
|
||||
|
||||
@@ -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.** Organization policies take precedence over local sandbox
|
||||
policies and cannot be overridden by individual users.
|
||||
|
||||
**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/v2
|
||||
|
||||
security:
|
||||
- bearerAuth: []
|
||||
|
||||
paths:
|
||||
/orgs/{org_name}/governance/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}/governance/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}/governance/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}/governance/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/merge-patch+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 <token>` 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. |
|
||||
|
||||
PAT and OAT strings can't 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. 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`. Present when `rules` is non-empty;
|
||||
absent when the allowlist has no rules.
|
||||
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, or the org is not entitled to use governance.
|
||||
`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, or the org is not
|
||||
entitled to use governance.
|
||||
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
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
layout: api-reference
|
||||
title: Docker AI Governance API
|
||||
linkTitle: AI Governance
|
||||
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
|
||||
aliases:
|
||||
- /ai/sandboxes/governance/api/
|
||||
---
|
||||
@@ -0,0 +1,139 @@
|
||||
{{/* 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 `<name>`. 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. Picks the first content type declared. */}}
|
||||
{{- with $op.requestBody -}}
|
||||
{{- $body := partial "api-ref/resolve.html" (dict "api" $api "node" .) -}}
|
||||
{{- $ct := "" -}}
|
||||
{{- $media := dict -}}
|
||||
{{- range $name, $m := $body.content -}}
|
||||
{{- if not $ct -}}{{- $ct = $name -}}{{- $media = $m -}}{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if $ct -}}
|
||||
{{- $lines = $lines | append (printf "-H 'Content-Type: %s'" $ct) -}}
|
||||
{{- $example := "" -}}
|
||||
{{- with $media.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 ") -}}
|
||||
@@ -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"
|
||||
-}}
|
||||
|
||||
|
||||
<p
|
||||
class="mb-3 text-xs font-semibold tracking-wider text-gray-500 uppercase dark:text-gray-400"
|
||||
>
|
||||
On this page
|
||||
</p>
|
||||
<ul class="space-y-1">
|
||||
{{- with $api.components }}
|
||||
{{- if .securitySchemes }}
|
||||
<li>
|
||||
<a
|
||||
href="#authentication"
|
||||
class="block py-0.5 text-gray-700 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400"
|
||||
>
|
||||
Authentication
|
||||
</a>
|
||||
</li>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $api.tags }}
|
||||
{{- $tagName := .name }}
|
||||
<li class="mt-3">
|
||||
<a
|
||||
href="#tag-{{ $tagName | urlize }}"
|
||||
class="block py-0.5 text-xs font-semibold tracking-wider text-gray-500 uppercase hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400"
|
||||
>
|
||||
{{ $tagName | title }}
|
||||
</a>
|
||||
<ul class="mt-1 space-y-0.5">
|
||||
{{- range $path, $item := $paths }}
|
||||
{{- range $methods }}
|
||||
{{- $method := . }}
|
||||
{{- with index $item $method }}
|
||||
{{- $op := . }}
|
||||
{{- if in $op.tags $tagName }}
|
||||
{{- $label := $op.summary | default $op.operationId | default $path }}
|
||||
<li>
|
||||
<a
|
||||
href="#operation-{{ $op.operationId }}"
|
||||
class="flex items-baseline gap-2 py-0.5 text-gray-700 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400"
|
||||
>
|
||||
<span
|
||||
class="{{ index $methodColors $method }} w-12 flex-none font-mono text-[10px] font-bold uppercase"
|
||||
>{{ upper $method }}</span
|
||||
>
|
||||
<span class="min-w-0 truncate">{{ $label }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
</ul>
|
||||
</li>
|
||||
{{- end }}
|
||||
{{- with $api.components }}
|
||||
{{- if .schemas }}
|
||||
<li class="mt-3">
|
||||
<a
|
||||
href="#schemas"
|
||||
class="block py-0.5 text-xs font-semibold tracking-wider text-gray-500 uppercase hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400"
|
||||
>
|
||||
Schemas
|
||||
</a>
|
||||
</li>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
</ul>
|
||||
@@ -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 -}}
|
||||
@@ -0,0 +1,700 @@
|
||||
{{/* 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 -}}
|
||||
|
||||
|
||||
<div class="flex w-full min-w-0 gap-8">
|
||||
<div class="min-w-0 flex-1">
|
||||
{{ partial "breadcrumbs.html" . }}
|
||||
<h1 data-pagefind-weight="10" class="mb-2 text-3xl font-bold">
|
||||
{{ .Title | safeHTML }}
|
||||
</h1>
|
||||
{{ with .Description }}
|
||||
<p class="text-gray-600 dark:text-gray-400">{{ . }}</p>
|
||||
{{ end }}
|
||||
{{ template "api-ref-meta" (dict "api" $api "spec" $specRes) }}
|
||||
{{ partialCached "md-dropdown.html" "-" "-" }}
|
||||
|
||||
{{ template "api-ref-overview" $api }}
|
||||
{{ template "api-ref-auth" $api }}
|
||||
{{ template "api-ref-tags" $api }}
|
||||
{{ template "api-ref-schemas" $api }}
|
||||
</div>
|
||||
|
||||
<aside class="hidden w-56 flex-none lg:block">
|
||||
<nav
|
||||
class="sticky top-20 max-h-[calc(100vh-6rem)] overflow-y-auto text-sm"
|
||||
>
|
||||
{{ partial "api-ref/nav.html" $api }}
|
||||
</nav>
|
||||
</aside>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{/* ── Meta strip: version, base URL, download link ──────────────────────── */}}
|
||||
{{ define "api-ref-meta" }}
|
||||
{{- $api := .api -}}
|
||||
{{- $spec := .spec -}}
|
||||
<div class="mt-4 flex flex-wrap items-center gap-x-6 gap-y-2 text-sm">
|
||||
{{- with $api.info }}
|
||||
{{- with .version }}
|
||||
<div>
|
||||
<span class="font-medium text-gray-500 dark:text-gray-400"
|
||||
>Version</span
|
||||
>
|
||||
<code
|
||||
class="ml-1 rounded bg-gray-100 px-1.5 py-0.5 text-xs dark:bg-gray-800"
|
||||
>{{ . }}</code
|
||||
>
|
||||
</div>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $api.servers }}
|
||||
<div>
|
||||
<span class="font-medium text-gray-500 dark:text-gray-400"
|
||||
>Base URL</span
|
||||
>
|
||||
<code
|
||||
class="ml-1 rounded bg-gray-100 px-1.5 py-0.5 text-xs text-blue-600 dark:bg-gray-800 dark:text-blue-400"
|
||||
>{{ .url }}</code
|
||||
>
|
||||
</div>
|
||||
{{- end }}
|
||||
<a
|
||||
href="{{ $spec.Permalink }}"
|
||||
class="ml-auto text-blue-600 hover:underline dark:text-blue-400"
|
||||
>Download OpenAPI specification</a
|
||||
>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{/* ── Overview ─────────────────────────────────────────────────────────── */}}
|
||||
{{ define "api-ref-overview" }}
|
||||
{{- $api := . -}}
|
||||
{{- with $api.info }}
|
||||
{{- with .description }}
|
||||
<section class="mb-10">
|
||||
<div class="prose dark:prose-invert max-w-none">
|
||||
{{ . | markdownify }}
|
||||
</div>
|
||||
</section>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* ── Authentication ───────────────────────────────────────────────────── */}}
|
||||
{{ define "api-ref-auth" }}
|
||||
{{- $api := . -}}
|
||||
{{- with $api.components }}
|
||||
{{- with .securitySchemes }}
|
||||
<section class="mb-12">
|
||||
<h2
|
||||
id="authentication"
|
||||
class="mb-4 scroll-mt-24 text-2xl font-semibold"
|
||||
>
|
||||
Authentication
|
||||
</h2>
|
||||
{{- range $name, $scheme := . }}
|
||||
<div class="mb-4 flex flex-wrap items-center gap-x-4 gap-y-1 text-sm">
|
||||
<code
|
||||
class="rounded bg-gray-100 px-1.5 py-0.5 text-xs dark:bg-gray-800"
|
||||
>{{ $name }}</code
|
||||
>
|
||||
{{- with $scheme.type }}
|
||||
<span class="text-gray-500 dark:text-gray-400"
|
||||
>type: <code class="text-xs">{{ . }}</code></span
|
||||
>
|
||||
{{- end }}
|
||||
{{- with $scheme.scheme }}
|
||||
<span class="text-gray-500 dark:text-gray-400"
|
||||
>scheme: <code class="text-xs">{{ . }}</code></span
|
||||
>
|
||||
{{- end }}
|
||||
{{- with $scheme.bearerFormat }}
|
||||
<span class="text-gray-500 dark:text-gray-400"
|
||||
>bearer format: <code class="text-xs">{{ . }}</code></span
|
||||
>
|
||||
{{- end }}
|
||||
</div>
|
||||
{{- with $scheme.description }}
|
||||
<div class="prose dark:prose-invert max-w-none">
|
||||
{{ . | markdownify }}
|
||||
</div>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
</section>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* ── Endpoints grouped by tag ─────────────────────────────────────────── */}}
|
||||
{{ define "api-ref-tags" }}
|
||||
{{- $api := . -}}
|
||||
{{- $paths := $api.paths -}}
|
||||
{{- $methods := slice "get" "post" "put" "patch" "delete" -}}
|
||||
{{- range $api.tags }}
|
||||
{{- $tagName := .name -}}
|
||||
<section class="mb-14">
|
||||
<h2
|
||||
id="tag-{{ $tagName | urlize }}"
|
||||
class="mb-1 scroll-mt-24 text-2xl font-semibold"
|
||||
>
|
||||
{{ $tagName | title }}
|
||||
</h2>
|
||||
{{- with .description }}
|
||||
<p class="mb-6 text-sm text-gray-500 dark:text-gray-400">{{ . }}</p>
|
||||
{{- end }}
|
||||
|
||||
{{- range $path, $item := $paths }}
|
||||
{{/* Resolve shared (path-level) parameters once. */}}
|
||||
{{- $sharedParams := slice -}}
|
||||
{{- with index $item "parameters" -}}
|
||||
{{- range . -}}
|
||||
{{- $p := partial "api-ref/resolve.html" (dict "api" $api "node" .) -}}
|
||||
{{- $sharedParams = $sharedParams | append $p -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- range $methods -}}
|
||||
{{- $method := . -}}
|
||||
{{- with index $item $method -}}
|
||||
{{- $op := . -}}
|
||||
{{- if in $op.tags $tagName -}}
|
||||
{{ template "api-ref-operation" (dict
|
||||
"api" $api
|
||||
"op" $op
|
||||
"method" $method
|
||||
"path" $path
|
||||
"sharedParams" $sharedParams
|
||||
)
|
||||
}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
</section>
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* ── Single operation card ────────────────────────────────────────────── */}}
|
||||
{{ define "api-ref-operation" }}
|
||||
{{- $api := .api -}}
|
||||
{{- $op := .op -}}
|
||||
{{- $method := .method -}}
|
||||
{{- $path := .path -}}
|
||||
{{- $sharedParams := .sharedParams -}}
|
||||
|
||||
{{- $methodColors := dict
|
||||
"get" "bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300"
|
||||
"post" "bg-green-100 text-green-800 dark:bg-green-900/40 dark:text-green-300"
|
||||
"put" "bg-yellow-100 text-yellow-800 dark:bg-yellow-900/40 dark:text-yellow-300"
|
||||
"patch" "bg-yellow-100 text-yellow-800 dark:bg-yellow-900/40 dark:text-yellow-300"
|
||||
"delete" "bg-red-100 text-red-800 dark:bg-red-900/40 dark:text-red-300"
|
||||
-}}
|
||||
|
||||
{{- $anchor := "" -}}
|
||||
{{- with $op.operationId -}}
|
||||
{{- $anchor = printf "operation-%s" . -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- $curl := partial "api-ref/curl.html" (dict
|
||||
"api" $api
|
||||
"op" $op
|
||||
"method" $method
|
||||
"path" $path
|
||||
"sharedParams" $sharedParams
|
||||
)
|
||||
-}}
|
||||
|
||||
|
||||
<div
|
||||
{{ with $anchor }}id="{{ . }}"{{ end }}
|
||||
class="mb-8 scroll-mt-24 overflow-hidden rounded-lg border border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-3 border-b border-gray-200 bg-gray-50 px-4 py-3 dark:border-gray-700 dark:bg-gray-900"
|
||||
>
|
||||
<span
|
||||
class="{{ index $methodColors $method }} inline-block rounded px-2 py-0.5 font-mono text-xs font-bold uppercase"
|
||||
>{{ upper $method }}</span
|
||||
>
|
||||
<code class="font-mono text-sm">{{ $path }}</code>
|
||||
<div class="ml-auto flex items-center gap-3">
|
||||
{{- with $op.summary }}
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>{{ . }}</span
|
||||
>
|
||||
{{- end }}
|
||||
<button
|
||||
x-data="{ copied: false }"
|
||||
@click="navigator.clipboard.writeText({{ $curl | jsonify }}).then(() => { copied = true; setTimeout(() => copied = false, 2000) })"
|
||||
:title="copied ? 'Copied!' : 'Copy as cURL'"
|
||||
class="inline-flex cursor-pointer items-center gap-1 rounded border border-gray-200 bg-white px-2 py-1 text-xs font-medium text-gray-700 transition-colors hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700"
|
||||
>
|
||||
<span class="icon-svg icon-sm" x-show="!copied" x-cloak>
|
||||
{{ partialCached "icon" "document-duplicate" "document-duplicate" }}
|
||||
</span>
|
||||
<span
|
||||
class="icon-svg icon-sm text-green-600 dark:text-green-400"
|
||||
x-show="copied"
|
||||
x-cloak
|
||||
>
|
||||
{{ partialCached "icon" "check-circle" "check-circle" }}
|
||||
</span>
|
||||
<span x-text="copied ? 'Copied' : 'cURL'"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-5">
|
||||
{{- with $op.description }}
|
||||
<div class="prose dark:prose-invert prose-sm mb-5 max-w-none">
|
||||
{{ . | markdownify }}
|
||||
</div>
|
||||
{{- end }}
|
||||
|
||||
{{ template "api-ref-params" (dict
|
||||
"api" $api
|
||||
"op" $op
|
||||
"sharedParams" $sharedParams
|
||||
)
|
||||
}}
|
||||
|
||||
{{ template "api-ref-body" (dict "api" $api "op" $op) }}
|
||||
|
||||
{{ template "api-ref-responses" (dict "api" $api "op" $op) }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{/* ── Parameters table (path + operation; in: path/query/header) ───────── */}}
|
||||
{{ define "api-ref-params" }}
|
||||
{{- $api := .api -}}
|
||||
{{- $op := .op -}}
|
||||
{{- $params := .sharedParams -}}
|
||||
{{- with index $op "parameters" -}}
|
||||
{{- range . -}}
|
||||
{{- $p := partial "api-ref/resolve.html" (dict "api" $api "node" .) -}}
|
||||
{{- $params = $params | append $p -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if $params }}
|
||||
<div class="mb-5">
|
||||
<h4
|
||||
class="mb-2 text-xs font-semibold tracking-wider text-gray-500 uppercase dark:text-gray-400"
|
||||
>
|
||||
Parameters
|
||||
</h4>
|
||||
<table class="w-full border-collapse text-sm">
|
||||
<thead>
|
||||
<tr class="border-b border-gray-200 dark:border-gray-700">
|
||||
<th
|
||||
class="w-36 py-1.5 pr-4 text-left font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Name
|
||||
</th>
|
||||
<th
|
||||
class="w-16 py-1.5 pr-4 text-left font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
In
|
||||
</th>
|
||||
<th
|
||||
class="w-20 py-1.5 pr-4 text-left font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Type
|
||||
</th>
|
||||
<th
|
||||
class="w-20 py-1.5 pr-4 text-left font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Required
|
||||
</th>
|
||||
<th
|
||||
class="py-1.5 text-left font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Description
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{- range $params }}
|
||||
<tr
|
||||
class="border-b border-gray-100 last:border-0 dark:border-gray-800"
|
||||
>
|
||||
<td class="py-1.5 pr-4 align-top">
|
||||
<code class="rounded bg-gray-100 px-1 text-xs dark:bg-gray-800"
|
||||
>{{ .name }}</code
|
||||
>
|
||||
</td>
|
||||
<td
|
||||
class="py-1.5 pr-4 align-top text-xs text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{ .in }}
|
||||
</td>
|
||||
<td
|
||||
class="py-1.5 pr-4 align-top font-mono text-xs text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{ template "api-ref-type" (dict "api" $api "schema" .schema) }}
|
||||
</td>
|
||||
<td
|
||||
class="py-1.5 pr-4 align-top text-xs text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{ if .required }}yes{{ else }}no{{ end }}
|
||||
</td>
|
||||
<td
|
||||
class="py-1.5 align-top text-sm text-gray-600 dark:text-gray-400"
|
||||
>
|
||||
{{ with .description }}{{ . | markdownify }}{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
{{- end }}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* ── Request body ─────────────────────────────────────────────────────── */}}
|
||||
{{ define "api-ref-body" }}
|
||||
{{- $api := .api -}}
|
||||
{{- $op := .op -}}
|
||||
{{- with $op.requestBody }}
|
||||
{{- $body := partial "api-ref/resolve.html" (dict "api" $api "node" .) }}
|
||||
<div class="mb-5">
|
||||
<h4
|
||||
class="mb-2 text-xs font-semibold tracking-wider text-gray-500 uppercase dark:text-gray-400"
|
||||
>
|
||||
Request body
|
||||
</h4>
|
||||
{{- with $body.description }}
|
||||
<p class="mb-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ . | markdownify }}
|
||||
</p>
|
||||
{{- end }}
|
||||
{{- $ct := "" -}}
|
||||
{{- $media := dict -}}
|
||||
{{- range $name, $m := $body.content -}}
|
||||
{{- if not $ct -}}{{- $ct = $name -}}{{- $media = $m -}}{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if $ct }}
|
||||
<p class="mb-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
Content type:
|
||||
<code class="rounded bg-gray-100 px-1 dark:bg-gray-800"
|
||||
>{{ $ct }}</code
|
||||
>
|
||||
</p>
|
||||
{{ template "api-ref-schema-link" (dict "api" $api "schema" $media.schema) }}
|
||||
{{ template "api-ref-examples" (dict
|
||||
"examples" $media.examples
|
||||
"prefix" "req"
|
||||
)
|
||||
}}
|
||||
{{- end }}
|
||||
</div>
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* ── Responses ────────────────────────────────────────────────────────── */}}
|
||||
{{ define "api-ref-responses" }}
|
||||
{{- $api := .api -}}
|
||||
{{- $op := .op -}}
|
||||
{{- $statusColors := dict
|
||||
"200" "bg-green-100 text-green-800 dark:bg-green-900/40 dark:text-green-300"
|
||||
"201" "bg-green-100 text-green-800 dark:bg-green-900/40 dark:text-green-300"
|
||||
"204" "bg-green-100 text-green-800 dark:bg-green-900/40 dark:text-green-300"
|
||||
"400" "bg-yellow-100 text-yellow-800 dark:bg-yellow-900/40 dark:text-yellow-300"
|
||||
"401" "bg-red-100 text-red-800 dark:bg-red-900/40 dark:text-red-300"
|
||||
"403" "bg-red-100 text-red-800 dark:bg-red-900/40 dark:text-red-300"
|
||||
"404" "bg-red-100 text-red-800 dark:bg-red-900/40 dark:text-red-300"
|
||||
"409" "bg-yellow-100 text-yellow-800 dark:bg-yellow-900/40 dark:text-yellow-300"
|
||||
"500" "bg-red-100 text-red-800 dark:bg-red-900/40 dark:text-red-300"
|
||||
-}}
|
||||
{{- with $op.responses }}
|
||||
<div>
|
||||
<h4
|
||||
class="mb-2 text-xs font-semibold tracking-wider text-gray-500 uppercase dark:text-gray-400"
|
||||
>
|
||||
Responses
|
||||
</h4>
|
||||
<div class="space-y-2">
|
||||
{{- range $code, $resp := . }}
|
||||
{{- $r := partial "api-ref/resolve.html" (dict "api" $api "node" $resp) }}
|
||||
<details
|
||||
{{ if hasPrefix $code "2" }}open{{ end }}
|
||||
class="group rounded border border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
<summary
|
||||
class="flex cursor-pointer list-none items-center gap-2 rounded-t bg-gray-50 px-3 py-2 dark:bg-gray-900 [&::-webkit-details-marker]:hidden"
|
||||
>
|
||||
<span
|
||||
class="inline-block text-gray-400 transition-transform group-open:rotate-90 dark:text-gray-500"
|
||||
>▸</span
|
||||
>
|
||||
<span
|
||||
class="{{ index $statusColors $code | default "bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300" }} inline-block rounded px-1.5 py-0.5 font-mono text-xs font-semibold"
|
||||
>{{ $code }}</span
|
||||
>
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400"
|
||||
>{{ $r.description }}</span
|
||||
>
|
||||
</summary>
|
||||
{{- $ct := "" -}}
|
||||
{{- $media := dict -}}
|
||||
{{- range $name, $m := $r.content -}}
|
||||
{{- if not $ct -}}
|
||||
{{- $ct = $name -}}{{- $media = $m -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if $ct }}
|
||||
<div class="px-3 pt-2 pb-3">
|
||||
{{ template "api-ref-schema-link" (dict "api" $api "schema" $media.schema) }}
|
||||
{{ template "api-ref-examples" (dict
|
||||
"examples" $media.examples
|
||||
"prefix" (printf "resp-%s" $code)
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
{{- end }}
|
||||
</details>
|
||||
{{- end }}
|
||||
</div>
|
||||
</div>
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* ── Example block with optional tabs ─────────────────────────────────── */}}
|
||||
{{ define "api-ref-examples" }}
|
||||
{{- $examples := .examples -}}
|
||||
{{- $prefix := .prefix -}}
|
||||
{{- $tabs := slice -}}
|
||||
{{- range $name, $ex := $examples -}}
|
||||
{{- $tabs = $tabs | append (dict "name" $name "ex" $ex) -}}
|
||||
{{- end -}}
|
||||
{{- if $tabs }}
|
||||
{{- $first := (index $tabs 0).name -}}
|
||||
<div
|
||||
{{ if gt (len $tabs) 1 }}x-data="{ tab: {{ $first | jsonify }} }"{{ end }}
|
||||
>
|
||||
{{- if gt (len $tabs) 1 }}
|
||||
<div class="mb-2 flex flex-wrap gap-1">
|
||||
{{- range $tabs }}
|
||||
{{- $label := .name -}}
|
||||
{{- with .ex.summary }}{{ $label = . }}{{ end -}}
|
||||
<button
|
||||
@click="tab = {{ .name | jsonify }}"
|
||||
:class="tab === {{ .name | jsonify }} ? 'bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300' : 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400'"
|
||||
class="rounded px-2 py-0.5 text-xs font-medium"
|
||||
>
|
||||
{{ $label }}
|
||||
</button>
|
||||
{{- end }}
|
||||
</div>
|
||||
{{- end }}
|
||||
{{- range $tabs }}
|
||||
<div
|
||||
{{ if gt (len $tabs) 1 }}
|
||||
x-show="tab === {{ .name | jsonify }}"
|
||||
{{ end }}
|
||||
>
|
||||
{{- with .ex.value -}}
|
||||
<div
|
||||
class="syntax-light dark:syntax-dark not-prose overflow-hidden rounded border border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
{{ highlight (. | jsonify (dict "indent" " ")) "json" "" }}
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* ── Inline schema-reference link (used in body/response) ─────────────── */}}
|
||||
{{ define "api-ref-schema-link" }}
|
||||
{{- $api := .api -}}
|
||||
{{- $schema := .schema -}}
|
||||
{{- if reflect.IsMap $schema -}}
|
||||
{{- with index $schema "$ref" -}}
|
||||
{{- $parts := split . "/" -}}
|
||||
{{- $name := index $parts (sub (len $parts) 1) -}}
|
||||
<p class="mb-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
Schema:
|
||||
<a
|
||||
href="#schema-{{ $name | urlize }}"
|
||||
class="font-mono text-blue-600 hover:underline dark:text-blue-400"
|
||||
>{{ $name }}</a
|
||||
>
|
||||
</p>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{ end }}
|
||||
|
||||
{{/* ── Type rendering for parameter / property tables ───────────────────── */}}
|
||||
{{ define "api-ref-type" }}
|
||||
{{- $api := .api -}}
|
||||
{{- $schema := .schema -}}
|
||||
{{- if not (reflect.IsMap $schema) -}}
|
||||
—
|
||||
{{- else -}}
|
||||
{{- with index $schema "$ref" -}}
|
||||
{{- $parts := split . "/" -}}
|
||||
{{- $name := index $parts (sub (len $parts) 1) -}}
|
||||
<a
|
||||
href="#schema-{{ $name | urlize }}"
|
||||
class="text-blue-600 hover:underline dark:text-blue-400"
|
||||
>{{ $name }}</a
|
||||
>
|
||||
{{- else -}}
|
||||
{{- with $schema.type -}}
|
||||
{{- if eq . "array" -}}
|
||||
array<{{ template "api-ref-type" (dict "api" $api "schema" $schema.items) }}>
|
||||
{{- else -}}
|
||||
{{ . }}
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
{{- if $schema.allOf -}}
|
||||
object
|
||||
{{- else if $schema.anyOf -}}
|
||||
any
|
||||
{{- else if $schema.oneOf -}}
|
||||
any
|
||||
{{- else -}}
|
||||
—
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{ end }}
|
||||
|
||||
{{/* ── Schemas section ──────────────────────────────────────────────────── */}}
|
||||
{{ define "api-ref-schemas" }}
|
||||
{{- $api := . -}}
|
||||
{{- with $api.components -}}
|
||||
{{- with .schemas }}
|
||||
<section class="mb-12">
|
||||
<h2 id="schemas" class="mb-6 scroll-mt-24 text-2xl font-semibold">
|
||||
Schemas
|
||||
</h2>
|
||||
{{- range $name, $schema := . }}
|
||||
<div
|
||||
id="schema-{{ $name | urlize }}"
|
||||
class="mb-8 scroll-mt-24 rounded border border-gray-200 p-4 dark:border-gray-700"
|
||||
>
|
||||
<h3 class="mb-1 font-mono text-base font-semibold">{{ $name }}</h3>
|
||||
{{- with $schema.description }}
|
||||
<div
|
||||
class="prose dark:prose-invert prose-sm mb-3 max-w-none text-sm"
|
||||
>
|
||||
{{ . | markdownify }}
|
||||
</div>
|
||||
{{- end }}
|
||||
{{- with $schema.enum }}
|
||||
<p class="mb-3 text-sm text-gray-500 dark:text-gray-400">
|
||||
Enum:
|
||||
{{- range $i, $v := . -}}
|
||||
{{- if $i }},{{ end -}}
|
||||
<code
|
||||
class="rounded bg-gray-100 px-1 text-xs dark:bg-gray-800"
|
||||
>{{ $v }}</code
|
||||
>
|
||||
{{- end -}}
|
||||
</p>
|
||||
{{- end }}
|
||||
{{- with $schema.properties }}
|
||||
{{ template "api-ref-properties" (dict
|
||||
"api" $api
|
||||
"schema" $schema
|
||||
)
|
||||
}}
|
||||
{{- else }}
|
||||
{{- with $schema.items }}
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Items:
|
||||
<span class="font-mono"
|
||||
>{{ template "api-ref-type" (dict "api" $api "schema" .) }}</span
|
||||
>
|
||||
</p>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
</div>
|
||||
{{- end }}
|
||||
</section>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* ── Property table for a schema ──────────────────────────────────────── */}}
|
||||
{{ define "api-ref-properties" }}
|
||||
{{- $api := .api -}}
|
||||
{{- $schema := .schema -}}
|
||||
{{- $required := $schema.required | default slice -}}
|
||||
<table class="w-full border-collapse text-sm">
|
||||
<thead>
|
||||
<tr class="border-b border-gray-200 dark:border-gray-700">
|
||||
<th
|
||||
class="w-40 py-1.5 pr-4 text-left font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Property
|
||||
</th>
|
||||
<th
|
||||
class="w-32 py-1.5 pr-4 text-left font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Type
|
||||
</th>
|
||||
<th
|
||||
class="w-20 py-1.5 pr-4 text-left font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Required
|
||||
</th>
|
||||
<th
|
||||
class="py-1.5 text-left font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Description
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{- range $propName, $prop := $schema.properties }}
|
||||
<tr class="border-b border-gray-100 last:border-0 dark:border-gray-800">
|
||||
<td class="py-1.5 pr-4 align-top">
|
||||
<code class="rounded bg-gray-100 px-1 text-xs dark:bg-gray-800"
|
||||
>{{ $propName }}</code
|
||||
>
|
||||
</td>
|
||||
<td
|
||||
class="py-1.5 pr-4 align-top font-mono text-xs text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{ template "api-ref-type" (dict "api" $api "schema" $prop) }}
|
||||
</td>
|
||||
<td
|
||||
class="py-1.5 pr-4 align-top text-xs text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{ if in $required $propName }}yes{{ else }}no{{ end }}
|
||||
</td>
|
||||
<td class="py-1.5 align-top text-sm text-gray-600 dark:text-gray-400">
|
||||
{{- $resolved := partial "api-ref/resolve.html" (dict "api" $api "node" $prop) -}}
|
||||
{{- with $resolved.description -}}
|
||||
{{ . | markdownify }}
|
||||
{{- end -}}
|
||||
</td>
|
||||
</tr>
|
||||
{{- end }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,185 @@
|
||||
{{- /*
|
||||
Markdown rendering of an OpenAPI reference page.
|
||||
|
||||
Mirrors the HTML api-reference layout: unmarshals the colocated YAML
|
||||
Page Resource into a plain Hugo map and walks the spec to produce a
|
||||
flat markdown document. Used by the "View Markdown" / "Copy Markdown"
|
||||
actions and the .md alternate link.
|
||||
*/ -}}
|
||||
{{- $specRes := .Resources.GetMatch "*.yaml" -}}
|
||||
{{- $api := $specRes | transform.Unmarshal -}}
|
||||
{{- $methods := slice "get" "post" "put" "patch" "delete" -}}
|
||||
{{- $paths := $api.paths -}}
|
||||
# {{ .Title }}
|
||||
|
||||
{{ with .Description }}{{ . }}{{ end }}
|
||||
|
||||
{{ with $api.info }}
|
||||
{{- with .version }}- **Version**: `{{ . }}`
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{- range $api.servers }}- **Base URL**: `{{ .url }}`
|
||||
{{ end }}
|
||||
- **OpenAPI specification**: [`{{ path.Base $specRes.RelPermalink }}`]({{ $specRes.Permalink }})
|
||||
|
||||
{{ with $api.info }}{{ with .description }}{{ . }}{{ end }}{{ end }}
|
||||
|
||||
{{ with $api.components }}{{ with .securitySchemes }}
|
||||
## Authentication
|
||||
|
||||
{{ range $name, $scheme := . }}
|
||||
**`{{ $name }}`**{{ with $scheme.type }} — type: `{{ . }}`{{ end }}{{ with $scheme.scheme }}, scheme: `{{ . }}`{{ end }}{{ with $scheme.bearerFormat }}, bearer format: `{{ . }}`{{ end }}
|
||||
|
||||
{{ with $scheme.description }}{{ . }}{{ end }}
|
||||
{{ end -}}
|
||||
{{ end }}{{ end }}
|
||||
|
||||
{{- range $api.tags }}
|
||||
## {{ .name | title }}
|
||||
|
||||
{{ with .description }}{{ . }}{{ end }}
|
||||
|
||||
{{- $tagName := .name -}}
|
||||
{{- range $path, $item := $paths -}}
|
||||
{{- $sharedParams := slice -}}
|
||||
{{- with index $item "parameters" -}}
|
||||
{{- range . -}}
|
||||
{{- $p := partial "api-ref/resolve.html" (dict "api" $api "node" .) -}}
|
||||
{{- $sharedParams = $sharedParams | append $p -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- range $methods -}}
|
||||
{{- $method := . -}}
|
||||
{{- with index $item $method -}}
|
||||
{{- $op := . -}}
|
||||
{{- if in $op.tags $tagName }}
|
||||
### `{{ upper $method }}` `{{ $path }}`
|
||||
|
||||
{{ with $op.summary }}**{{ . }}**{{ end }}
|
||||
|
||||
{{ with $op.description }}{{ . }}{{ end }}
|
||||
|
||||
{{- $params := $sharedParams -}}
|
||||
{{- with index $op "parameters" -}}
|
||||
{{- range . -}}
|
||||
{{- $p := partial "api-ref/resolve.html" (dict "api" $api "node" .) -}}
|
||||
{{- $params = $params | append $p -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{ if $params }}
|
||||
**Parameters**
|
||||
|
||||
{{ range $params -}}
|
||||
- `{{ .name }}` ({{ .in }}{{ with .schema }}{{ with .type }}, {{ . }}{{ end }}{{ end }}{{ if .required }}, required{{ end }}){{ with .description }} — {{ . | strings.TrimSpace }}{{ end }}
|
||||
{{ end }}
|
||||
{{- end }}
|
||||
|
||||
{{- with $op.requestBody -}}
|
||||
{{- $body := partial "api-ref/resolve.html" (dict "api" $api "node" .) }}
|
||||
**Request body**{{ with $body.description }} — {{ . | strings.TrimSpace }}{{ end }}
|
||||
|
||||
{{- $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" " ") }}
|
||||
```
|
||||
{{ end }}{{ end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- with $op.responses }}
|
||||
**Responses**
|
||||
|
||||
{{ range $code, $resp := . -}}
|
||||
{{- $r := partial "api-ref/resolve.html" (dict "api" $api "node" $resp) }}
|
||||
`{{ $code }}` — {{ with $r.description }}{{ . | strings.TrimSpace }}{{ end }}
|
||||
{{- $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" " ") }}
|
||||
```
|
||||
{{ end }}{{ end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
|
||||
{{ end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- with $api.components }}{{ with .schemas }}
|
||||
## Schemas
|
||||
|
||||
{{ range $name, $schema := . }}
|
||||
### `{{ $name }}`
|
||||
|
||||
{{ with $schema.description }}{{ . | strings.TrimSpace }}{{ end }}
|
||||
|
||||
{{ with $schema.enum -}}
|
||||
Enum: {{ range $i, $v := . }}{{ if $i }}, {{ end }}`{{ $v }}`{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{- with $schema.properties }}
|
||||
{{- $required := $schema.required | default slice }}
|
||||
| Property | Type | Required | Description |
|
||||
| -------- | ---- | -------- | ----------- |
|
||||
{{ range $propName, $prop := . -}}
|
||||
| `{{ $propName }}` | {{ template "api-ref-md-type" $prop }} | {{ if in $required $propName }}yes{{ else }}no{{ end }} | {{ with $prop.description }}{{ . | strings.TrimSpace | strings.ReplaceRE "\\s*\\n\\s*" " " }}{{ end }} |
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
|
||||
{{ end -}}
|
||||
{{ end }}{{ end }}
|
||||
|
||||
{{- /* ── Helpers ───────────────────────────────────────────────────────── */ -}}
|
||||
|
||||
{{- define "api-ref-md-type" -}}
|
||||
{{- $s := . -}}
|
||||
{{- if reflect.IsMap $s -}}
|
||||
{{- with index $s "$ref" -}}
|
||||
{{- $parts := split . "/" -}}
|
||||
{{- $name := index $parts (sub (len $parts) 1) -}}
|
||||
`{{ $name }}`
|
||||
{{- else -}}
|
||||
{{- with $s.type -}}
|
||||
{{- if eq . "array" -}}
|
||||
`array<`{{ template "api-ref-md-type" $s.items }}`>`
|
||||
{{- else -}}
|
||||
`{{ . }}`
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
{{- if $s.allOf -}}`object`
|
||||
{{- else if or $s.anyOf $s.oneOf -}}`any`
|
||||
{{- else -}}—{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- else -}}—{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "api-ref-md-schema-link" -}}
|
||||
{{- $schema := .schema -}}
|
||||
{{- $indent := .indent | default "" -}}
|
||||
{{- if reflect.IsMap $schema -}}
|
||||
{{- with index $schema "$ref" -}}
|
||||
{{- $parts := split . "/" -}}
|
||||
{{- $name := index $parts (sub (len $parts) 1) -}}
|
||||
{{ $indent }}Schema: `{{ $name }}`
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
Reference in New Issue
Block a user