datanotes 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SECURITY.md +5 -3
- package/dist/api/extensions.cjs +1345 -21
- package/dist/api/extensions.js +1345 -21
- package/dist/api/index.cjs +1345 -21
- package/dist/api/index.js +1345 -21
- package/dist/api/mcp.cjs +1345 -21
- package/dist/api/mcp.js +1345 -21
- package/dist/datanotes.cjs +2927 -609
- package/dist/docs/administration/extensions.md +2 -2
- package/dist/docs/api/operations/add_column.md +1 -0
- package/dist/docs/api/operations/alter_table.md +4 -3
- package/dist/docs/api/operations/check_table.md +1 -0
- package/dist/docs/api/operations/create_table.md +1 -0
- package/dist/docs/api/operations/describe_database.md +2 -0
- package/dist/docs/api/operations/describe_history.md +1 -0
- package/dist/docs/api/operations/describe_semantic_index.md +1 -0
- package/dist/docs/api/operations/describe_table.md +1 -0
- package/dist/docs/api/operations/graph_neighbors.md +1 -0
- package/dist/docs/api/operations/graph_path.md +1 -0
- package/dist/docs/api/operations/graph_traverse.md +1 -0
- package/dist/docs/api/operations/infer_table.md +1 -0
- package/dist/docs/api/operations/list_changes.md +1 -0
- package/dist/docs/api/operations/list_folder.md +1 -0
- package/dist/docs/api/operations/list_link_targets.md +1 -0
- package/dist/docs/api/operations/list_migrations.md +1 -0
- package/dist/docs/api/operations/list_tables.md +2 -0
- package/dist/docs/api/operations/list_webhooks.md +1 -0
- package/dist/docs/api/operations/read_note.md +1 -0
- package/dist/docs/api/operations/search.md +39 -0
- package/dist/docs/api/operations/search_notes.md +1 -0
- package/dist/docs/api/operations/select_rows.md +1 -0
- package/dist/docs/api/operations/semantic_search.md +1 -0
- package/dist/docs/api/operations/table_changes.md +1 -0
- package/dist/docs/api/operations/tags/queries.md +1 -0
- package/dist/docs/api/operations/tags/tokens.md +1 -1
- package/dist/docs/api/operations/tokens_create.md +1 -0
- package/dist/docs/api/operations/tokens_update.md +2 -1
- package/dist/docs/api.md +5 -4
- package/dist/docs/develop/build-an-app.md +2 -1
- package/dist/docs/develop/change-feed-and-webhooks.md +1 -1
- package/dist/docs/develop/http-api.md +1 -1
- package/dist/docs/guides/data-quality/check-constraints.md +10 -10
- package/dist/docs/guides/data-quality/hand-edits.md +8 -8
- package/dist/docs/guides/database-objects/bases.md +11 -11
- package/dist/docs/guides/database-objects/catalogs-and-schemas.md +30 -10
- package/dist/docs/guides/database-objects/columns.md +243 -40
- package/dist/docs/guides/database-objects/relations.md +28 -28
- package/dist/docs/guides/database-objects/tables.md +167 -43
- package/dist/docs/guides/database-objects/views.md +44 -44
- package/dist/docs/guides/history/change-data-feed.md +34 -34
- package/dist/docs/guides/history/journal-and-undo.md +12 -12
- package/dist/docs/guides/history/restore.md +20 -20
- package/dist/docs/guides/history/table-history.md +31 -31
- package/dist/docs/guides/read-and-write/automatic-links.md +15 -15
- package/dist/docs/guides/read-and-write/import-notes.md +20 -20
- package/dist/docs/guides/read-and-write/joins-and-aggregates.md +7 -7
- package/dist/docs/guides/read-and-write/read-and-filter.md +10 -10
- package/dist/docs/guides/read-and-write/write-rows.md +10 -10
- package/dist/docs/guides/search/hybrid.md +162 -0
- package/dist/docs/guides/search/text.md +1 -1
- package/dist/docs/guides/security/security-model.md +5 -3
- package/dist/docs/guides/security/tokens.md +121 -8
- package/dist/docs/reference/cli.md +1 -1
- package/dist/docs/reference/glossary.md +1 -1
- package/dist/docs/reference/operations/auxiliary.md +3 -3
- package/dist/docs/reference/operations/queries.md +16 -0
- package/dist/docs/reference/operations/rows.md +1 -1
- package/dist/docs/reference/operations/schema.md +5 -5
- package/dist/docs/reference/operations.md +6 -5
- package/dist/docs/release-notes.md +59 -0
- package/dist/docs/start/connect-agents.md +10 -3
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Give each program its own token
|
|
2
2
|
|
|
3
|
-
> Create named tokens with their own permissions, so the engine limits what each program or agent can
|
|
3
|
+
> Create named tokens with their own permissions, so the engine limits what each program or agent can read and write, and the journal says who wrote what.
|
|
4
4
|
|
|
5
|
-
A named token lets a program, a device or an agent call the engine with only the permissions it needs. The owner token can do everything
|
|
5
|
+
A named token lets a program, a device or an agent call the engine with only the permissions it needs. The owner token can do everything. A named token reads what you allow and writes only what you allow. Without `read`, it reads every table. The journal records the token’s name with every write it makes, so you can tell who changed what.
|
|
6
6
|
|
|
7
7
|
To see a token at work in a complete program, follow [Build an app on Datanotes](https://datanotes.dev/develop/build-an-app/). For the ways to build on the engine, read [Build on Datanotes](https://datanotes.dev/develop/build-on-datanotes/). [Who did what](https://datanotes.dev/guides/security/who-did-what/) shows how the journal names each token.
|
|
8
8
|
|
|
@@ -17,7 +17,7 @@ curl -X POST http://127.0.0.1:27150/datanotes/tokens/create/ \
|
|
|
17
17
|
-d '{"name": "film-diary", "write": ["main.public.*"]}'
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
This request creates a token named `film-diary
|
|
20
|
+
This request creates a token named `film-diary`. It may write rows of every table in the `main.public` schema, such as `film` and `actor`. It passes no `read`, so the token reads every table. The answer holds the token in `token` and the registry’s entry in `entry`. The name is kebab-case, and `owner` is reserved.
|
|
21
21
|
|
|
22
22
|
## Manage your tokens
|
|
23
23
|
|
|
@@ -32,13 +32,124 @@ The owner manages tokens with these routes under `/datanotes/`:
|
|
|
32
32
|
|
|
33
33
|
## Choose what a token can do
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Each permission of a named token comes from its entry:
|
|
36
36
|
|
|
37
|
-
* **Row
|
|
38
|
-
* **
|
|
39
|
-
* **
|
|
37
|
+
* **Row reads**: only of tables that match a pattern in `read`. Without `read`, the token reads every table.
|
|
38
|
+
* **Row writes**: only on tables that match a pattern in `write`.
|
|
39
|
+
* **Schema changes**: only with `"ddl": true`.
|
|
40
|
+
* **Maintenance**: only with `"maintain": true`, meant for an agent’s own token. It covers configuration, undo, restore, vacuum and rebuilding the semantic index.
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
A pattern of `read` or `write` takes one of these forms:
|
|
43
|
+
|
|
44
|
+
* **`catalog.schema.table`**: one table, such as `personal.food.pantry`
|
|
45
|
+
* **`catalog.schema.*`**: every table of a schema
|
|
46
|
+
* **`catalog.*`**: every table of a catalog
|
|
47
|
+
* **`*`**: every table
|
|
48
|
+
|
|
49
|
+
Some routes stay the owner’s, whatever a named token’s permissions. A named token can’t create or delete tokens and webhooks, list tokens, rotate them or read the engine’s status. A token may list webhooks and read its own entry with `tokens/me/`. With `tokens/update/`, it may change its own `description` and `autolink`, never its permissions.
|
|
50
|
+
|
|
51
|
+
## Limit what a token can read
|
|
52
|
+
|
|
53
|
+
By default, a named token reads every table. Give it `read` patterns, and it reads only the tables they match. Use them for an agent that works on one part of your notes, so it never sees the rest.
|
|
54
|
+
|
|
55
|
+
This request creates a `cook` token for a cooking agent:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
curl -X POST http://127.0.0.1:27150/datanotes/tokens/create/ \
|
|
59
|
+
-H "Authorization: Bearer your_owner_token" \
|
|
60
|
+
-H "Content-Type: application/json" \
|
|
61
|
+
-d '{
|
|
62
|
+
"name": "cook",
|
|
63
|
+
"read": ["personal.food.*"],
|
|
64
|
+
"write": ["personal.food.pantry"]
|
|
65
|
+
}'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The `cook` token has these permissions:
|
|
69
|
+
|
|
70
|
+
* **It reads** `personal.food.pantry`, `personal.food.meals` and every other table of `personal.food`.
|
|
71
|
+
* **It writes** only `personal.food.pantry`.
|
|
72
|
+
* **It never sees** `personal.health.*`: its rows, notes in search, nodes in the graph, changes in the feed, or names in `list_tables`.
|
|
73
|
+
|
|
74
|
+
A new table in `personal.food` becomes readable at once, because the pattern matches it.
|
|
75
|
+
|
|
76
|
+
`tokens/` and `tokens/me/` show `read` next to `write`. A token without `read` shows `["*"]`. `datanotes status` prints the limits in its text, such as `cook (read personal.food.*, write personal.food.pantry)`. A token with `"read": []` shows `read nothing`. The status in JavaScript Object Notation (JSON) carries `read` only when it is limited.
|
|
77
|
+
|
|
78
|
+
### The rules of `read`
|
|
79
|
+
|
|
80
|
+
The engine follows these rules:
|
|
81
|
+
|
|
82
|
+
* **No `read` means every table.** A token made before read patterns existed keeps reading everything. `["*"]` means the same.
|
|
83
|
+
* **A token reads what it writes.** Every `write` pattern must fall inside a `read` pattern. A write answers with the rows it touches and takes `where` filters, so a write-only token would still read. `tokens/create/` and `tokens/update/` refuse such a token with 400, and the hint names the missing pattern.
|
|
84
|
+
* **`ddl` and `maintain` need every table.** A token with either one can’t have a limited `read`. Cloning a table, adding a constraint, undo and restore read rows of any table. The engine refuses the combination with 400.
|
|
85
|
+
* **A view needs its tables too.** To read a view, a token must read the view’s name and every table the view reads. That includes nested views, joins, unions and the links its filters follow. `list_tables` lists a view only when the engine can tell its tables from the view’s query alone. It hides a view over a union, even when every table of the union is readable.
|
|
86
|
+
* **Free notes stay hidden.** Only a token that reads every table reads a note outside tables, or any other file of the vault. A token with a limited `read` never reads free notes.
|
|
87
|
+
* **A link stays visible as text.** A readable row may link to a row the token can’t read. The link text, such as `[[Pasta]]`, stays in the answer. `expand` doesn’t expand it, so the linked note’s properties stay hidden. `file_backlink_count` stays a plain count.
|
|
88
|
+
|
|
89
|
+
### What the engine filters and what it refuses
|
|
90
|
+
|
|
91
|
+
The engine refuses a name you write yourself and filters a set you did not list.
|
|
92
|
+
|
|
93
|
+
A name you write is refused with 403 when the token can’t read it:
|
|
94
|
+
|
|
95
|
+
* The `table` of a query, a join or a union branch
|
|
96
|
+
* A link a `where` filter follows, such as `for_weight.kg`, and the target of `within`
|
|
97
|
+
* `this_note`, and the `path` of `read_note`
|
|
98
|
+
* An exact table name in `tables`, as `search`, `semantic_search` and the graph operations take it
|
|
99
|
+
* The `table` of `describe_table`, `check_table`, `infer_table`, `list_link_targets`, `table_changes` and `describe_history`
|
|
100
|
+
|
|
101
|
+
The refusal names the token and the table, such as `token cook may not read personal.health.weight`. It also carries a hint and a link to this section.
|
|
102
|
+
|
|
103
|
+
A set you did not list is filtered to what the token may read, with no error:
|
|
104
|
+
|
|
105
|
+
* **Listings**: `list_tables`, `describe_database`, `list_folder` and `system.information_schema`
|
|
106
|
+
* **Bases**: `base:<name>` keeps only the readable tables that extend it
|
|
107
|
+
* **Wildcards**: a pattern in `tables`, or every table when you leave `table` out
|
|
108
|
+
* **Search and the graph**: `search`, `search_notes`, `semantic_search`, and the neighbours and paths of the graph
|
|
109
|
+
* **History and changes**: `list_changes`, `list_migrations`, `describe_history` and the change feed’s stream
|
|
110
|
+
|
|
111
|
+
The engine filters before it applies `limit`, so a search still returns up to `limit` results. A token sees its own tool calls on the change feed, never another token’s. `list_webhooks` shows only the webhooks whose tables the token may all read.
|
|
112
|
+
|
|
113
|
+
A few more rules apply to a token with a limited `read`:
|
|
114
|
+
|
|
115
|
+
* **Schema changes stay hidden.** The change feed never shows it a change to `metastore.yaml`, whatever the table.
|
|
116
|
+
* **A short name means the readable table.** In the graph’s `tables`, `through` and `from_nodes.tables`, a short name may match several tables. The engine then picks the one the token may read.
|
|
117
|
+
* **A refused write keeps its short message.** A link the engine refuses lists no rows of a table the token can’t read. The tool-call event of that refusal on the change feed holds the same short message.
|
|
118
|
+
|
|
119
|
+
A write by a limited token still checks every table: links, uniqueness and CHECKs. Only its answer changes. It leaves out the rows of tables the token can’t read, even when the write touched them.
|
|
120
|
+
|
|
121
|
+
### Names are hidden, not secret
|
|
122
|
+
|
|
123
|
+
Listings hide the tables a token can’t read. A refusal, though, repeats the name the caller gave. So a caller who guesses `personal.health.weight` learns that the table exists. It never learns the table’s rows. The contents are protected; the names are only hidden from listings. Don’t put a secret in a table’s name.
|
|
124
|
+
|
|
125
|
+
### Change what a token reads
|
|
126
|
+
|
|
127
|
+
The owner changes `read` with `tokens/update/`. This request lets `cook` read the health tables too:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
curl -X POST http://127.0.0.1:27150/datanotes/tokens/update/ \
|
|
131
|
+
-H "Authorization: Bearer your_owner_token" \
|
|
132
|
+
-H "Content-Type: application/json" \
|
|
133
|
+
-d '{
|
|
134
|
+
"name": "cook",
|
|
135
|
+
"read": ["personal.food.*", "personal.health.*"]
|
|
136
|
+
}'
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
To let a token read every table again, pass `"read": null`. A token can’t change its own `read`: the engine answers 403.
|
|
140
|
+
|
|
141
|
+
The token of an [extension](https://datanotes.dev/administration/extensions/), `ext-<name>`, gets no `read` from the installer, so it reads every table. You may limit it with `tokens/update/`, as long as `read` covers the tables it writes. A tool that then reads another table gets a 403. An install or update may write a table outside `read`. The engine then refuses the token’s new `write` with 400. Widen `read` first, then run the command again.
|
|
142
|
+
|
|
143
|
+
### Known gaps
|
|
144
|
+
|
|
145
|
+
Read patterns limit tables, not columns or rows. These gaps remain:
|
|
146
|
+
|
|
147
|
+
* **No write-only tokens.** A token always reads what it writes.
|
|
148
|
+
* **No column or row limits.** A token reads every column and every row of a readable table.
|
|
149
|
+
* **Writes into other tables.** A write can change rows of other tables: `on_referenced: "set_null"`, inverse links and `align_relations`. The engine doesn’t check those changes against `write` yet. The answer still hides the rows the token can’t read.
|
|
150
|
+
* **Counts.** `describe_semantic_index` and `file_backlink_count` count every note, readable or not. They may tell how many notes exist.
|
|
151
|
+
* **Tool calls.** Any token may record a tool call with `tool-calls/record/`, in the name of any agent.
|
|
152
|
+
* **Guessed names.** As [Names are hidden, not secret](#names-are-hidden-not-secret) explains, a refusal confirms that a table exists.
|
|
42
153
|
|
|
43
154
|
## Turn automatic links off for a sync program
|
|
44
155
|
|
|
@@ -58,3 +169,5 @@ A token’s `autolink` is the default of its writes:
|
|
|
58
169
|
* **Without it**, the token’s writes follow the database’s `autolink` setting.
|
|
59
170
|
|
|
60
171
|
Turn it off for the tokens of sync programs and of modules that write text for you. Keep it unset for an agent’s token. To change it later, call `tokens/update/` with `autolink`. Pass `null` to follow the database setting again. `tokens/` and `tokens/me/` list it with the other fields of the entry.
|
|
172
|
+
|
|
173
|
+
With a limited `read`, automatic links point only to notes the token may read.
|
|
@@ -142,7 +142,7 @@ A set of rows with the same columns, defined in the metastore. See [Tables](http
|
|
|
142
142
|
|
|
143
143
|
## Token
|
|
144
144
|
|
|
145
|
-
A credential for the HTTP API, sent as `Authorization: Bearer`. The owner token can do everything
|
|
145
|
+
A credential for the HTTP API, sent as `Authorization: Bearer`. The owner token can do everything. A named token reads and writes only the tables you allow, and has only the permissions you give it. See [Tokens and permissions](https://datanotes.dev/guides/security/tokens/).
|
|
146
146
|
|
|
147
147
|
## Transaction log
|
|
148
148
|
|
|
@@ -10,7 +10,7 @@ Every write is validated. Writes to rows and to the schema are journaled and und
|
|
|
10
10
|
|
|
11
11
|
**List Tables** · read-only
|
|
12
12
|
|
|
13
|
-
List every table: `name`, `catalog`, `schema`, `qualified` (`personal.metrics.mood` — use it wherever a table is expected; `metrics.mood` or `mood` also work when unambiguous), `path` and, when declared, `description`, `aliases` (other words for what it records) and `tags`. Tables are defined in `metastore.yaml` (catalog → schema → table): read it freely, but it is read-only for agents and changes only through the schema operations (create_table, add_column, …). Returns `{tables, views}`: `views` lists the stored views (same fields, no path): select them like tables. `system.information_schema` is read with `select_rows`.
|
|
13
|
+
List every table: `name`, `catalog`, `schema`, `qualified` (`personal.metrics.mood` — use it wherever a table is expected; `metrics.mood` or `mood` also work when unambiguous), `path` and, when declared, `description`, `aliases` (other words for what it records) and `tags`. Tables are defined in `metastore.yaml` (catalog → schema → table): read it freely, but it is read-only for agents and changes only through the schema operations (create_table, add_column, …). Returns `{tables, views}`: `views` lists the stored views (same fields, no path): select them like tables. `system.information_schema` is read with `select_rows`. A token with `read` patterns sees only the tables and views it may read.
|
|
14
14
|
|
|
15
15
|
Call this first to find the table for what the user reports (match their words against name, description and aliases), and before creating tables or columns so you reuse existing ones instead of creating duplicates.
|
|
16
16
|
|
|
@@ -36,7 +36,7 @@ Examples: [Catalogs, schemas and the database](https://datanotes.dev/guides/data
|
|
|
36
36
|
|
|
37
37
|
**Describe Database** · read-only
|
|
38
38
|
|
|
39
|
-
Describe the database: its folder (`root`), `metastore` (`metastore.yaml`: read it freely, but it is read-only for agents and changes only through the schema operations), configuration (default catalog and schema, row partition and file-name formats, archive folder, time zone, history, automatic links), journal folder, `bases` (shared column sets and the tables using them), `catalogs` → `schemas` → `tables` (with `row_count`, `tags`, and `unreadable_count` when some rows’ front matter cannot be read) and `views`; catalogs and schemas show their `comment` (set with `comment_on`). A table or base defined before descriptions were required has `missing_descriptions`: what lacks one, for alter_table / alter_column (define_base for a base) to fill, so every table and column says what it holds.
|
|
39
|
+
Describe the database: its folder (`root`), `metastore` (`metastore.yaml`: read it freely, but it is read-only for agents and changes only through the schema operations), configuration (default catalog and schema, row partition and file-name formats, archive folder, time zone, history, automatic links), journal folder, `bases` (shared column sets and the tables using them), `catalogs` → `schemas` → `tables` (with `row_count`, `tags`, and `unreadable_count` when some rows’ front matter cannot be read) and `views`; catalogs and schemas show their `comment` (set with `comment_on`). A table or base defined before descriptions were required has `missing_descriptions`: what lacks one, for alter_table / alter_column (define_base for a base) to fill, so every table and column says what it holds. A token with `read` patterns sees only the tables, views and bases it may read.
|
|
40
40
|
|
|
41
41
|
Examples: [Catalogs, schemas and the database](https://datanotes.dev/guides/database-objects/catalogs-and-schemas/), [Hand edits](https://datanotes.dev/guides/data-quality/hand-edits/).
|
|
42
42
|
|
|
@@ -68,7 +68,7 @@ Examples: [Catalogs, schemas and the database](https://datanotes.dev/guides/data
|
|
|
68
68
|
|
|
69
69
|
Validate every row against the table (required, types, ranges, enums, links, unknown columns, CHECK constraints, those that follow links included, filtered link targets (ref_where / ref_by), stale generated/identity values, missing inverse links, cycles of hierarchy and acyclic columns, front matter that cannot be read: code `unreadable`, with no column, for a note whose `table` line names the table but whose YAML is broken, which every other read leaves out). A CHECK violation has code `check` and the CHECK’s name as `constraint` (every other code names its `column`), as in the `violations` of list_changes. Use it to find rows edited by hand that break the schema. Without `table`, every table is checked: each row then names its `table`, `tables_checked` counts the tables checked, and a table that cannot be checked is listed in `errors` ({table, error}) while the others are. The rows with violations are paged: `limit` (default 500, at most 5000) from `offset`; `total` counts them all. (Hand edits are also checked as they happen: see `violations` in list_changes.)
|
|
70
70
|
|
|
71
|
-
Examples: [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/), [Hand edits](https://datanotes.dev/guides/data-quality/hand-edits/), [Find rows that break the schema](https://datanotes.dev/guides/data-quality/check-table/).
|
|
71
|
+
Examples: [Tables](https://datanotes.dev/guides/database-objects/tables/), [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/), [Hand edits](https://datanotes.dev/guides/data-quality/hand-edits/), [Find rows that break the schema](https://datanotes.dev/guides/data-quality/check-table/).
|
|
72
72
|
|
|
73
73
|
| Parameter | Type | Required | Default | Description |
|
|
74
74
|
| --------- | --------- | -------- | ------- | -------------------------------------------- |
|
|
@@ -110,6 +110,22 @@ Examples: [Find notes by meaning](https://datanotes.dev/guides/search/semantic/)
|
|
|
110
110
|
| `folder` | string \\ | null | | `null` |
|
|
111
111
|
| `min_score` | number \\ | null | | `null` |
|
|
112
112
|
|
|
113
|
+
## `search`
|
|
114
|
+
|
|
115
|
+
**Search** · read-only
|
|
116
|
+
|
|
117
|
+
Search the notes by their words and, when semantic search is on, by their meaning: `{query, results: [{path, table?, score, heading, snippet, matched_by}], meaning}`, one result per note, best first. It always works: with semantic search off it ranks by words alone (BM25: rare words count more; a word in the note’s name or properties counts more), with it on it fuses both rankings (Reciprocal Rank Fusion), so a note found only by its words or only by its meaning is still there. `matched_by` says which (`words`, `meaning`); `meaning` is `on`, `off` or `failed` (the embedding provider failed: words only, `meaning_error` says why). Query syntax as search_docs: `"a phrase"` and `+word` must appear, `-word` must not (both rankings); words in any language, case and accents ignored, no stemming. Filters: `tables` (qualified or short names), `folder`, `min_score` (the meaning ranking’s cosine floor). `limit`: default 10, max 100. Then read_note / select_rows for the details.
|
|
118
|
+
|
|
119
|
+
Examples: [Search notes by words and meaning](https://datanotes.dev/guides/search/hybrid/).
|
|
120
|
+
|
|
121
|
+
| Parameter | Type | Required | Default | Description |
|
|
122
|
+
| ----------- | ------------ | -------- | ------- | ----------- |
|
|
123
|
+
| `query` | string | yes | | |
|
|
124
|
+
| `limit` | integer | | `10` | |
|
|
125
|
+
| `tables` | string\[] \\ | null | | `null` |
|
|
126
|
+
| `folder` | string \\ | null | | `null` |
|
|
127
|
+
| `min_score` | number \\ | null | | `null` |
|
|
128
|
+
|
|
113
129
|
## `graph_neighbors`
|
|
114
130
|
|
|
115
131
|
**Graph Neighbors** · read-only
|
|
@@ -34,7 +34,7 @@ Update exactly ONE row, addressed by its `id` (the system UUID) or its `path`; r
|
|
|
34
34
|
* `expect`: compare-and-set — `{"status": "open"}` writes only if the row still has those values (a person may have changed it since you read it); otherwise refused, nothing written.
|
|
35
35
|
* `autolink`: the text written links the notes it mentions by name, at their first mention (off with `autolink: false` or the database’s setting). `links_added` lists the links made; `ambiguous_mentions` the mentions left as text with the notes they may name: read those, then link the right one with update_row and `autolink: false`, or leave the text.
|
|
36
36
|
|
|
37
|
-
Examples: [Columns and types](https://datanotes.dev/guides/database-objects/columns/), [Bases](https://datanotes.dev/guides/database-objects/bases/), [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/), [CHECK constraints](https://datanotes.dev/guides/data-quality/check-constraints/), [Hand edits](https://datanotes.dev/guides/data-quality/hand-edits/), [Write and move rows](https://datanotes.dev/guides/read-and-write/write-rows/), [Automatic links](https://datanotes.dev/guides/read-and-write/automatic-links/), [Batch writes](https://datanotes.dev/guides/read-and-write/batch/), [Journal and undo](https://datanotes.dev/guides/history/journal-and-undo/), [Table history and time travel](https://datanotes.dev/guides/history/table-history/), [Change data feed](https://datanotes.dev/guides/history/change-data-feed/), [Restore a table or the database](https://datanotes.dev/guides/history/restore/).
|
|
37
|
+
Examples: [Tables](https://datanotes.dev/guides/database-objects/tables/), [Columns and types](https://datanotes.dev/guides/database-objects/columns/), [Bases](https://datanotes.dev/guides/database-objects/bases/), [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/), [CHECK constraints](https://datanotes.dev/guides/data-quality/check-constraints/), [Hand edits](https://datanotes.dev/guides/data-quality/hand-edits/), [Write and move rows](https://datanotes.dev/guides/read-and-write/write-rows/), [Automatic links](https://datanotes.dev/guides/read-and-write/automatic-links/), [Batch writes](https://datanotes.dev/guides/read-and-write/batch/), [Journal and undo](https://datanotes.dev/guides/history/journal-and-undo/), [Table history and time travel](https://datanotes.dev/guides/history/table-history/), [Change data feed](https://datanotes.dev/guides/history/change-data-feed/), [Restore a table or the database](https://datanotes.dev/guides/history/restore/).
|
|
38
38
|
|
|
39
39
|
| Parameter | Type | Required | Default | Description |
|
|
40
40
|
| --------------- | ------------ | -------- | ------- | ----------- |
|
|
@@ -18,7 +18,7 @@ Create a table: `name`, in `catalog` and `schema` (default: the database’s def
|
|
|
18
18
|
* The table’s options, as alter_table names them: `aliases` (how agents find it, with the description), `partition`, `exclude`, `archive`, `history` (keep its versions; default: the database’s setting), `autolink_target` (true / false: may automatic links link its rows? default: yes, unless its rows are named by a date or a time, as with `time_column`).
|
|
19
19
|
* `row_template`: markdown body of new rows. Shared columns come from bases: `set_table_bases`.
|
|
20
20
|
* `instructions`: how to fill a row, for agents (Markdown); describe_table returns them.
|
|
21
|
-
* `tags`: key/value labels of the table, strings (`{"domain": "health"}`); list_tables and describe_table return them, alter_table changes them. Enum columns: `values` and, for each, its meaning in `value_descriptions` (`{"stated": "the user said it", …}`): describe_table shows them and a refused value lists them. Generated link columns: `generated: "link({occurred_at:YYYY-MM-DD})"` with `ref` = the days table links each row to the row of `ref` named by the template (its folder read from the schema; the target may not exist yet). Filtered link targets (link columns): `ref_where` (a `where` every target must match) and `ref_by` (a column of the row picks the target level: `{"column": "kind", "levels": ["area", "project", "workstream"]}` or `{"column": "kind", "map": {"area": null, "project": ["area"]}}`; null = top level, no link; `target_column` when the target names it differently: an enum target column must have every value the map allows, a string one takes any value). A link to no row of the table, or to the row itself, is refused too. Existing rows that break them refuse the change; `list_link_targets` lists the valid targets of a row.
|
|
21
|
+
* `tags`: key/value labels of the table, strings (`{"domain": "health"}`); list_tables and describe_table return them, alter_table changes them. Enum columns: `values` and, for each, its meaning in `value_descriptions` (`{"stated": "the user said it", …}`): describe_table shows them and a refused value lists them. Generated link columns: `generated: "link({occurred_at:YYYY-MM-DD})"` with `ref` = the days table links each row to the row of `ref` named by the template (its folder read from the schema; the target may not exist yet). Generated aggregates of a json column holding a list of objects (integer or number columns; avg: number): `count(feeds)`, `count(feeds.min)` (the items with a min), `sum(feeds.min)`, `avg(…)`, `min(…)`, `max(…)`, with an optional filter on one field: `count(feeds, side = "left")`, `sum(feeds.min, min > 0)` (=, !=, <, <=, >, >=; “text”, a number, true or false). Recomputed on every write; no list: count and sum 0, avg/min/max empty; a field that is not a number: empty. Filtered link targets (link columns): `ref_where` (a `where` every target must match) and `ref_by` (a column of the row picks the target level: `{"column": "kind", "levels": ["area", "project", "workstream"]}` or `{"column": "kind", "map": {"area": null, "project": ["area"]}}`; null = top level, no link; `target_column` when the target names it differently: an enum target column must have every value the map allows, a string one takes any value). A link to no row of the table, or to the row itself, is refused too. Existing rows that break them refuse the change; `list_link_targets` lists the valid targets of a row.
|
|
22
22
|
|
|
23
23
|
The schema changes only through operations like this one: never edit metastore.yaml by hand (no validation, no undo).
|
|
24
24
|
|
|
@@ -65,7 +65,7 @@ Examples: [Tables](https://datanotes.dev/guides/database-objects/tables/).
|
|
|
65
65
|
|
|
66
66
|
**Alter Table** · destructive write
|
|
67
67
|
|
|
68
|
-
Alter a table: move it (`catalog`, `schema`), rename it (`name`), change `partition` / `filename` patterns, `description` (changes, never goes: “” is refused), `aliases`, `exclude` folders, `row_template` (“” removes it), `instructions` (how to fill a row, for agents; “” removes them), `history` (true | false | “default”), `autolink_target` (true | false | “default”: may automatic links link its rows? default: yes, unless its rows are named by a date or a time) or `tags` (key/value labels merged into the table’s, e.g. `{"domain": "health"}`; a `null` value removes that tag; `clear_tags: true` removes every tag first, so with `tags` the table keeps exactly those), `archive` (folder name inside the table folder) or `fields` (untyped fields, name → default; a `null` value removes one). Rows, archive and transaction log follow; links are updated; renamed rows keep their old name in `aliases`. A change of metadata only (tags, description, aliases, history, autolink_target, row_template, instructions, fields) rewrites the table entry alone: no row is moved or rewritten. Refused when a row cannot fill a pattern. Renaming or moving the table
|
|
68
|
+
Alter a table: move it (`catalog`, `schema`), rename it (`name`), change `partition` / `filename` patterns, `description` (changes, never goes: “” is refused), `aliases`, `exclude` folders, `row_template` (“” removes it), `instructions` (how to fill a row, for agents; “” removes them), `history` (true | false | “default”), `autolink_target` (true | false | “default”: may automatic links link its rows? default: yes, unless its rows are named by a date or a time) or `tags` (key/value labels merged into the table’s, e.g. `{"domain": "health"}`; a `null` value removes that tag; `clear_tags: true` removes every tag first, so with `tags` the table keeps exactly those), `archive` (folder name inside the table folder) or `fields` (untyped fields, name → default; a `null` value removes one). Rows, archive and transaction log follow; links are updated; renamed rows keep their old name in `aliases`. A change of metadata only (tags, description, aliases, history, autolink_target, row_template, instructions, fields) rewrites the table entry alone: no row is moved or rewritten. Refused when a row cannot fill a pattern. Renaming or moving the table rewrites what names it in other definitions: the `ref` and `vocabulary` of the columns of other tables and bases (lists of links included) and the tables of views (`table`, `union[].table`, `join.table`); `schema_references` lists them. CHECKs and `ref_where` / `ref_by` that read it through a link follow the new `ref`. Use `dry_run` to see `planned_moves`; undo with undo_migration.
|
|
69
69
|
|
|
70
70
|
The schema changes only through operations like this one: never edit metastore.yaml by hand (no validation, no undo).
|
|
71
71
|
|
|
@@ -115,11 +115,11 @@ Examples: [Tables](https://datanotes.dev/guides/database-objects/tables/), [Base
|
|
|
115
115
|
|
|
116
116
|
**Add Column** · write
|
|
117
117
|
|
|
118
|
-
Add a typed column. Sparse by default (written only on rows that have a value — use for metrics, e.g. `{"name": "metric_mood", "type": "integer", "min": 1, "max": 5, "label": "Mood", "aliases": ["mood"], "description": "Mood of the day, 1 = bad, 5 = great"}`). `description` is required: what the column holds, for the agents and people who read the schema (the engine writes it for `identity` “uuid” / “created” columns). `dense: true` in the spec adds it to every row with its `default`. Call `describe_table` first: never add a column whose meaning an existing column (name/label/aliases/description) already covers. Column names are permanent identifiers; only `rename_column` changes them. Enum columns: `values` and, for each, its meaning in `value_descriptions` (`{"stated": "the user said it", …}`): describe_table shows them and a refused value lists them. Generated link columns: `generated: "link({occurred_at:YYYY-MM-DD})"` with `ref` = the days table links each row to the row of `ref` named by the template (its folder read from the schema; the target may not exist yet). Filtered link targets (link columns): `ref_where` (a `where` every target must match) and `ref_by` (a column of the row picks the target level: `{"column": "kind", "levels": ["area", "project", "workstream"]}` or `{"column": "kind", "map": {"area": null, "project": ["area"]}}`; null = top level, no link; `target_column` when the target names it differently: an enum target column must have every value the map allows, a string one takes any value). A link to no row of the table, or to the row itself, is refused too. Existing rows that break them refuse the change; `list_link_targets` lists the valid targets of a row. Acyclic links: `acyclic: true` on a link (or list of links) to the table itself refuses every write that closes a cycle, on either side of an `inverse`; refused while existing rows form one (they are listed).
|
|
118
|
+
Add a typed column. Sparse by default (written only on rows that have a value — use for metrics, e.g. `{"name": "metric_mood", "type": "integer", "min": 1, "max": 5, "label": "Mood", "aliases": ["mood"], "description": "Mood of the day, 1 = bad, 5 = great"}`). `description` is required: what the column holds, for the agents and people who read the schema (the engine writes it for `identity` “uuid” / “created” columns). `dense: true` in the spec adds it to every row with its `default`. Call `describe_table` first: never add a column whose meaning an existing column (name/label/aliases/description) already covers. Column names are permanent identifiers; only `rename_column` changes them. Enum columns: `values` and, for each, its meaning in `value_descriptions` (`{"stated": "the user said it", …}`): describe_table shows them and a refused value lists them. Generated link columns: `generated: "link({occurred_at:YYYY-MM-DD})"` with `ref` = the days table links each row to the row of `ref` named by the template (its folder read from the schema; the target may not exist yet). Generated aggregates of a json column holding a list of objects (integer or number columns; avg: number): `count(feeds)`, `count(feeds.min)` (the items with a min), `sum(feeds.min)`, `avg(…)`, `min(…)`, `max(…)`, with an optional filter on one field: `count(feeds, side = "left")`, `sum(feeds.min, min > 0)` (=, !=, <, <=, >, >=; “text”, a number, true or false). Recomputed on every write; no list: count and sum 0, avg/min/max empty; a field that is not a number: empty. Filtered link targets (link columns): `ref_where` (a `where` every target must match) and `ref_by` (a column of the row picks the target level: `{"column": "kind", "levels": ["area", "project", "workstream"]}` or `{"column": "kind", "map": {"area": null, "project": ["area"]}}`; null = top level, no link; `target_column` when the target names it differently: an enum target column must have every value the map allows, a string one takes any value). A link to no row of the table, or to the row itself, is refused too. Existing rows that break them refuse the change; `list_link_targets` lists the valid targets of a row. Acyclic links: `acyclic: true` on a link (or list of links) to the table itself refuses every write that closes a cycle, on either side of an `inverse`; refused while existing rows form one (they are listed).
|
|
119
119
|
|
|
120
120
|
The schema changes only through operations like this one: never edit metastore.yaml by hand (no validation, no undo).
|
|
121
121
|
|
|
122
|
-
Examples: [Columns and types](https://datanotes.dev/guides/database-objects/columns/), [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/), [Import existing notes](https://datanotes.dev/guides/read-and-write/import-notes/).
|
|
122
|
+
Examples: [Tables](https://datanotes.dev/guides/database-objects/tables/), [Columns and types](https://datanotes.dev/guides/database-objects/columns/), [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/), [Import existing notes](https://datanotes.dev/guides/read-and-write/import-notes/).
|
|
123
123
|
|
|
124
124
|
| Parameter | Type | Required | Default | Description |
|
|
125
125
|
| --------- | ------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------- |
|
|
@@ -290,7 +290,7 @@ Create (or `replace`) a view: a named select stored in metastore.yaml. Name it w
|
|
|
290
290
|
|
|
291
291
|
The schema changes only through operations like this one: never edit metastore.yaml by hand (no validation, no undo).
|
|
292
292
|
|
|
293
|
-
Examples: [Views](https://datanotes.dev/guides/database-objects/views/).
|
|
293
|
+
Examples: [Tables](https://datanotes.dev/guides/database-objects/tables/), [Views](https://datanotes.dev/guides/database-objects/views/).
|
|
294
294
|
|
|
295
295
|
| Parameter | Type | Required | Default | Description |
|
|
296
296
|
| ------------- | ------------ | -------- | ------- | ----------- |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Every operation of the HTTP API, offered to agents as an MCP tool of the same name, grouped by kind.
|
|
4
4
|
|
|
5
|
-
The engine has
|
|
5
|
+
The engine has 55 operations of five kinds, like the statements of a SQL reference. The MCP server, `datanotes mcp`, offers each operation as an MCP tool of the same name, and `engineTools()` of `datanotes/mcp` gives the same definitions to [your own MCP server](https://datanotes.dev/develop/build-on-datanotes/#build-your-own-mcp-server). `DatanotesClient.call(name, args)` runs one from code. Over HTTP each one is `POST /datanotes/operations/<name>/`, with its arguments as the JSON body: the [HTTP API reference](https://datanotes.dev/api/) lists them.
|
|
6
6
|
|
|
7
7
|
Next to them, `datanotes mcp` offers three tools that read these docs offline, `list_docs`, `read_docs` and `search_docs`; they aren’t operations of the engine ([Read the docs offline](https://datanotes.dev/guides/agents/docs-offline/)). `search_docs` takes required and excluded words, and becomes a hybrid search when the engine has semantic search on ([Search by words and by meaning](https://datanotes.dev/guides/agents/docs-offline/#search-by-words-and-by-meaning)).
|
|
8
8
|
|
|
@@ -16,7 +16,7 @@ Operations that define the database: tables, columns, constraints, bases and vie
|
|
|
16
16
|
| [`drop_table`](https://datanotes.dev/reference/operations/schema/#drop_table) | Drop a table. | [Tables](https://datanotes.dev/guides/database-objects/tables/) |
|
|
17
17
|
| [`alter_table`](https://datanotes.dev/reference/operations/schema/#alter_table) | Alter a table: move it (`catalog`, `schema`), rename it (`name`), change `partition` / `filename` patterns, `description` (changes, never goes: “” is refused), `aliases`, `exclude` folders, `row_template` (“” removes it), `instructions` (how to fill a row, for agents; “” removes them), `history` (true \| false \| “default”), `autolink_target` (true \| false \| “default”: may automatic links link its rows? default: yes, unless its rows are named by a date or a time) or `tags` (key/value labels merged into the table’s, e.g. | [Catalogs, schemas and the database](https://datanotes.dev/guides/database-objects/catalogs-and-schemas/), [Tables](https://datanotes.dev/guides/database-objects/tables/), [Automatic links](https://datanotes.dev/guides/read-and-write/automatic-links/), [Import existing notes](https://datanotes.dev/guides/read-and-write/import-notes/), [Table history and time travel](https://datanotes.dev/guides/history/table-history/) |
|
|
18
18
|
| [`clone_table`](https://datanotes.dev/reference/operations/schema/#clone_table) | CREATE TABLE `target` CLONE `source`: same definition plus independent copies of its rows (deep clone), optionally as of a `version` / `timestamp`. | [Tables](https://datanotes.dev/guides/database-objects/tables/), [Bases](https://datanotes.dev/guides/database-objects/bases/), [Table history and time travel](https://datanotes.dev/guides/history/table-history/) |
|
|
19
|
-
| [`add_column`](https://datanotes.dev/reference/operations/schema/#add_column) | Add a typed column. | [Columns and types](https://datanotes.dev/guides/database-objects/columns/), [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/), [Import existing notes](https://datanotes.dev/guides/read-and-write/import-notes/)
|
|
19
|
+
| [`add_column`](https://datanotes.dev/reference/operations/schema/#add_column) | Add a typed column. | [Tables](https://datanotes.dev/guides/database-objects/tables/), [Columns and types](https://datanotes.dev/guides/database-objects/columns/), [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/), [Import existing notes](https://datanotes.dev/guides/read-and-write/import-notes/) |
|
|
20
20
|
| [`rename_column`](https://datanotes.dev/reference/operations/schema/#rename_column) | Rename a column in metastore.yaml and on every row. | [Columns and types](https://datanotes.dev/guides/database-objects/columns/) |
|
|
21
21
|
| [`drop_column`](https://datanotes.dev/reference/operations/schema/#drop_column) | Remove a column from the schema and every row. | [Columns and types](https://datanotes.dev/guides/database-objects/columns/) |
|
|
22
22
|
| [`alter_column`](https://datanotes.dev/reference/operations/schema/#alter_column) | Change a column’s attributes (type, min/max/step, values, required, default, label, aliases, description, note, `ref_where`, `ref_by` (see list_link_targets), `acyclic` (refused while the rows already form a cycle: they are listed); `null` removes an attribute, except `description`, which changes but never goes (null or “” is refused); `dense` true/false). | [Tables](https://datanotes.dev/guides/database-objects/tables/), [Columns and types](https://datanotes.dev/guides/database-objects/columns/), [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/) |
|
|
@@ -26,7 +26,7 @@ Operations that define the database: tables, columns, constraints, bases and vie
|
|
|
26
26
|
| [`set_table_bases`](https://datanotes.dev/reference/operations/schema/#set_table_bases) | Set the bases a table inherits from (`bases`, in order; `[]` removes them). | [Bases](https://datanotes.dev/guides/database-objects/bases/) |
|
|
27
27
|
| [`add_constraint`](https://datanotes.dev/reference/operations/schema/#add_constraint) | ALTER TABLE ADD CONSTRAINT `name` CHECK: `check` is a where filter every row must match, e.g. | [CHECK constraints](https://datanotes.dev/guides/data-quality/check-constraints/) |
|
|
28
28
|
| [`drop_constraint`](https://datanotes.dev/reference/operations/schema/#drop_constraint) | Remove the CHECK constraint `name` from `table`. | [CHECK constraints](https://datanotes.dev/guides/data-quality/check-constraints/) |
|
|
29
|
-
| [`create_view`](https://datanotes.dev/reference/operations/schema/#create_view) | Create (or `replace`) a view: a named select stored in metastore.yaml. | [Views](https://datanotes.dev/guides/database-objects/views/)
|
|
29
|
+
| [`create_view`](https://datanotes.dev/reference/operations/schema/#create_view) | Create (or `replace`) a view: a named select stored in metastore.yaml. | [Tables](https://datanotes.dev/guides/database-objects/tables/), [Views](https://datanotes.dev/guides/database-objects/views/) |
|
|
30
30
|
| [`drop_view`](https://datanotes.dev/reference/operations/schema/#drop_view) | Remove a view from `metastore.yaml` (undo with `undo_migration`). | [Views](https://datanotes.dev/guides/database-objects/views/) |
|
|
31
31
|
|
|
32
32
|
## Rows (DML)
|
|
@@ -36,7 +36,7 @@ Operations that write rows: insert, update, merge, delete, move, adopt and batch
|
|
|
36
36
|
| Operation | What it does | Examples |
|
|
37
37
|
| -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
38
38
|
| [`insert_row`](https://datanotes.dev/reference/operations/rows/#insert_row) | Insert a row (new note). | [Catalogs, schemas and the database](https://datanotes.dev/guides/database-objects/catalogs-and-schemas/), [Tables](https://datanotes.dev/guides/database-objects/tables/), [Columns and types](https://datanotes.dev/guides/database-objects/columns/), [Bases](https://datanotes.dev/guides/database-objects/bases/), [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/), [Validation](https://datanotes.dev/guides/data-quality/validation/), [CHECK constraints](https://datanotes.dev/guides/data-quality/check-constraints/), [Write and move rows](https://datanotes.dev/guides/read-and-write/write-rows/), [Automatic links](https://datanotes.dev/guides/read-and-write/automatic-links/), [Batch writes](https://datanotes.dev/guides/read-and-write/batch/), [Import existing notes](https://datanotes.dev/guides/read-and-write/import-notes/), [Journal and undo](https://datanotes.dev/guides/history/journal-and-undo/), [Table history and time travel](https://datanotes.dev/guides/history/table-history/), [Change data feed](https://datanotes.dev/guides/history/change-data-feed/), [Restore a table or the database](https://datanotes.dev/guides/history/restore/) |
|
|
39
|
-
| [`update_row`](https://datanotes.dev/reference/operations/rows/#update_row) | Update exactly ONE row, addressed by its `id` (the system UUID) or its `path`; refused when the row does not exist (or id and path disagree). | [Columns and types](https://datanotes.dev/guides/database-objects/columns/), [Bases](https://datanotes.dev/guides/database-objects/bases/), [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/), [CHECK constraints](https://datanotes.dev/guides/data-quality/check-constraints/), [Hand edits](https://datanotes.dev/guides/data-quality/hand-edits/), [Write and move rows](https://datanotes.dev/guides/read-and-write/write-rows/), [Automatic links](https://datanotes.dev/guides/read-and-write/automatic-links/), [Batch writes](https://datanotes.dev/guides/read-and-write/batch/), [Journal and undo](https://datanotes.dev/guides/history/journal-and-undo/), [Table history and time travel](https://datanotes.dev/guides/history/table-history/), [Change data feed](https://datanotes.dev/guides/history/change-data-feed/), [Restore a table or the database](https://datanotes.dev/guides/history/restore/)
|
|
39
|
+
| [`update_row`](https://datanotes.dev/reference/operations/rows/#update_row) | Update exactly ONE row, addressed by its `id` (the system UUID) or its `path`; refused when the row does not exist (or id and path disagree). | [Tables](https://datanotes.dev/guides/database-objects/tables/), [Columns and types](https://datanotes.dev/guides/database-objects/columns/), [Bases](https://datanotes.dev/guides/database-objects/bases/), [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/), [CHECK constraints](https://datanotes.dev/guides/data-quality/check-constraints/), [Hand edits](https://datanotes.dev/guides/data-quality/hand-edits/), [Write and move rows](https://datanotes.dev/guides/read-and-write/write-rows/), [Automatic links](https://datanotes.dev/guides/read-and-write/automatic-links/), [Batch writes](https://datanotes.dev/guides/read-and-write/batch/), [Journal and undo](https://datanotes.dev/guides/history/journal-and-undo/), [Table history and time travel](https://datanotes.dev/guides/history/table-history/), [Change data feed](https://datanotes.dev/guides/history/change-data-feed/), [Restore a table or the database](https://datanotes.dev/guides/history/restore/) |
|
|
40
40
|
| [`update_rows`](https://datanotes.dev/reference/operations/rows/#update_rows) | Update every row matching `where` (required: `{}` is every row). | [Write and move rows](https://datanotes.dev/guides/read-and-write/write-rows/) |
|
|
41
41
|
| [`merge_rows`](https://datanotes.dev/reference/operations/rows/#merge_rows) | MERGE (upsert) `rows` into `table` by the key columns `on` (e.g. | [Write and move rows](https://datanotes.dev/guides/read-and-write/write-rows/) |
|
|
42
42
|
| [`delete_rows`](https://datanotes.dev/reference/operations/rows/#delete_rows) | Delete rows matching `where`. | [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/), [Write and move rows](https://datanotes.dev/guides/read-and-write/write-rows/), [Table history and time travel](https://datanotes.dev/guides/history/table-history/) |
|
|
@@ -58,6 +58,7 @@ Operations that read rows and notes: select_rows, link targets, text and semanti
|
|
|
58
58
|
| [`list_folder`](https://datanotes.dev/reference/operations/queries/#list_folder) | The direct children of a vault folder: `{folder, folders, files}`, each a vault path, sorted; dot-files and dot-folders are left out. | [Catalogs, schemas and the database](https://datanotes.dev/guides/database-objects/catalogs-and-schemas/), [Tables](https://datanotes.dev/guides/database-objects/tables/), [Columns and types](https://datanotes.dev/guides/database-objects/columns/), [Write and move rows](https://datanotes.dev/guides/read-and-write/write-rows/), [Read and search notes](https://datanotes.dev/guides/search/text/) |
|
|
59
59
|
| [`search_notes`](https://datanotes.dev/reference/operations/queries/#search_notes) | Plain-text search: every word of `query` must occur, case- and accent-insensitive, in the note’s path or text; there are no operators. | [Read and search notes](https://datanotes.dev/guides/search/text/) |
|
|
60
60
|
| [`semantic_search`](https://datanotes.dev/reference/operations/queries/#semantic_search) | Find notes by meaning, not exact words: `{query, results: [{path, score, heading, snippet, table?}]}`, one result per note (its best passage), best first. | [Find notes by meaning](https://datanotes.dev/guides/search/semantic/) |
|
|
61
|
+
| [`search`](https://datanotes.dev/reference/operations/queries/#search) | Search the notes by their words and, when semantic search is on, by their meaning: `{query, results: [{path, table?, score, heading, snippet, matched_by}], meaning}`, one result per note, best first. | [Search notes by words and meaning](https://datanotes.dev/guides/search/hybrid/) |
|
|
61
62
|
| [`graph_neighbors`](https://datanotes.dev/reference/operations/queries/#graph_neighbors) | Direct neighbours of notes: what they link to (`out`) and what links to them (`in`, backlinks), with counts per edge label. | [Walk the link graph](https://datanotes.dev/guides/search/link-graph/) |
|
|
62
63
|
| [`graph_traverse`](https://datanotes.dev/reference/operations/queries/#graph_traverse) | Breadth-first walk over links from `from_nodes` (`{"paths": [...]}`, `{"table", "where"}` and/or `{"tables": [...]}`). | [Walk the link graph](https://datanotes.dev/guides/search/link-graph/) |
|
|
63
64
|
| [`graph_path`](https://datanotes.dev/reference/operations/queries/#graph_path) | Shortest chain of links between two sets of notes (each `{"paths": [...]}`, `{"table", "where"}` and/or `{"tables": [...]}`): `found`, `length` and `path`: the steps (`from`, `label`, `direction`, `to`). | [Walk the link graph](https://datanotes.dev/guides/search/link-graph/) |
|
|
@@ -90,7 +91,7 @@ Operations that describe and check the database, configure it and maintain its i
|
|
|
90
91
|
| [`describe_table`](https://datanotes.dev/reference/operations/auxiliary/#describe_table) | Describe a table (`catalog.schema.table` or a shorter unambiguous name): `qualified`, `name`, `catalog`, `schema`, `description`, `aliases`, `layout` (`folder`, `row_folder`: the folder pattern of new rows, `archive`), `options` (`partition`, `filename`, `archive`, `exclude`), typed `columns` with label/aliases/description and the dense flag, `untyped_fields`, `row_count`, and when set: `tags`, `bases` (the bases it inherits from, in order), `constraints` (its CHECKs, name → where), `constraint_hints` (their own hints, name → hint), `instructions` (how to fill a row: follow them before you write one), `row_template`, `history`, `autolink_target`, `fields` (their values). | [Catalogs, schemas and the database](https://datanotes.dev/guides/database-objects/catalogs-and-schemas/), [Tables](https://datanotes.dev/guides/database-objects/tables/), [Bases](https://datanotes.dev/guides/database-objects/bases/), [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/) |
|
|
91
92
|
| [`describe_database`](https://datanotes.dev/reference/operations/auxiliary/#describe_database) | Describe the database: its folder (`root`), `metastore` (`metastore.yaml`: read it freely, but it is read-only for agents and changes only through the schema operations), configuration (default catalog and schema, row partition and file-name formats, archive folder, time zone, history, automatic links), journal folder, `bases` (shared column sets and the tables using them), `catalogs` → `schemas` → `tables` (with `row_count`, `tags`, and `unreadable_count` when some rows’ front matter cannot be read) and `views`; catalogs and schemas show their `comment` (set with `comment_on`). | [Catalogs, schemas and the database](https://datanotes.dev/guides/database-objects/catalogs-and-schemas/), [Hand edits](https://datanotes.dev/guides/data-quality/hand-edits/) |
|
|
92
93
|
| [`configure_database`](https://datanotes.dev/reference/operations/auxiliary/#configure_database) | Configure the database (persisted in metastore.yaml `config:`): `root` (rename the database folder; links follow; revert by renaming back), `default_catalog`, `default_schema`, `default_partition` (“” = flat) and `default_filename` (date formats for new time-based tables), `archive_folder`, `timezone` (IANA; completes date-times written without offset), `history` (transaction log for every table: true in a database the engine creates, false when `config` lacks it, as in a metastore from before 3.0.0; change it only on the user’s request), `autolink` (the text a write puts in a body links the notes it mentions; on by default). | [Catalogs, schemas and the database](https://datanotes.dev/guides/database-objects/catalogs-and-schemas/), [Tables](https://datanotes.dev/guides/database-objects/tables/), [Automatic links](https://datanotes.dev/guides/read-and-write/automatic-links/), [Table history and time travel](https://datanotes.dev/guides/history/table-history/) |
|
|
93
|
-
| [`check_table`](https://datanotes.dev/reference/operations/auxiliary/#check_table) | Validate every row against the table (required, types, ranges, enums, links, unknown columns, CHECK constraints, those that follow links included, filtered link targets (ref_where / ref_by), stale generated/identity values, missing inverse links, cycles of hierarchy and acyclic columns, front matter that cannot be read: code `unreadable`, with no column, for a note whose `table` line names the table but whose YAML is broken, which every other read leaves out). | [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/), [Hand edits](https://datanotes.dev/guides/data-quality/hand-edits/), [Find rows that break the schema](https://datanotes.dev/guides/data-quality/check-table/)
|
|
94
|
+
| [`check_table`](https://datanotes.dev/reference/operations/auxiliary/#check_table) | Validate every row against the table (required, types, ranges, enums, links, unknown columns, CHECK constraints, those that follow links included, filtered link targets (ref_where / ref_by), stale generated/identity values, missing inverse links, cycles of hierarchy and acyclic columns, front matter that cannot be read: code `unreadable`, with no column, for a note whose `table` line names the table but whose YAML is broken, which every other read leaves out). | [Tables](https://datanotes.dev/guides/database-objects/tables/), [Relations and hierarchies](https://datanotes.dev/guides/database-objects/relations/), [Hand edits](https://datanotes.dev/guides/data-quality/hand-edits/), [Find rows that break the schema](https://datanotes.dev/guides/data-quality/check-table/) |
|
|
94
95
|
| [`infer_table`](https://datanotes.dev/reference/operations/auxiliary/#infer_table) | Read-only schema inference from notes’ front matter. | [Hand edits](https://datanotes.dev/guides/data-quality/hand-edits/), [Import existing notes](https://datanotes.dev/guides/read-and-write/import-notes/) |
|
|
95
96
|
| [`describe_semantic_index`](https://datanotes.dev/reference/operations/auxiliary/#describe_semantic_index) | The semantic search index: `enabled`, `provider`, `model`, `files`, `passages`, `pending`, `running`, `last_sync`, `last_error`. | [Find notes by meaning](https://datanotes.dev/guides/search/semantic/) |
|
|
96
97
|
| [`sync_semantic_index`](https://datanotes.dev/reference/operations/auxiliary/#sync_semantic_index) | Bring the semantic search index up to date: embed the notes changed since the last run (the engine also does it on its own about ten seconds after edits). | [Find notes by meaning](https://datanotes.dev/guides/search/semantic/) |
|
|
@@ -4,6 +4,65 @@
|
|
|
4
4
|
|
|
5
5
|
This page lists each version of Datanotes, newest first. Datanotes follows [semantic versioning](https://semver.org): a major version can break what you rely on, and its notes list those changes first. The package is on [npm](https://www.npmjs.com/package/datanotes), and each version has a [GitHub release](https://github.com/davidefornelli/datanotes/releases).
|
|
6
6
|
|
|
7
|
+
## 3.4.0
|
|
8
|
+
|
|
9
|
+
Datanotes 3.4.0 brings three additions:
|
|
10
|
+
|
|
11
|
+
* search the notes by words and meaning in one call
|
|
12
|
+
* limit what a named token reads
|
|
13
|
+
* count and total the items of a `json` list
|
|
14
|
+
|
|
15
|
+
It only adds to 3.3: no operation, argument or setting goes away, and a token without `read` reads every table as before.
|
|
16
|
+
|
|
17
|
+
### Search by words and meaning
|
|
18
|
+
|
|
19
|
+
[Search notes by words and meaning](https://datanotes.dev/guides/search/hybrid/) explains how it ranks.
|
|
20
|
+
|
|
21
|
+
* **`search`**: a new operation. It ranks the notes by their words, with BM25. With semantic search on, it fuses that ranking with the meaning, with Reciprocal Rank Fusion.
|
|
22
|
+
* **Always on**: with semantic search off, or when the embedding provider fails, it ranks by words alone. `meaning` says which.
|
|
23
|
+
* **`matched_by`**: each hit says whether its words, its meaning or both found it.
|
|
24
|
+
* **Query syntax**: `"a phrase"`, `+word` and `-word`, as in `search_docs`. Filters: `tables`, `folder` and `min_score`.
|
|
25
|
+
* **Any language**: case and accents are ignored, and words aren’t stemmed.
|
|
26
|
+
* **Read permissions**: a token searches only the notes it may read.
|
|
27
|
+
|
|
28
|
+
### Named tokens: `read` patterns
|
|
29
|
+
|
|
30
|
+
[Limit what a token can read](https://datanotes.dev/guides/security/tokens/#limit-what-a-token-can-read) explains the rules.
|
|
31
|
+
|
|
32
|
+
* **`read`**: a new field of `tokens/create/` and `tokens/update/`. It takes table patterns, like `write`: `catalog.schema.table`, `catalog.schema.*`, `catalog.*` or `*`. `tokens/update/` takes `null` to read every table again.
|
|
33
|
+
* **Refused reads**: a query, a view or a note that names a table outside `read` answers 403. The message names the table, with a hint and a docs link.
|
|
34
|
+
* **Filtered sets**: listings, bases, search, the graph, history and the change feed leave out the tables the token may not read.
|
|
35
|
+
* **Free notes**: a token with a limited `read` never reads the notes outside tables.
|
|
36
|
+
* **Write implies read**: every `write` pattern must fall inside `read`. A token with `ddl` or `maintain` keeps reading every table.
|
|
37
|
+
* **Answers of writes**: a write still checks every table. Its answer leaves out the rows the token may not read.
|
|
38
|
+
* **Where it shows**: `tokens/`, `tokens/me/` and `datanotes status` list `read`. A token without it lists `["*"]`.
|
|
39
|
+
|
|
40
|
+
### Count and total the items of a json list
|
|
41
|
+
|
|
42
|
+
A `generated` column can now aggregate a `json` column that holds a list of objects. For example, a day keeps its feedings in `feeds`. The engine then keeps `feeds_count` and `left_min` up to date. [Count and total the items of a json list](https://datanotes.dev/guides/database-objects/columns/#count-and-total-the-items-of-a-json-list) shows it.
|
|
43
|
+
|
|
44
|
+
* **Five functions**: `count(feeds)`, `count(feeds.min)` for the items with a value, `sum(feeds.min)`, `avg(…)`, `min(…)` and `max(…)`.
|
|
45
|
+
* **A filter**: `count(feeds, side = "left")` keeps the items whose field matches. It compares one field with text, a number, `true` or `false`, using `=`, `!=`, `<`, `<=`, `>` or `>=`.
|
|
46
|
+
* **On every write**: the engine recomputes the value when it writes the row. After a hand edit, `check_table` reports the stale value and `recompute_columns` fixes it.
|
|
47
|
+
* **Checked when you define it**: the engine refuses an aggregate of a column that isn’t `json`, or a column type that doesn’t fit the result. Each refusal has its own hint and docs.
|
|
48
|
+
* **No wrong numbers**: with no items, `count` and `sum` are 0, and `avg`, `min` and `max` are empty. A field that isn’t a number leaves the value empty.
|
|
49
|
+
|
|
50
|
+
## 3.3.1
|
|
51
|
+
|
|
52
|
+
Datanotes 3.3.1 fixes `alter_table` when it renames or moves a table. It only adds to 3.3: no operation, argument or setting goes away.
|
|
53
|
+
|
|
54
|
+
### Fixes
|
|
55
|
+
|
|
56
|
+
* **References follow a moved table**: `alter_table` with `name`, `catalog` or `schema` rewrites every name of the table in the other definitions. Before, the links of other tables kept the old name, and `check_table` then reported a `ref` violation on every row. The engine now rewrites:
|
|
57
|
+
|
|
58
|
+
* the `ref` of link columns and lists of links, in tables and in bases
|
|
59
|
+
* the `vocabulary` of string columns
|
|
60
|
+
* the tables a view reads, in `table`, `union` and `join`
|
|
61
|
+
|
|
62
|
+
* **`schema_references`**: a new field of the answer lists each name rewritten, also on a dry run. `undo_migration` puts the old names back. [Rename or move a table](https://datanotes.dev/guides/database-objects/tables/#rename-or-move-a-table) shows it.
|
|
63
|
+
|
|
64
|
+
* **CHECKs and link filters**: a CHECK that follows links, or a `ref_where` / `ref_by`, that reads the table through a link no longer refuses the move. These rules name columns, not tables, so they follow the rewritten `ref`. `drop_table` still refuses while they read the table.
|
|
65
|
+
|
|
7
66
|
## 3.3.0
|
|
8
67
|
|
|
9
68
|
Datanotes 3.3.0 installs extensions: tables, views, bases and MCP tools from a git repository, pinned to a version. It only adds to 3.2: no operation, argument or setting goes away.
|
|
@@ -156,7 +156,14 @@ The engine still checks the token on every call. A tool that writes also needs a
|
|
|
156
156
|
|
|
157
157
|
## Give each agent its own token
|
|
158
158
|
|
|
159
|
-
A named token limits what an agent can do, and the journal records its name. It
|
|
159
|
+
A named token limits what an agent can do, and the journal records its name. It has these limits:
|
|
160
|
+
|
|
161
|
+
* **Reads**: only the tables you allow in `read`; without `read`, every table
|
|
162
|
+
* **Writes**: only the tables you allow in `write`
|
|
163
|
+
* **Schema changes**: only with `ddl`
|
|
164
|
+
* **Undo**: only with `maintain`
|
|
165
|
+
|
|
166
|
+
Create one with the owner’s token. This one reads and writes every table, changes the schema and may undo:
|
|
160
167
|
|
|
161
168
|
```bash
|
|
162
169
|
curl -s http://127.0.0.1:27150/datanotes/tokens/create/ \
|
|
@@ -165,7 +172,7 @@ curl -s http://127.0.0.1:27150/datanotes/tokens/create/ \
|
|
|
165
172
|
-d '{"name": "claude", "write": ["*"], "ddl": true, "maintain": true}'
|
|
166
173
|
```
|
|
167
174
|
|
|
168
|
-
The answer contains the token, and shows it only once. [Tokens](https://datanotes.dev/guides/security/tokens/) describes every permission.
|
|
175
|
+
The answer contains the token, and shows it only once. [Tokens](https://datanotes.dev/guides/security/tokens/) describes every permission. To keep an agent to some tables, give it `read` patterns, as [Limit what a token can read](https://datanotes.dev/guides/security/tokens/#limit-what-a-token-can-read) shows.
|
|
169
176
|
|
|
170
177
|
## Agents and prompt injection
|
|
171
178
|
|
|
@@ -173,7 +180,7 @@ An agent reads text it didn’t write: your notes, web pages, emails and the ans
|
|
|
173
180
|
|
|
174
181
|
* **Offer only the tools it needs.** Use `--profile read` for an agent that reads, and `--tools` for a narrow one.
|
|
175
182
|
* **Keep the webhook tools for yourself.** They are off by default. Add `--owner-tools` only for an agent you ask to create a webhook.
|
|
176
|
-
* **Give it a named token.** A [named token](#give-each-agent-its-own-token) writes only the tables you allow
|
|
183
|
+
* **Give it a named token.** A [named token](#give-each-agent-its-own-token) reads and writes only the tables you allow. It never creates webhooks.
|
|
177
184
|
* **Confirm what leaves the database.** Ask your MCP client to confirm tool calls that write or send data.
|
|
178
185
|
* **Check what it changed.** [See which agent made a change](#see-which-agent-made-a-change), and undo it with `undo_migration`.
|
|
179
186
|
|
package/package.json
CHANGED