diff --git a/content/docs/concept/how-cue-works-with-openapi/en.md b/content/docs/concept/how-cue-works-with-openapi/en.md
new file mode 100644
index 0000000000..7c97192ca5
--- /dev/null
+++ b/content/docs/concept/how-cue-works-with-openapi/en.md
@@ -0,0 +1,236 @@
+---
+title: How CUE works with OpenAPI
+tags:
+- encodings
+- cue command
+authors:
+- jpluscplusm
+- myitcv
+toc_hide: true
+---
+
+{{{with _script_ "en" "HIDDEN_ set up caches"}}}
+export GOMODCACHE=/caches/gomodcache
+export GOCACHE=/caches/gobuild
+{{{end}}}
+
+CUE works with the
+[OpenAPI 3.0.0 standard](https://github.com/OAI/OpenAPI-Specification/tree/3.0.0)
+for the description of REST APIs by supporting the use and import of OpenAPI
+`components.schemas` data schemas, and the export of CUE definitions into the
+same namespace.
+
+CUE is usually more clear and concise than the equivalent OpenAPI. However,
+given that they meet different needs for different types of users, CUE's
+ability to round-trip between CUE and OpenAPI's data schema subset acts as a
+useful bridge between their two worlds.
+
+## Reading and writing OpenAPI with the `cue` command
+
+The `cue` command can convert CUE schemas into OpenAPI's `components.schemas`.
+CUE files can be converted into OpenAPI so long as they only specify
+definitions and metadata (`info`, `$version`, etc) at their top-level.
+
+Let's start with a trivial CUE schema that we want to convert to OpenAPI:
+
+{{{with upload "en" "schema.cue"}}}
+-- schema.cue --
+// A schema for the pet API.
+package api
+
+$version: "v1.2.3"
+// A Pet is a pet that we handle.
+#Pet: {
+ // A pet has a name.
+ name!: string
+ // We only handle certain kinds of pets.
+ kind!: #Kind
+ // Centenarian pets are not handled.
+ age?: uint & <100 // TODO: increase limit if the tortoise proposal is accepted.
+ ...
+}
+
+// Kind encodes the different pets we handle.
+#Kind: "cat" | "dog" | "goldfish"
+{{{end}}}
+
+The [`cue def`]({{< relref "docs/reference/cli/cue-def" >}}) command normalizes
+the schema, and optionally converts it into another format:
+
+{{{with script "en" "cue def schema.cue"}}}
+cue def schema.cue -o api.pet.yaml --out openapi+yaml
+{{{end}}}
+
+{{{with _script_ "en" "HIDDEN_ move api.pet.yaml sideways"}}}
+mv api.pet.yaml .api.pet.yaml
+{{{end}}}
+
+The OpenAPI `info.title` field can be extracted from the top-level CUE comment,
+or can be specified directly.
+The same goes for OpenAPI's `info.version` field, which is extracted from CUE's
+top-level `$version` field if not specified directly.
+
+Be aware of just how *long* an equivalent OpenAPI definition can become - not
+all formats possess CUE's succinctness and economy of expression!
+The `cue def` command creates this file:
+
+{{{with upload "en" "api.pet.yaml"}}}
+-- api.pet.yaml --
+openapi: 3.0.0
+info:
+ title: A schema for the pet API.
+ version: v1.2.3
+paths: {}
+components:
+ schemas:
+ Kind:
+ description: Kind encodes the different pets we handle.
+ type: string
+ enum:
+ - cat
+ - dog
+ - goldfish
+ Pet:
+ description: A Pet is a pet that we handle.
+ type: object
+ required:
+ - name
+ - kind
+ properties:
+ name:
+ description: A pet has a name.
+ type: string
+ kind:
+ $ref: '#/components/schemas/Kind'
+ age:
+ description: Centenarian pets are not handled.
+ type: integer
+ minimum: 0
+ maximum: 100
+ exclusiveMaximum: true
+{{{end}}}
+
+{{{with _script_ "en" "HIDDEN_ diff api.pet.yaml"}}}
+diff -wu api.pet.yaml .api.pet.yaml
+rm .api.pet.yaml # tidy up
+{{{end}}}
+
+Because CUE is more expressive than OpenAPI, it isn't possible to generate a
+precise OpenAPI equivalent for *every* CUE schema. CUE does the best conversion
+it can, limited by what OpenAPI's data schemas can represent.
+
+[`cue import`]({{< relref "docs/reference/cli/cue-import" >}}) can perform the
+reverse operation, taking the OpenAPI definition emitted above and converting
+it back to CUE:
+
+{{{with script "en" "import openapi yaml"}}}
+cue import -p api api.pet.yaml
+{{{end}}}
+
+{{{with _script_ "en" "HIDDEN_ move api.pet.cue sideways"}}}
+mv api.pet.cue .api.pet.cue
+{{{end}}}
+
+This produces the following CUE, which is as close to the original `schema.cue`
+as OpenAPI's capabilities currently permit:
+
+{{{with upload "en" "api.pet.cue"}}}
+-- api.pet.cue --
+// A schema for the pet API.
+package api
+
+info: {
+ title: *"A schema for the pet API." | string
+ version: *"v1.2.3" | string
+}
+// Kind encodes the different pets we handle.
+#Kind: "cat" | "dog" | "goldfish"
+
+// A Pet is a pet that we handle.
+#Pet: {
+ // A pet has a name.
+ name: string
+ kind: #Kind
+
+ // Centenarian pets are not handled.
+ age?: int & >=0 & <100
+ ...
+}
+{{{end}}}
+
+{{{with _script_ "en" "HIDDEN_ diff api.pet.cue"}}}
+diff -wu api.pet.cue .api.pet.cue
+rm .api.pet.cue # tidy up
+{{{end}}}
+
+## Using CUE's Go API
+
+CUE can also generate OpenAPI through its Go API.
+
+Generating an OpenAPI definition can be as simple as this:
+
+{{{with _script_ "en" "go mod init"}}}
+go mod init mod.example
+{{{end}}}
+
+{{{with upload "en" "go emit openapi main"}}}
+-- main.go --
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "log"
+
+ "cuelang.org/go/cue/cuecontext"
+ "cuelang.org/go/cue/load"
+ "cuelang.org/go/encoding/openapi"
+)
+
+func main() {
+ ctx := cuecontext.New()
+ bis := load.Instances([]string{"schema.cue"}, nil)
+ v := ctx.BuildInstance(bis[0])
+
+ // Generate the OpenAPI schema from the value loaded from schema.cue
+ b, err := openapi.Gen(v, nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Render as indented JSON
+ var out bytes.Buffer
+ if err = json.Indent(&out, b, "", " "); err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("%s\n", out.Bytes())
+}
+{{{end}}}
+
+{{{with _script_ "en" "go mod tidy"}}}
+#ellipsis 0
+go get cuelang.org/go@${CUELANG_CUE_LATEST}
+#ellipsis 0
+go mod tidy
+{{{end}}}
+
+{{{with script "en" "go run"}}}
+#ellipsis 10
+go run .
+{{{end}}}
+
+The [`encoding/openapi`](https://pkg.go.dev/cuelang.org/go/encoding/openapi)
+package provides options to make a definition self-contained, to filter
+constraints, and so on. The *expanding references* option enables the
+"Structural OpenAPI" form required by CRDs targeting Kubernetes version 1.15
+and later.
+
+## Related content
+
+- CUE supports OpenAPI's `components.schemas` namespace, and general `info` metadata --
+ {{}} tracks the support of other namespaces defined by the OpenAPI standard
+- The [OpenAPI 3.0.0 specification](https://github.com/OAI/OpenAPI-Specification/tree/3.0.0)
+- {{< linkto/related/reference "cli/cue-def" >}}
+- {{< linkto/related/reference "cli/cue-import" >}}
+- The [`encoding/openapi`](https://pkg.go.dev/cuelang.org/go/encoding/openapi) package
diff --git a/content/docs/concept/how-cue-works-with-openapi/gen_cache.cue b/content/docs/concept/how-cue-works-with-openapi/gen_cache.cue
new file mode 100644
index 0000000000..5dc5fb834f
--- /dev/null
+++ b/content/docs/concept/how-cue-works-with-openapi/gen_cache.cue
@@ -0,0 +1,118 @@
+package site
+{
+ content: {
+ docs: {
+ concept: {
+ "how-cue-works-with-openapi": {
+ page: {
+ cache: {
+ upload: {
+ "schema.cue": "IhWYgY2d4XJ14dVZmRAuFEOvvNEGp0VQ4d4KmUiGPOI="
+ "api.pet.yaml": "a59sa6kiZ8MIhIuFnjuABOwBKtJb6/0GvCczVlyVRrc="
+ "api.pet.cue": "pfphGZPbu+Bc4/4G9UHYevOhe+M796iWteICO8ORgbg="
+ "go emit openapi main": "jhu5SQXRFuKEuJidmqemyFh57/DWy/VIXCmfWazF7IQ="
+ }
+ multi_step: {
+ hash: "J0UNMMV9HUS9M1DM0IFBABVSFGGVSUB7HGB29SLKL1ACHV001V1G===="
+ scriptHash: "MM7VG55V7882CDD5KQ5RA7LT8H0DUEA4KEINTQBIDTMNFM6E3IG0===="
+ steps: [{
+ doc: ""
+ cmd: "export GOMODCACHE=/caches/gomodcache"
+ exitCode: 0
+ output: ""
+ }, {
+ doc: ""
+ cmd: "export GOCACHE=/caches/gobuild"
+ exitCode: 0
+ output: ""
+ }, {
+ doc: ""
+ cmd: "cue def schema.cue -o api.pet.yaml --out openapi+yaml"
+ exitCode: 0
+ output: ""
+ }, {
+ doc: ""
+ cmd: "mv api.pet.yaml .api.pet.yaml"
+ exitCode: 0
+ output: ""
+ }, {
+ doc: ""
+ cmd: "diff -wu api.pet.yaml .api.pet.yaml"
+ exitCode: 0
+ output: ""
+ }, {
+ doc: ""
+ cmd: "rm .api.pet.yaml # tidy up"
+ exitCode: 0
+ output: ""
+ }, {
+ doc: ""
+ cmd: "cue import -p api api.pet.yaml"
+ exitCode: 0
+ output: ""
+ }, {
+ doc: ""
+ cmd: "mv api.pet.cue .api.pet.cue"
+ exitCode: 0
+ output: ""
+ }, {
+ doc: ""
+ cmd: "diff -wu api.pet.cue .api.pet.cue"
+ exitCode: 0
+ output: ""
+ }, {
+ doc: ""
+ cmd: "rm .api.pet.cue # tidy up"
+ exitCode: 0
+ output: ""
+ }, {
+ doc: ""
+ cmd: "go mod init mod.example"
+ exitCode: 0
+ output: """
+ go: creating new go.mod: module mod.example
+
+ """
+ }, {
+ doc: "#ellipsis 0"
+ cmd: "go get cuelang.org/go@v0.8.2"
+ exitCode: 0
+ output: """
+ ...
+
+ """
+ }, {
+ doc: "#ellipsis 0"
+ cmd: "go mod tidy"
+ exitCode: 0
+ output: """
+ ...
+
+ """
+ }, {
+ doc: "#ellipsis 10"
+ cmd: "go run ."
+ exitCode: 0
+ output: """
+ {
+ "openapi": "3.0.0",
+ "info": {
+ "title": "A schema for the pet API.",
+ "version": "v1.2.3"
+ },
+ "paths": {},
+ "components": {
+ "schemas": {
+ "Kind": {
+ ...
+
+ """
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/content/docs/concept/how-cue-works-with-openapi/page.cue b/content/docs/concept/how-cue-works-with-openapi/page.cue
new file mode 100644
index 0000000000..2e5c4a7965
--- /dev/null
+++ b/content/docs/concept/how-cue-works-with-openapi/page.cue
@@ -0,0 +1,3 @@
+package site
+
+content: docs: concept: "how-cue-works-with-openapi": page: _
diff --git a/hugo/content/en/docs/concept/how-cue-works-with-openapi/index.md b/hugo/content/en/docs/concept/how-cue-works-with-openapi/index.md
new file mode 100644
index 0000000000..6cb15f9f64
--- /dev/null
+++ b/hugo/content/en/docs/concept/how-cue-works-with-openapi/index.md
@@ -0,0 +1,202 @@
+---
+title: How CUE works with OpenAPI
+tags:
+- encodings
+- cue command
+authors:
+- jpluscplusm
+- myitcv
+toc_hide: true
+---
+
+CUE works with the
+[OpenAPI 3.0.0 standard](https://github.com/OAI/OpenAPI-Specification/tree/3.0.0)
+for the description of REST APIs by supporting the use and import of OpenAPI
+`components.schemas` data schemas, and the export of CUE definitions into the
+same namespace.
+
+CUE is usually more clear and concise than the equivalent OpenAPI. However,
+given that they meet different needs for different types of users, CUE's
+ability to round-trip between CUE and OpenAPI's data schema subset acts as a
+useful bridge between their two worlds.
+
+## Reading and writing OpenAPI with the `cue` command
+
+The `cue` command can convert CUE schemas into OpenAPI's `components.schemas`.
+CUE files can be converted into OpenAPI so long as they only specify
+definitions and metadata (`info`, `$version`, etc) at their top-level.
+
+Let's start with a trivial CUE schema that we want to convert to OpenAPI:
+
+```cue { title="schema.cue" }
+// A schema for the pet API.
+package api
+
+$version: "v1.2.3"
+// A Pet is a pet that we handle.
+#Pet: {
+ // A pet has a name.
+ name!: string
+ // We only handle certain kinds of pets.
+ kind!: #Kind
+ // Centenarian pets are not handled.
+ age?: uint & <100 // TODO: increase limit if the tortoise proposal is accepted.
+ ...
+}
+
+// Kind encodes the different pets we handle.
+#Kind: "cat" | "dog" | "goldfish"
+```
+
+The [`cue def`]({{< relref "docs/reference/cli/cue-def" >}}) command normalizes
+the schema, and optionally converts it into another format:
+
+```text { title="TERMINAL" codeToCopy="Y3VlIGRlZiBzY2hlbWEuY3VlIC1vIGFwaS5wZXQueWFtbCAtLW91dCBvcGVuYXBpK3lhbWw=" }
+$ cue def schema.cue -o api.pet.yaml --out openapi+yaml
+```
+The OpenAPI `info.title` field can be extracted from the top-level CUE comment,
+or can be specified directly.
+The same goes for OpenAPI's `info.version` field, which is extracted from CUE's
+top-level `$version` field if not specified directly.
+
+Be aware of just how *long* an equivalent OpenAPI definition can become - not
+all formats possess CUE's succinctness and economy of expression!
+The `cue def` command creates this file:
+
+```yaml { title="api.pet.yaml" }
+openapi: 3.0.0
+info:
+ title: A schema for the pet API.
+ version: v1.2.3
+paths: {}
+components:
+ schemas:
+ Kind:
+ description: Kind encodes the different pets we handle.
+ type: string
+ enum:
+ - cat
+ - dog
+ - goldfish
+ Pet:
+ description: A Pet is a pet that we handle.
+ type: object
+ required:
+ - name
+ - kind
+ properties:
+ name:
+ description: A pet has a name.
+ type: string
+ kind:
+ $ref: '#/components/schemas/Kind'
+ age:
+ description: Centenarian pets are not handled.
+ type: integer
+ minimum: 0
+ maximum: 100
+ exclusiveMaximum: true
+```
+Because CUE is more expressive than OpenAPI, it isn't possible to generate a
+precise OpenAPI equivalent for *every* CUE schema. CUE does the best conversion
+it can, limited by what OpenAPI's data schemas can represent.
+
+[`cue import`]({{< relref "docs/reference/cli/cue-import" >}}) can perform the
+reverse operation, taking the OpenAPI definition emitted above and converting
+it back to CUE:
+
+```text { title="TERMINAL" codeToCopy="Y3VlIGltcG9ydCAtcCBhcGkgYXBpLnBldC55YW1s" }
+$ cue import -p api api.pet.yaml
+```
+This produces the following CUE, which is as close to the original `schema.cue`
+as OpenAPI's capabilities currently permit:
+
+```cue { title="api.pet.cue" }
+// A schema for the pet API.
+package api
+
+info: {
+ title: *"A schema for the pet API." | string
+ version: *"v1.2.3" | string
+}
+// Kind encodes the different pets we handle.
+#Kind: "cat" | "dog" | "goldfish"
+
+// A Pet is a pet that we handle.
+#Pet: {
+ // A pet has a name.
+ name: string
+ kind: #Kind
+
+ // Centenarian pets are not handled.
+ age?: int & >=0 & <100
+ ...
+}
+```
+## Using CUE's Go API
+
+CUE can also generate OpenAPI through its Go API.
+
+Generating an OpenAPI definition can be as simple as this:
+```go { title="main.go" }
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "log"
+
+ "cuelang.org/go/cue/cuecontext"
+ "cuelang.org/go/cue/load"
+ "cuelang.org/go/encoding/openapi"
+)
+
+func main() {
+ ctx := cuecontext.New()
+ bis := load.Instances([]string{"schema.cue"}, nil)
+ v := ctx.BuildInstance(bis[0])
+
+ // Generate the OpenAPI schema from the value loaded from schema.cue
+ b, err := openapi.Gen(v, nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Render as indented JSON
+ var out bytes.Buffer
+ if err = json.Indent(&out, b, "", " "); err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("%s\n", out.Bytes())
+}
+```
+```text { title="TERMINAL" codeToCopy="Z28gcnVuIC4=" }
+$ go run .
+{
+ "openapi": "3.0.0",
+ "info": {
+ "title": "A schema for the pet API.",
+ "version": "v1.2.3"
+ },
+ "paths": {},
+ "components": {
+ "schemas": {
+ "Kind": {
+...
+```
+
+The [`encoding/openapi`](https://pkg.go.dev/cuelang.org/go/encoding/openapi)
+package provides options to make a definition self-contained, to filter
+constraints, and so on. The *expanding references* option enables the
+"Structural OpenAPI" form required by CRDs targeting Kubernetes version 1.15
+and later.
+
+## Related content
+
+- CUE supports OpenAPI's `components.schemas` namespace, and general `info` metadata --
+ {{}} tracks the support of other namespaces defined by the OpenAPI standard
+- The [OpenAPI 3.0.0 specification](https://github.com/OAI/OpenAPI-Specification/tree/3.0.0)
+- {{< linkto/related/reference "cli/cue-def" >}}
+- {{< linkto/related/reference "cli/cue-import" >}}
+- The [`encoding/openapi`](https://pkg.go.dev/cuelang.org/go/encoding/openapi) package