Skip to main content

Databases

Databases are Notion-style tables on SQLite, stored locally in your channel. Each is a typed table you can view, relate, roll up, and even have an agent fill in.

Typed fields​

A database is a set of rows over typed fields. The supported field types are:

TypeHolds
textfree text
numbernumeric value
datea date (stored as a ms timestamp)
selectone choice from a fixed set of choices
relationa link to a single row in another database
multi_relationlinks to many rows in another database
rollupan aggregate computed over a relation's targets
formulaa computed value
aia value produced by an agent (cached per row)
pythona value computed by Python
toola value produced by a capability/tool
buttonan action trigger

Computed fields (rollup, formula, ai, python, tool, button) are derived, not stored input. They're computed at read time and can't be written directly — a write to one is rejected with an explanatory error rather than silently ignored.

Views​

A database isn't tied to one layout. The same rows can be shown through several views, each surfacing a different axis of the data. The bundled view plugins:

  • kanban — columns grouped by a select/status field
  • smart_calendar — rows placed on a time axis (events, with repeat / reminders / triggers)
  • smart_chart — a number plotted over time
  • notebook_graph — the relations graph (also a 3D «Связи» mode)

You embed a view in a note with a view fence, or run it standalone in the plugins dock with its own source picker (see the table guide).

Relations and rollups​

Connect databases with relation / multi_relation fields instead of copying data.

  • Two-way sync. Linking row A to row B keeps both sides consistent — if the target database has a relation field pointing back, it's updated in the same operation. Link/unlink are idempotent.
  • Backlinks are free. For every relation elsewhere that points at this database, a virtual backlink field appears automatically: it carries the count and the resolved reverse links. Nothing is stored — the forward relation stays the single source of truth.
  • Rollups computed server-side. A rollup aggregates over a relation's targets (SQL aggregates; multi-relation via json_each). Views never re-derive it; they receive it already computed.

When a row changes, dependent databases (those that depend on it via a relation target or a rollup source) are notified through a bounded dependency fan-out — so an automation watching one database fires when an upstream row changes its inputs, without anyone listening to the whole workspace.

The ai field​

An ai field's value is produced by an agent and cached per row. Instead of filling a cell by hand, you let the agent compute it from the rest of the row (for example, a one-line summary, a classification, or an extracted field). It behaves like any other computed field — read-only from the grid's point of view, recomputed when its inputs change — but its value comes from the LLM.

How writes propagate​

Every write — whether from the grid, a view plugin, or a script — goes through one contract and emits a store.changed event on the WebSocket bus. So a single edit re-renders the grid, recomputes formulas, runs any on_change handlers, and refreshes every other view of that data, all live. (A write that doesn't emit is considered a bug.)

See also​