diff --git a/docs/scripting/db/cursor/array.mdx b/docs/scripting/db/cursor/array.mdx
new file mode 100644
index 00000000..762a3390
--- /dev/null
+++ b/docs/scripting/db/cursor/array.mdx
@@ -0,0 +1,31 @@
+---
+title: array(_and_close)
+alias: array_and_close
+---
+
+Retrieve all documents of the cursor, and close it.
+
+## Syntax
+
+```
+let cursor = #db.f(query, projection)
+cursor.array()
+```
+
+or
+```
+let cursor = #db.f(query, projection)
+cursor.array_and_close()
+```
+
+### Parameters
+
+No parameters.
+
+### Return
+
+Returns all documents from the cursor, as an array of objects.
+
+## Cursor State
+
+This function closes the cursor. If this is undesirable, try [array_and_keep_open](./array_and_keep_open).
diff --git a/docs/scripting/db/cursor/close.mdx b/docs/scripting/db/cursor/close.mdx
new file mode 100644
index 00000000..da421f39
--- /dev/null
+++ b/docs/scripting/db/cursor/close.mdx
@@ -0,0 +1,31 @@
+Close the cursor.
+
+## Syntax
+
+```
+let cursor = #db.f(query, projection)
+cursor.close()
+```
+
+### Parameters
+
+No parameters.
+
+### Return
+
+Always returns ((%Vnull%)).
+
+## Cursor State
+
+This function closes the cursor.
+
+## Example
+
+```js
+function(context, args) {
+ let cursor = #db.f({ type: "my_data" });
+
+ // removing the following line causes an error:
+ cursor.close();
+}
+```
diff --git a/docs/scripting/db/cursor/count.mdx b/docs/scripting/db/cursor/count.mdx
new file mode 100644
index 00000000..ebcede91
--- /dev/null
+++ b/docs/scripting/db/cursor/count.mdx
@@ -0,0 +1,33 @@
+---
+title: count(_and_close)
+alias: count_and_close
+---
+
+Retrieve the total number of documents of the cursor, and close it.
+
+This method ignores `skip` and `limit` settings.
+
+## Syntax
+
+```
+let cursor = #db.f(query, projection)
+cursor.count()
+```
+
+or
+```
+let cursor = #db.f(query, projection)
+cursor.count_and_close()
+```
+
+### Parameters
+
+No parameters.
+
+### Return
+
+The total number of documents of this cursor, as a number.
+
+## Cursor State
+
+This function closes the cursor. If this is undesirable, try [count_and_keep_open](./count_and_keep_open).
diff --git a/docs/scripting/db/cursor/distinct.mdx b/docs/scripting/db/cursor/distinct.mdx
new file mode 100644
index 00000000..ce90c8b6
--- /dev/null
+++ b/docs/scripting/db/cursor/distinct.mdx
@@ -0,0 +1,35 @@
+---
+title: distinct(_and_close)
+alias: distinct_and_close
+---
+
+Retrieve the unique values of a given field for all documents of the cursor, and close it.
+
+This method ignores `skip` and `limit` settings.
+
+## Syntax
+
+```
+let cursor = #db.f(query, projection)
+cursor.distinct(field)
+```
+
+or
+```
+let cursor = #db.f(query, projection)
+cursor.distinct_and_close(field)
+```
+
+### Parameters
+
+#### field
+
+A string naming the field to pull unique values from.
+
+### Return
+
+The unique values of the given field from all documents of the cursor, as an array.
+
+## Cursor State
+
+This function closes the cursor. If this is undesirable, try [distinct_and_keep_open](./distinct_and_keep_open).
diff --git a/docs/scripting/db/cursor/first.mdx b/docs/scripting/db/cursor/first.mdx
new file mode 100644
index 00000000..1cb73853
--- /dev/null
+++ b/docs/scripting/db/cursor/first.mdx
@@ -0,0 +1,56 @@
+---
+title: first(_and_close)
+alias: first_and_close
+---
+
+Retrieve the first document of the cursor, and close it.
+
+## Syntax
+
+```
+let cursor = #db.f(query, projection)
+cursor.first()
+```
+
+or
+```
+let cursor = #db.f(query, projection)
+cursor.first_and_close()
+```
+
+### Parameters
+
+No parameters.
+
+### Return
+
+Returns the first document from the cursor as an object.
+If the cursor is empty, returns ((%Vnull%)).
+
+## Cursor State
+
+This function closes the cursor. If this is undesirable, try [first_and_keep_open](./first_and_keep_open).
+
+## Example
+
+For the following examples, assume the database contains the following documents, inserted in that order:
+```
+{ type: "my_data", my_key: "foo", order: 2 }
+{ type: "my_data", my_key: "bar", order: 1 }
+```
+
+```js
+function(context, args) {
+ let data = #db.f({ type: "my_data" }).first();
+ return data; // { type: "my_data", my_key: "foo", order: 2 }
+}
+```
+
+```js
+function(context, args) {
+ let data = #db.f({ type: "my_data" }).sort({ order: 1 }).first();
+ return data; // { type: "my_data", my_key: "bar", order: 1 }
+}
+```
+
+
diff --git a/docs/scripting/db/cursor/first_and_keep_open.mdx b/docs/scripting/db/cursor/first_and_keep_open.mdx
new file mode 100644
index 00000000..c10fc520
--- /dev/null
+++ b/docs/scripting/db/cursor/first_and_keep_open.mdx
@@ -0,0 +1,42 @@
+
+Retrieve the first document of the cursor, keeping it open.
+
+## Syntax
+
+```
+let cursor = #db.f(query, projection)
+cursor.first_and_keep_open()
+// continue using `cursor`...
+```
+
+### Parameters
+
+No parameters.
+
+### Return
+
+Returns the first document from the cursor as an object.
+If the cursor is empty, returns ((%Vnull%)).
+
+## Cursor State
+
+This function keeps the cursor open.
+
+Try [first](./first) for a variant of this function that automatically closes the cursor.
+
+## Example
+
+For the following example, assume the database contains the following documents, inserted in that order:
+```
+{ type: "my_data", my_key: "foo", order: 2 }
+{ type: "my_data", my_key: "bar", order: 1 }
+```
+
+```js
+function(context, args) {
+ let cursor = #db.f({ type: "my_data" });
+ let a = cursor.skip(1).first_and_keep_open();
+ let b = cursor.skip(0).first_and_close();
+ return [ a.my_key, b.my_key ]; // [ "bar", "foo" ]
+}
+```
diff --git a/docs/scripting/db/cursor/index.mdx b/docs/scripting/db/cursor/index.mdx
new file mode 100644
index 00000000..3302aab1
--- /dev/null
+++ b/docs/scripting/db/cursor/index.mdx
@@ -0,0 +1,55 @@
+---
+title: Cursors
+sidebar_collapsible: true
+sidebar_collapsed: true
+---
+
+Database cursors are special objects returned from [#db.f(..)](../db.f).
+
+## Usage
+
+Cursors are exclusively returned from #db.f(..):
+
+```
+let cursor = #db.f(query, projection)
+```
+
+A cursor _MUST_ be closed before the script run finishes, otherwise the following error occurs:
+
+> `:::TRUST COMMUNICATION::: 1 db cursors were left open at end of script run`
+
+Cursors can be closed via the [close](./close) method, or by using a terminating method such as [`first`](./first).
+
+After a cursor is closed, it can no longer be used. Subsequent attempts to use the cursor result in the following error:
+
+> `:::TRUST COMMUNICATION::: cursor was invalid. this can happen if it was used after being closed`
+
+### Cursor Handles
+
+((%FTODO: Write something that people who have never used a file descriptor in their life can understand.%))
+
+
+Technical details
+
+A cursor is a wrapper object around a _"cursor handle"_.
+`#db.f` always returns a cursor with a new, unique "cursor handle".
+
+Multiple cursor objects can refer to the same "cursor handle".
+See [skip](./skip) for a method that causes this to happen.
+
+When _any_ cursor with a given "cursor handle" is closed, _all_ cursors for that "cursor handle" become invalid.
+(You only need to close one cursor for each "cursor handle" to avoid the 'cursors were left open' error.)
+
+Cursor objects that use the same "cursor handle" are nevertheless separate objects, and do not compare equal.
+
+Note that your code will never interact with a "cursor handle" directly, only with cursor objects.
+
+
+
+## Methods
+
+The following is an auto-generated list of cursor methods:
+
+import DocCardList from '@theme/DocCardList';
+
+
diff --git a/docs/scripting/db/cursor/limit.mdx b/docs/scripting/db/cursor/limit.mdx
new file mode 100644
index 00000000..d52c36fa
--- /dev/null
+++ b/docs/scripting/db/cursor/limit.mdx
@@ -0,0 +1,25 @@
+Set the limit of the cursor.
+
+## Syntax
+
+```
+let cursor = #db.f(query, projection)
+cursor.limit(count)
+```
+
+### Parameters
+
+#### count
+
+The maximum number of results to return.
+If this parameter is `0`, the limit is removed.
+
+### Return
+
+Returns a new cursor, with the same handle as the input cursor.
+
+This can be used to chain into other methods.
+
+## Cursor State
+
+This function keeps the cursor open.
diff --git a/docs/scripting/db/cursor/skip.mdx b/docs/scripting/db/cursor/skip.mdx
new file mode 100644
index 00000000..940cb716
--- /dev/null
+++ b/docs/scripting/db/cursor/skip.mdx
@@ -0,0 +1,25 @@
+Set the skip count of the cursor.
+
+## Syntax
+
+```
+let cursor = #db.f(query, projection)
+cursor.skip(skip)
+```
+
+### Parameters
+
+#### skip
+
+The number of results to skip.
+Note that this is not cumulative; `cursor.skip(5).skip(1)` will skip 1 document, not 6.
+
+### Return
+
+Returns a new cursor, with the same handle as the input cursor.
+
+This can be used to chain into other methods.
+
+## Cursor State
+
+This function keeps the cursor open.